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
Generated on Wed Dec 7 2011 03:38:44 for IT++ by Doxygen 1.7.4