IT++ Logo

pulse_shape.h

Go to the documentation of this file.
00001 
00030 #ifndef PULSE_SHAPE_H
00031 #define PULSE_SHAPE_H
00032 
00033 #include <itpp/base/vec.h>
00034 #include <itpp/base/matfunc.h>
00035 #include <itpp/base/math/trig_hyp.h>
00036 #include <itpp/signal/filter.h>
00037 #include <itpp/signal/resampling.h>
00038 
00039 
00040 namespace itpp
00041 {
00042 
00073 template<class T1, class T2, class T3>
00074 class Pulse_Shape
00075 {
00076 public:
00078   Pulse_Shape();
00080   Pulse_Shape(const Vec<T2> &impulse_response, int upsampling_factor);
00082   virtual ~Pulse_Shape() {}
00090   void set_pulse_shape(const Vec<T2> &impulse_response, int upsampling_factor);
00092   Vec<T2> get_pulse_shape(void) const;
00094   int get_upsampling_factor() const;
00096   int get_pulse_length() const;
00098   int get_filter_length() const;
00099 
00101   void shape_symbols(const Vec<T1> &input, Vec<T3> &output);
00103   Vec<T3> shape_symbols(const Vec<T1> &input);
00104 
00106   void shape_samples(const Vec<T1> &input, Vec<T3> &output);
00108   Vec<T3> shape_samples(const Vec<T1> &input);
00109 
00111   void clear(void);
00112 
00113 protected:
00115   Vec<T2> impulse_response;
00117   MA_Filter<T1, T2, T3> shaping_filter;
00119   int pulse_length;
00121   int upsampling_factor;
00123   bool setup_done;
00124 };
00125 
00162 template<class T1>
00163 class Raised_Cosine : public Pulse_Shape<T1, double, T1>
00164 {
00165 public:
00167   Raised_Cosine() {}
00169   Raised_Cosine(double roll_off, int filter_length = 6, int upsampling_factor = 8);
00171   virtual ~Raised_Cosine() {}
00173   void set_pulse_shape(double roll_off_factor, int filter_length = 6, int upsampling_factor = 8);
00175   double get_roll_off(void) const;
00176 
00177 protected:
00179   double roll_off_factor;
00180 };
00181 
00226 template<class T1>
00227 class Root_Raised_Cosine : public Pulse_Shape<T1, double, T1>
00228 {
00229 public:
00231   Root_Raised_Cosine() {}
00233   Root_Raised_Cosine(double roll_off_factor, int filter_length = 6, int upsampling_factor = 8);
00235   virtual ~Root_Raised_Cosine() {}
00237   void set_pulse_shape(double roll_off_factor, int filter_length = 6, int upsampling_factor = 8);
00239   double get_roll_off(void) const;
00240 
00241 protected:
00243   double roll_off_factor;
00244 };
00245 
00246 //-------------------------------------------------------------------------
00247 // Implementation of templated code starts here
00248 //-------------------------------------------------------------------------
00249 
00250 //---------------------------- Pulse_Shape --------------------------------
00251 
00252 template<class T1, class T2, class T3>
00253 Pulse_Shape<T1, T2, T3>::Pulse_Shape()
00254 {
00255   setup_done = false;
00256   pulse_length = 0;
00257   upsampling_factor = 0;
00258 }
00259 
00260 
00261 template<class T1, class T2, class T3>
00262 Pulse_Shape<T1, T2, T3>::Pulse_Shape(const Vec<T2> &impulse_response, int upsampling_factor)
00263 {
00264   set_pulse_shape(impulse_response, upsampling_factor);
00265 }
00266 
00267 template<class T1, class T2, class T3>
00268 void Pulse_Shape<T1, T2, T3>::set_pulse_shape(const Vec<T2> &impulse_response_in, int upsampling_factor_in)
00269 {
00270   it_error_if(impulse_response_in.size() == 0, "Pulse_Shape: impulse response is zero length");
00271   it_error_if(upsampling_factor_in < 1, "Pulse_Shape: incorrect upsampling factor");
00272 
00273   pulse_length = (impulse_response_in.size() - 1) / upsampling_factor_in;
00274   upsampling_factor = upsampling_factor_in;
00275 
00276   impulse_response = impulse_response_in;
00277   shaping_filter.set_coeffs(impulse_response);
00278   shaping_filter.clear();
00279   setup_done = true;
00280 }
00281 
00282 template<class T1, class T2, class T3>
00283 Vec<T2> Pulse_Shape<T1, T2, T3>::get_pulse_shape(void) const
00284 {
00285   return impulse_response;
00286 }
00287 
00288 template<class T1, class T2, class T3>
00289 int Pulse_Shape<T1, T2, T3>::get_upsampling_factor(void) const
00290 {
00291   return upsampling_factor;
00292 }
00293 
00294 template<class T1, class T2, class T3>
00295 int Pulse_Shape<T1, T2, T3>::get_pulse_length(void) const
00296 {
00297   return pulse_length;
00298 }
00299 
00300 template<class T1, class T2, class T3>
00301 int Pulse_Shape<T1, T2, T3>::get_filter_length(void) const
00302 {
00303   return impulse_response.size();
00304 }
00305 
00306 template<class T1, class T2, class T3>
00307 void Pulse_Shape<T1, T2, T3>::shape_symbols(const Vec<T1>& input, Vec<T3> &output)
00308 {
00309   it_assert(setup_done, "Pulse_Shape must be set up before using");
00310   it_error_if(pulse_length == 0, "Pulse_Shape: impulse response is zero length");
00311   it_error_if(input.size() == 0, "Pulse_Shape: input is zero length");
00312 
00313   if (upsampling_factor > 1)
00314     output = shaping_filter(upsample(input, upsampling_factor));
00315   else
00316     output = input;
00317 }
00318 
00319 template<class T1, class T2, class T3>
00320 Vec<T3> Pulse_Shape<T1, T2, T3>::shape_symbols(const Vec<T1>& input)
00321 {
00322   it_assert(setup_done, "Pulse_Shape must be set up before using");
00323   Vec<T3> temp;
00324   shape_symbols(input, temp);
00325   return temp;
00326 }
00327 
00328 template<class T1, class T2, class T3>
00329 void Pulse_Shape<T1, T2, T3>::shape_samples(const Vec<T1>& input, Vec<T3> &output)
00330 {
00331   it_assert(setup_done, "Pulse_Shape must be set up before using");
00332   it_error_if(pulse_length == 0, "Pulse_Shape: impulse response is zero length");
00333   it_error_if(input.size() == 0, "Pulse_Shape: input is zero length");
00334 
00335   if (upsampling_factor > 1)
00336     output = shaping_filter(input);
00337   else
00338     output = input;
00339 }
00340 
00341 template<class T1, class T2, class T3>
00342 Vec<T3> Pulse_Shape<T1, T2, T3>::shape_samples(const Vec<T1>& input)
00343 {
00344   it_assert(setup_done, "Pulse_Shape must be set up before using");
00345   Vec<T3> temp;
00346   shape_samples(input, temp);
00347   return temp;
00348 }
00349 
00350 template<class T1, class T2, class T3>
00351 void Pulse_Shape<T1, T2, T3>::clear(void)
00352 {
00353   it_assert(setup_done, "Pulse_Shape must be set up before using");
00354   shaping_filter.clear();
00355 }
00356 
00357 //-------------------- Raised_Cosine -----------------------------------
00358 
00359 template<class T1>
00360 Raised_Cosine<T1>::Raised_Cosine(double roll_off_factor, int filter_length, int upsampling_factor)
00361 {
00362   set_pulse_shape(roll_off_factor, filter_length, upsampling_factor);
00363 }
00364 
00365 template<class T1>
00366 void Raised_Cosine<T1>::set_pulse_shape(double roll_off_factor_in, int filter_length, int upsampling_factor_in)
00367 {
00368   it_error_if(roll_off_factor_in < 0 || roll_off_factor_in > 1, "Raised_Cosine: roll-off out of range");
00369   roll_off_factor = roll_off_factor_in;
00370 
00371   it_assert(is_even(filter_length), "Raised_Cosine: Filter length not even");
00372 
00373   int i;
00374   double t, den;
00375   this->upsampling_factor = upsampling_factor_in;
00376   this->pulse_length = filter_length;
00377   this->impulse_response.set_size(filter_length * upsampling_factor_in + 1,
00378                                   false);
00379 
00380   for (i = 0; i < this->impulse_response.size(); i++) {
00381     // delayed to be casual
00382     t = (double)(i - filter_length * upsampling_factor_in / 2)
00383         / upsampling_factor_in;
00384     den = 1 - sqr(2 * roll_off_factor * t);
00385     if (den == 0) {
00386       // exception according to "The Care and feeding of digital,
00387       // pulse-shaping filters" by Ken Gentile,
00388       // the limit of raised cosine impulse responce function,
00389       // as (alpha * t / tau) approaches (+- 0.5) is given as:
00390       this->impulse_response(i) = sinc(t) * pi / 4;
00391     }
00392     else {
00393       this->impulse_response(i) = std::cos(roll_off_factor * pi * t)
00394                                   * sinc(t) / den;
00395     }
00396   }
00397 
00398   // BUGFIX: Commented out to achieve similar results to Matlab
00399   // rcosfil function. Now the concatenation of two root-raised
00400   // cosine filters gives tha same results as a one raised cosine
00401   // shaping function.
00402   // this->impulse_response /= std::sqrt(double(this->upsampling_factor));
00403   this->shaping_filter.set_coeffs(this->impulse_response);
00404   this->shaping_filter.clear();
00405   this->setup_done = true;
00406 }
00407 
00408 template<class T1>
00409 double Raised_Cosine<T1>::get_roll_off(void) const
00410 {
00411   it_assert(this->setup_done, "Pulse_Shape must be set up before using");
00412   return roll_off_factor;
00413 }
00414 
00415 //-------------------- Root_Raised_Cosine -----------------------------------
00416 
00417 template<class T1>
00418 Root_Raised_Cosine<T1>::Root_Raised_Cosine(double roll_off_factor, int filter_length, int upsampling_factor)
00419 {
00420   set_pulse_shape(roll_off_factor, filter_length, upsampling_factor);
00421 }
00422 
00423 template<class T1>
00424 void Root_Raised_Cosine<T1>::set_pulse_shape(double roll_off_factor_in, int filter_length, int upsampling_factor_in)
00425 {
00426   it_error_if(roll_off_factor_in <= 0 || roll_off_factor_in > 1,
00427               "Root_Raised_Cosine: roll-off out of range");
00428   roll_off_factor = roll_off_factor_in;
00429 
00430   it_assert(is_even(filter_length),
00431             "Root_Raised_Cosine: Filter length not even");
00432 
00433   int i;
00434   double t, num, den, tmp_arg;
00435   this->upsampling_factor = upsampling_factor_in;
00436   this->pulse_length = filter_length;
00437   this->impulse_response.set_size(filter_length * upsampling_factor_in + 1,
00438                                   false);
00439 
00440   for (i = 0; i < this->impulse_response.size(); i++) {
00441     // delayed to be casual
00442     t = (double)(i - filter_length * upsampling_factor_in / 2)
00443         / upsampling_factor_in;
00444     den = 1 - sqr(4 * roll_off_factor * t);
00445     if (t == 0) {
00446       this->impulse_response(i) = 1 + (4 * roll_off_factor / pi)
00447                                   - roll_off_factor;
00448     }
00449     else if (den == 0) {
00450       tmp_arg = pi / (4 * roll_off_factor);
00451       this->impulse_response(i) = roll_off_factor / std::sqrt(2.0)
00452                                   * ((1 + 2 / pi) * std::sin(tmp_arg) + (1 - 2 / pi) * std::cos(tmp_arg));
00453     }
00454     else {
00455       num = std::sin(pi * (1 - roll_off_factor) * t)
00456             + std::cos(pi * (1 + roll_off_factor) * t) * 4 * roll_off_factor * t;
00457       this->impulse_response(i) = num / (pi * t * den);
00458     }
00459   }
00460 
00461   this->impulse_response /= std::sqrt(double(upsampling_factor_in));
00462   this->shaping_filter.set_coeffs(this->impulse_response);
00463   this->shaping_filter.clear();
00464   this->setup_done = true;
00465 }
00466 
00467 template<class T1>
00468 double Root_Raised_Cosine<T1>::get_roll_off(void) const
00469 {
00470   it_assert(this->setup_done, "Pulse_Shape must be set up before using");
00471   return roll_off_factor;
00472 }
00473 
00475 
00476 // ----------------------------------------------------------------------
00477 // Instantiations
00478 // ----------------------------------------------------------------------
00479 
00480 #ifdef HAVE_EXTERN_TEMPLATE
00481 
00482 extern template class Pulse_Shape<double, double, double>;
00483 extern template class Pulse_Shape < std::complex<double>, double,
00484   std::complex<double> >;
00485 extern template class Pulse_Shape < std::complex<double>, std::complex<double>,
00486   std::complex<double> >;
00487 
00488 extern template class Root_Raised_Cosine<double>;
00489 extern template class Root_Raised_Cosine<std::complex<double> >;
00490 
00491 extern template class Raised_Cosine<double>;
00492 extern template class Raised_Cosine<std::complex<double> >;
00493 
00494 #endif // HAVE_EXTERN_TEMPLATE
00495 
00497 
00498 } // namespace itpp
00499 
00500 #endif // #ifndef PULSE_SHAPE_H
SourceForge Logo

Generated on Thu Apr 23 20:06:42 2009 for IT++ by Doxygen 1.5.8