// create symtrack object with basic parameters // _ftype : filter type (e.g. LIQUID_FIRFILT_RRC) // _k : samples per symbol // _m : filter delay (symbols) // _beta : filter excess bandwidth // _ms : modulation scheme (e.g. LIQUID_MODEM_QPSK) SYMTRACK() SYMTRACK(_create)(int _ftype, unsigned int _k, unsigned int _m, float _beta, int _ms) { // validate input if (_k < 2) { fprintf(stderr,"error: symtrack_%s_create(), filter samples/symbol must be at least 2\n", EXTENSION_FULL); exit(1); } else if (_m == 0) { fprintf(stderr,"error: symtrack_%s_create(), filter delay must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_beta <= 0.0f || _beta > 1.0f) { fprintf(stderr,"error: symtrack_%s_create(), filter excess bandwidth must be in (0,1]\n", EXTENSION_FULL); exit(1); } else if (_ms == LIQUID_MODEM_UNKNOWN || _ms >= LIQUID_MODEM_NUM_SCHEMES) { fprintf(stderr,"error: symtrack_%s_create(), invalid modulation scheme\n", EXTENSION_FULL); exit(1); } // allocate memory for main object SYMTRACK() q = (SYMTRACK()) malloc( sizeof(struct SYMTRACK(_s)) ); // set input parameters q->filter_type = _ftype; q->k = _k; q->m = _m; q->beta = _beta; q->mod_scheme = _ms == LIQUID_MODEM_UNKNOWN ? LIQUID_MODEM_BPSK : _ms; // create automatic gain control q->agc = AGC(_create)(); // create symbol synchronizer (output rate: 2 samples per symbol) if (q->filter_type == LIQUID_FIRFILT_UNKNOWN) q->symsync = SYMSYNC(_create_kaiser)(q->k, q->m, 0.9f, 16); else q->symsync = SYMSYNC(_create_rnyquist)(q->filter_type, q->k, q->m, q->beta, 16); SYMSYNC(_set_output_rate)(q->symsync, 2); // create equalizer as default low-pass filter with integer symbol delay (2 samples/symbol) q->eq_len = 2 * 4 + 1; q->eq = EQLMS(_create_lowpass)(q->eq_len,0.45f); // nco and phase-locked loop q->nco = NCO(_create)(LIQUID_VCO); // demodulator q->demod = MODEM(_create)(q->mod_scheme); // set default bandwidth SYMTRACK(_set_bandwidth)(q, 0.9f); // reset and return main object SYMTRACK(_reset)(q); return q; }
// create square-root Nyquist symbol synchronizer // _type : filter type (e.g. LIQUID_RNYQUIST_RRC) // _k : samples/symbol // _m : symbol delay // _beta : rolloff factor (0 < beta <= 1) // _M : number of filters in the bank SYMSYNC() SYMSYNC(_create_rnyquist)(int _type, unsigned int _k, unsigned int _m, float _beta, unsigned int _M) { // validate input if (_k < 2) { fprintf(stderr,"error: symsync_%s_create_rnyquist(), samples/symbol must be at least 2\n", EXTENSION_FULL); exit(1); } else if (_m == 0) { fprintf(stderr,"error: symsync_%s_create_rnyquist(), filter delay (m) must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_beta < 0.0f || _beta > 1.0f) { fprintf(stderr,"error: symsync_%s_create_rnyquist(), filter excess bandwidth must be in [0,1]\n", EXTENSION_FULL); exit(1); } // allocate memory for filter coefficients unsigned int H_len = 2*_M*_k*_m + 1; float Hf[H_len]; // design square-root Nyquist pulse-shaping filter liquid_firdes_rnyquist(_type, _k*_M, _m, _beta, 0, Hf); // copy coefficients to type-specific array TC H[H_len]; unsigned int i; for (i=0; i<H_len; i++) H[i] = Hf[i]; // create object and return return SYMSYNC(_create)(_k, _M, H, H_len); }
// create symsync using Kaiser filter interpolator; useful // when the input signal has matched filter applied already // _k : input samples/symbol // _m : symbol delay // _beta : rolloff factor, beta in (0,1] // _M : number of filters in the bank SYMSYNC() SYMSYNC(_create_kaiser)(unsigned int _k, unsigned int _m, float _beta, unsigned int _M) { // validate input if (_k < 2) { fprintf(stderr,"error: symsync_%s_create_kaiser(), samples/symbol must be at least 2\n", EXTENSION_FULL); exit(1); } else if (_m == 0) { fprintf(stderr,"error: symsync_%s_create_kaiser(), filter delay (m) must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_beta < 0.0f || _beta > 1.0f) { fprintf(stderr,"error: symsync_%s_create_kaiser(), filter excess bandwidth must be in [0,1]\n", EXTENSION_FULL); exit(1); } // allocate memory for filter coefficients unsigned int H_len = 2*_M*_k*_m + 1; float Hf[H_len]; // design interpolating filter whose bandwidth is outside the cut-off // frequency of input signal // TODO: use _beta to compute more accurate cut-off frequency float fc = 0.75f; // filter cut-off frequency (nominal) float As = 40.0f; // filter stop-band attenuation liquid_firdes_kaiser(H_len, fc / (float)(_k*_M), As, 0.0f, Hf); // copy coefficients to type-specific array, adjusting to relative // filter gain unsigned int i; TC H[H_len]; for (i=0; i<H_len; i++) H[i] = Hf[i] * 2.0f * fc; // create object and return return SYMSYNC(_create)(_k, _M, H, H_len); }
// create synchronizer object from external coefficients // _k : samples per symbol // _M : number of filters in the bank // _h : matched filter coefficients // _h_len : length of matched filter SYMSYNC() SYMSYNC(_create)(unsigned int _k, unsigned int _M, TC * _h, unsigned int _h_len) { // validate input if (_k < 2) { fprintf(stderr,"error: symsync_%s_create(), input sample rate must be at least 2\n", EXTENSION_FULL); exit(1); } else if (_h_len == 0) { fprintf(stderr,"error: symsync_%s_create(), filter length must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_M == 0) { fprintf(stderr,"error: symsync_%s_create(), number of filter banks must be greater than zero\n", EXTENSION_FULL); exit(1); } SYMSYNC() q = (SYMSYNC()) malloc(sizeof(struct SYMSYNC(_s))); q->k = _k; q->M = _M; q->tau = 0.0f; q->bf = 0.0f; q->b = 0; // set output rate (nominally 1, full decimation) SYMSYNC(_set_output_rate)(q, 1); // TODO: validate length q->h_len = (_h_len-1)/q->M; // compute derivative filter TC dh[_h_len]; float hdh_max = 0.0f; unsigned int i; for (i=0; i<_h_len; i++) { if (i==0) { dh[i] = _h[i+1] - _h[_h_len-1]; } else if (i==_h_len-1) { dh[i] = _h[0] - _h[i-1]; } else { dh[i] = _h[i+1] - _h[i-1]; } // find maximum of h*dh if ( fabsf(_h[i]*dh[i]) > hdh_max || i==0 ) hdh_max = fabsf(_h[i]*dh[i]); } // apply scaling factor for normalized response // TODO: scale to 1.0 for consistency for (i=0; i<_h_len; i++) dh[i] *= 0.06f / hdh_max; q->mf = FIRPFB(_create)(q->M, _h, _h_len); q->dmf = FIRPFB(_create)(q->M, dh, _h_len); // reset state and initialize loop filter q->A[0] = 1.0f; q->B[0] = 0.0f; q->A[1] = 0.0f; q->B[1] = 0.0f; q->A[2] = 0.0f; q->B[2] = 0.0f; q->pll = iirfiltsos_rrrf_create(q->B, q->A); SYMSYNC(_reset)(q); SYMSYNC(_set_lf_bw)(q, 0.01f); // set output rate nominally at 1 sample/symbol (full decimation) SYMSYNC(_set_output_rate)(q, 1); // unlock loop control SYMSYNC(_unlock)(q); #if DEBUG_SYMSYNC q->debug_del = windowf_create(DEBUG_BUFFER_LEN); q->debug_tau = windowf_create(DEBUG_BUFFER_LEN); q->debug_bsoft = windowf_create(DEBUG_BUFFER_LEN); q->debug_b = windowf_create(DEBUG_BUFFER_LEN); q->debug_q_hat = windowf_create(DEBUG_BUFFER_LEN); #endif // return main object return q; }
#define DEBUG_SYMSYNC 0 #define DEBUG_SYMSYNC_PRINT 0 #define DEBUG_SYMSYNC_FILENAME "symsync_internal_debug.m" #define DEBUG_BUFFER_LEN (1024) // // forward declaration of internal methods // // step synchronizer // _q : symsync object // _x : input sample // _y : output sample array pointer // _ny : number of output samples written void SYMSYNC(_step)(SYMSYNC() _q, TI _x, TO * _y, unsigned int * _ny); // advance synchronizer's internal loop filter // _q : synchronizer object // _mf : matched-filter output // _dmf : derivative matched-filter output void SYMSYNC(_advance_internal_loop)(SYMSYNC() _q, TO _mf, TO _dmf); // print results to output debugging file // _q : synchronizer object // _filename : output filename
float Hf[H_len]; // design square-root Nyquist pulse-shaping filter liquid_firdes_rnyquist(_type, _k*_npfb, _m, _beta, 0, Hf); // copy coefficients to type-specific array TC H[H_len]; unsigned int i; for (i=0; i<H_len; i++) H[i] = Hf[i]; // create object and return return SYMSYNC(_create)(_k, _npfb, H, H_len); } void SYMSYNC(_destroy)(SYMSYNC() _q) { #if DEBUG_SYMSYNC // output debugging file SYMSYNC(_output_debug_file)(_q, DEBUG_SYMSYNC_FILENAME); // destroy internal window objects windowf_destroy(_q->debug_del); windowf_destroy(_q->debug_tau); windowf_destroy(_q->debug_bsoft); windowf_destroy(_q->debug_b); windowf_destroy(_q->debug_q_hat); #endif // destroy filterbank objects FIRPFB(_destroy)(_q->mf);