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