/* _m : number of correlators */ PRESYNC() PRESYNC(_create)(TC * _v, unsigned int _n, float _dphi_max, unsigned int _m) { // validate input if (_n < 1) { fprintf(stderr, "error: bpresync_%s_create(), invalid input length\n", EXTENSION_FULL); exit(1); } else if (_m == 0) { fprintf(stderr, "error: bpresync_%s_create(), number of correlators must be at least 1\n", EXTENSION_FULL); exit(1); } // allocate main object memory and initialize PRESYNC() _q = (PRESYNC()) malloc(sizeof(struct PRESYNC(_s))); _q->n = _n; _q->m = _m; _q->n_inv = 1.0f / (float)(_q->n); unsigned int i; // create internal receive buffers _q->rx_i = WINDOW(_create)(_q->n); _q->rx_q = WINDOW(_create)(_q->n); // create internal array of frequency offsets _q->dphi = (float*) malloc( _q->m*sizeof(float) ); // create internal synchronizers _q->sync_i = (DOTPROD()*) malloc( _q->m*sizeof(DOTPROD()) ); _q->sync_q = (DOTPROD()*) malloc( _q->m*sizeof(DOTPROD()) ); // buffer T vi_prime[_n]; T vq_prime[_n]; for (i=0; i<_q->m; i++) { // generate signal with frequency offset _q->dphi[i] = (float)i / (float)(_q->m-1)*_dphi_max; unsigned int k; for (k=0; k<_q->n; k++) { vi_prime[k] = REAL( _v[k] * cexpf(-_Complex_I*k*_q->dphi[i]) ); vq_prime[k] = IMAG( _v[k] * cexpf(-_Complex_I*k*_q->dphi[i]) ); } _q->sync_i[i] = DOTPROD(_create)(vi_prime, _q->n); _q->sync_q[i] = DOTPROD(_create)(vq_prime, _q->n); } // allocate memory for cross-correlation _q->rxy = (float*) malloc( _q->m*sizeof(float) ); // reset object PRESYNC(_reset)(_q); return _q; }
// create firpfb from external coefficients // _M : number of filters in the bank // _h : coefficients [size: _M*_h_len x 1] // _h_len : filter delay (symbols) FIRPFB() FIRPFB(_create)(unsigned int _M, TC * _h, unsigned int _h_len) { // validate input if (_M == 0) { fprintf(stderr,"error: firpfb_%s_create(), number of filters must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_h_len == 0) { fprintf(stderr,"error: firpfb_%s_create(), filter length must be greater than zero\n", EXTENSION_FULL); exit(1); } // create main filter object FIRPFB() q = (FIRPFB()) malloc(sizeof(struct FIRPFB(_s))); // set user-defined parameters q->num_filters = _M; q->h_len = _h_len; // each filter is realized as a dotprod object q->dp = (DOTPROD()*) malloc((q->num_filters)*sizeof(DOTPROD())); // generate bank of sub-samped filters // length of each sub-sampled filter unsigned int h_sub_len = _h_len / q->num_filters; TC h_sub[h_sub_len]; unsigned int i, n; for (i=0; i<q->num_filters; i++) { for (n=0; n<h_sub_len; n++) { // load filter in reverse order h_sub[h_sub_len-n-1] = _h[i + n*(q->num_filters)]; } // create dot product object q->dp[i] = DOTPROD(_create)(h_sub,h_sub_len); } // save sub-sampled filter length q->h_sub_len = h_sub_len; // create window buffer q->w = WINDOW(_create)(q->h_sub_len); // set default scaling q->scale = 1; // reset object and return FIRPFB(_reset)(q); return q; }
// create a resamp2 object // _m : filter semi-length (effective length: 4*_m+1) // _fc : center frequency of half-band filter // _As : stop-band attenuation [dB], _As > 0 RESAMP2() RESAMP2(_create)(unsigned int _m, float _fc, float _As) { // validate input if (_m < 2) { fprintf(stderr,"error: resamp2_%s_create(), filter semi-length must be at least 2\n", EXTENSION_FULL); exit(1); } RESAMP2() q = (RESAMP2()) malloc(sizeof(struct RESAMP2(_s))); q->m = _m; q->fc = _fc; q->As = _As; if ( q->fc < -0.5f || q->fc > 0.5f ) { fprintf(stderr,"error: resamp2_%s_create(), fc (%12.4e) must be in (-1,1)\n", EXTENSION_FULL, q->fc); exit(1); } // change filter length as necessary q->h_len = 4*(q->m) + 1; q->h = (TC *) malloc((q->h_len)*sizeof(TC)); q->h1_len = 2*(q->m); q->h1 = (TC *) malloc((q->h1_len)*sizeof(TC)); // design filter prototype unsigned int i; float t, h1, h2; TC h3; float beta = kaiser_beta_As(q->As); for (i=0; i<q->h_len; i++) { t = (float)i - (float)(q->h_len-1)/2.0f; h1 = sincf(t/2.0f); h2 = kaiser(i,q->h_len,beta,0); #if TC_COMPLEX == 1 h3 = cosf(2.0f*M_PI*t*q->fc) + _Complex_I*sinf(2.0f*M_PI*t*q->fc); #else h3 = cosf(2.0f*M_PI*t*q->fc); #endif q->h[i] = h1*h2*h3; } // resample, alternate sign, [reverse direction] unsigned int j=0; for (i=1; i<q->h_len; i+=2) q->h1[j++] = q->h[q->h_len - i - 1]; // create dotprod object q->dp = DOTPROD(_create)(q->h1, 2*q->m); // create window buffers q->w0 = WINDOW(_create)(2*(q->m)); q->w1 = WINDOW(_create)(2*(q->m)); RESAMP2(_clear)(q); return q; }
void firpfbch_print(firpfbch _c) { printf("firpfbch: [%u channels]\n", _c->num_channels); unsigned int i; for (i=0; i<_c->num_channels; i++) { DOTPROD(_print)(_c->dp[i]); } }
void firpfbch_analyzer_run(firpfbch _c, float complex * _y) { // NOTE: The analyzer is different from the synthesizer in // that the invocation of the commutator results in a // delay from the first input sample to the resulting // partitions. As a result, the inverse DFT is // invoked after the first filter is run, after which // the remaining filters are executed. // restore saved IDFT input state X from X_prime memmove(_c->X, _c->X_prime, (_c->num_channels)*sizeof(float complex)); unsigned int i, b; unsigned int k = _c->filter_index; // push first value and compute output float complex * r; WINDOW(_read)(_c->w[k], &r); DOTPROD(_execute)(_c->dp[0], r, &(_c->X[0])); // execute inverse fft, store in buffer _c->x FFT_EXECUTE(_c->fft); // copy results to output buffer memmove(_y, _c->x, (_c->num_channels)*sizeof(float complex)); // push remaining samples into filter bank and execute in // *reverse* order, putting result into the inverse DFT // input buffer _c->X //for (i=1; i<_c->num_channels; i++) { // NOTE : the filter window buffers have already been loaded // in the proper reverse order, so there is no need // to execute the dot products in any particular order, // so long as they are aligned with the proper input // buffer. for (i=1; i<_c->num_channels; i++) { b = (k+i) % _c->num_channels; WINDOW(_read)(_c->w[b], &r); DOTPROD(_execute)(_c->dp[i], r, &(_c->X[i])); } }
// create firhilb object // _m : filter semi-length (delay: 2*m+1) // _As : stop-band attenuation [dB] FIRHILB() FIRHILB(_create)(unsigned int _m, float _As) { // validate firhilb inputs if (_m < 2) { fprintf(stderr,"error: firhilb_create(), filter semi-length (m) must be at least 2\n"); exit(1); } // allocate memory for main object FIRHILB() q = (FIRHILB()) malloc(sizeof(struct FIRHILB(_s))); q->m = _m; // filter semi-length q->As = fabsf(_As); // stop-band attenuation // set filter length and allocate memory for coefficients q->h_len = 4*(q->m) + 1; q->h = (T *) malloc((q->h_len)*sizeof(T)); q->hc = (T complex *) malloc((q->h_len)*sizeof(T complex)); // allocate memory for quadrature filter component q->hq_len = 2*(q->m); q->hq = (T *) malloc((q->hq_len)*sizeof(T)); // compute filter coefficients for half-band filter liquid_firdes_kaiser(q->h_len, 0.25f, q->As, 0.0f, q->h); // alternate sign of non-zero elements unsigned int i; for (i=0; i<q->h_len; i++) { float t = (float)i - (float)(q->h_len-1)/2.0f; q->hc[i] = q->h[i] * cexpf(_Complex_I*0.5f*M_PI*t); q->h[i] = cimagf(q->hc[i]); } // resample, reverse direction unsigned int j=0; for (i=1; i<q->h_len; i+=2) q->hq[j++] = q->h[q->h_len - i - 1]; // create windows for upper and lower polyphase filter branches q->w1 = WINDOW(_create)(2*(q->m)); q->w0 = WINDOW(_create)(2*(q->m)); WINDOW(_clear)(q->w0); WINDOW(_clear)(q->w1); // create internal dot product object q->dpq = DOTPROD(_create)(q->hq, q->hq_len); // reset internal state and return object FIRHILB(_reset)(q); return q; }
void firpfbch_destroy(firpfbch _c) { unsigned int i; for (i=0; i<_c->num_channels; i++) { DOTPROD(_destroy)(_c->dp[i]); WINDOW(_destroy)(_c->w[i]); } free(_c->dp); free(_c->w); FFT_DESTROY_PLAN(_c->fft); free(_c->h); free(_c->x); free(_c->X); free(_c->X_prime); free(_c); }
// create firfilt object // _h : coefficients (filter taps) [size: _n x 1] // _n : filter length FIRFILT() FIRFILT(_create)(TC * _h, unsigned int _n) { // validate input if (_n == 0) { fprintf(stderr,"error: firfilt_%s_create(), filter length must be greater than zero\n", EXTENSION_FULL); exit(1); } // create filter object and initialize FIRFILT() q = (FIRFILT()) malloc(sizeof(struct FIRFILT(_s))); q->h_len = _n; q->h = (TC *) malloc((q->h_len)*sizeof(TC)); #if LIQUID_FIRFILT_USE_WINDOW // create window (internal buffer) q->w = WINDOW(_create)(q->h_len); #else // initialize array for buffering q->w_len = 1<<liquid_msb_index(q->h_len); // effectively 2^{floor(log2(len))+1} q->w_mask = q->w_len - 1; q->w = (TI *) malloc((q->w_len + q->h_len + 1)*sizeof(TI)); q->w_index = 0; #endif // load filter in reverse order unsigned int i; for (i=_n; i>0; i--) q->h[i-1] = _h[_n-i]; // create dot product object q->dp = DOTPROD(_create)(q->h, q->h_len); // set default scaling q->scale = 1; // reset filter state (clear buffer) FIRFILT(_reset)(q); return q; }
void firpfbch_synthesizer_execute(firpfbch _c, float complex * _x, float complex * _y) { unsigned int i; // copy samples into ifft input buffer _c->X memmove(_c->X, _x, (_c->num_channels)*sizeof(float complex)); // execute inverse fft, store in buffer _c->x FFT_EXECUTE(_c->fft); // push samples into filter bank and execute, putting // samples into output buffer _y float complex * r; for (i=0; i<_c->num_channels; i++) { WINDOW(_push)(_c->w[i], _c->x[i]); WINDOW(_read)(_c->w[i], &r); DOTPROD(_execute)(_c->dp[i], r, &(_y[i])); // invoke scaling factor _y[i] /= (float)(_c->num_channels); } }
// create decimator object // _M : decimation factor // _h : filter coefficients [size: _h_len x 1] // _h_len : filter coefficients length FIRDECIM() FIRDECIM(_create)(unsigned int _M, TC * _h, unsigned int _h_len) { // validate input if (_h_len == 0) { fprintf(stderr,"error: decim_%s_create(), filter length must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_M == 0) { fprintf(stderr,"error: decim_%s_create(), decimation factor must be greater than zero\n", EXTENSION_FULL); exit(1); } FIRDECIM() q = (FIRDECIM()) malloc(sizeof(struct FIRDECIM(_s))); q->h_len = _h_len; q->M = _M; // allocate memory for coefficients q->h = (TC*) malloc((q->h_len)*sizeof(TC)); // load filter in reverse order unsigned int i; for (i=0; i<q->h_len; i++) q->h[i] = _h[_h_len-i-1]; // create window (internal buffer) q->w = WINDOW(_create)(q->h_len); // create dot product object q->dp = DOTPROD(_create)(q->h, q->h_len); // reset filter state (clear buffer) FIRDECIM(_clear)(q); return q; }
firpfbch firpfbch_create(unsigned int _num_channels, unsigned int _m, float _beta, float _dt, int _nyquist, int _gradient) { firpfbch c = (firpfbch) malloc(sizeof(struct firpfbch_s)); c->num_channels = _num_channels; c->m = _m; c->beta = _beta; c->dt = _dt; c->nyquist = _nyquist; // validate inputs if (_m < 1) { printf("error: firpfbch_create(), invalid filter delay (must be greater than 0)\n"); exit(1); } // create bank of filters c->dp = (DOTPROD()*) malloc((c->num_channels)*sizeof(DOTPROD())); c->w = (WINDOW()*) malloc((c->num_channels)*sizeof(WINDOW())); // design filter // TODO: use filter prototype object c->h_len = 2*(c->m)*(c->num_channels); c->h = (float*) malloc((c->h_len+1)*sizeof(float)); if (c->nyquist == FIRPFBCH_NYQUIST) { float fc = 0.5f/(float)(c->num_channels); // cutoff frequency liquid_firdes_kaiser(c->h_len+1, fc, c->beta, 0.0f, c->h); } else if (c->nyquist == FIRPFBCH_ROOTNYQUIST) { design_rkaiser_filter(c->num_channels, c->m, c->beta, c->dt, c->h); } else { printf("error: firpfbch_create(), unsupported nyquist flag: %d\n", _nyquist); exit(1); } unsigned int i; if (_gradient) { float dh[c->h_len]; for (i=0; i<c->h_len; i++) { if (i==0) { dh[i] = c->h[i+1] - c->h[c->h_len-1]; } else if (i==c->h_len-1) { dh[i] = c->h[0] - c->h[i-1]; } else { dh[i] = c->h[i+1] - c->h[i-1]; } } memmove(c->h, dh, (c->h_len)*sizeof(float)); } // generate bank of sub-samped filters unsigned int n; unsigned int h_sub_len = 2*(c->m); // length of each sub-sampled filter float h_sub[h_sub_len]; for (i=0; i<c->num_channels; i++) { // sub-sample prototype filter, loading coefficients in reverse order for (n=0; n<h_sub_len; n++) { h_sub[h_sub_len-n-1] = c->h[i + n*(c->num_channels)]; } // create window buffer and dotprod object (coefficients // loaded in reverse order) c->dp[i] = DOTPROD(_create)(h_sub,h_sub_len); c->w[i] = WINDOW(_create)(h_sub_len); #if DEBUG_FIRPFBCH_PRINT printf("h_sub[%u] :\n", i); for (n=0; n<h_sub_len; n++) printf(" h[%3u] = %8.4f\n", n, h_sub[n]); #endif } #if DEBUG_FIRPFBCH_PRINT for (i=0; i<c->h_len+1; i++) printf("h(%4u) = %12.4e;\n", i+1, c->h[i]); #endif // allocate memory for buffers // TODO : use fftw_malloc if HAVE_FFTW3_H c->x = (float complex*) malloc((c->num_channels)*sizeof(float complex)); c->X = (float complex*) malloc((c->num_channels)*sizeof(float complex)); c->X_prime = (float complex*) malloc((c->num_channels)*sizeof(float complex)); firpfbch_clear(c); // create fft plan c->fft = FFT_CREATE_PLAN(c->num_channels, c->X, c->x, FFT_DIR_BACKWARD, FFT_METHOD); return c; }
// create FIR polyphase filterbank channelizer object // _type : channelizer type (LIQUID_ANALYZER | LIQUID_SYNTHESIZER) // _M : number of channels // _p : filter length (symbols) // _h : filter coefficients, [size: _M*_p x 1] FIRPFBCH() FIRPFBCH(_create)(int _type, unsigned int _M, unsigned int _p, TC * _h) { // validate input if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) { fprintf(stderr,"error: firpfbch_%s_create(), invalid type %d\n", EXTENSION_FULL, _type); exit(1); } else if (_M == 0) { fprintf(stderr,"error: firpfbch_%s_create(), number of channels must be greater than 0\n", EXTENSION_FULL); exit(1); } else if (_p == 0) { fprintf(stderr,"error: firpfbch_%s_create(), invalid filter size (must be greater than 0)\n", EXTENSION_FULL); exit(1); } // create main object FIRPFBCH() q = (FIRPFBCH()) malloc(sizeof(struct FIRPFBCH(_s))); // set user-defined properties q->type = _type; q->num_channels = _M; q->p = _p; // derived values q->h_len = q->num_channels * q->p; // create bank of filters q->dp = (DOTPROD()*) malloc((q->num_channels)*sizeof(DOTPROD())); q->w = (WINDOW()*) malloc((q->num_channels)*sizeof(WINDOW())); // copy filter coefficients q->h = (TC*) malloc((q->h_len)*sizeof(TC)); unsigned int i; for (i=0; i<q->h_len; i++) q->h[i] = _h[i]; // generate bank of sub-samped filters unsigned int n; unsigned int h_sub_len = q->p; TC h_sub[h_sub_len]; for (i=0; i<q->num_channels; i++) { // sub-sample prototype filter, loading coefficients in reverse order for (n=0; n<h_sub_len; n++) { h_sub[h_sub_len-n-1] = q->h[i + n*(q->num_channels)]; } // create window buffer and dotprod object (coefficients // loaded in reverse order) q->dp[i] = DOTPROD(_create)(h_sub,h_sub_len); q->w[i] = WINDOW(_create)(h_sub_len); } // allocate memory for buffers // TODO : use fftw_malloc if HAVE_FFTW3_H q->x = (T*) malloc((q->num_channels)*sizeof(T)); q->X = (T*) malloc((q->num_channels)*sizeof(T)); // create fft plan if (q->type == LIQUID_ANALYZER) q->fft = FFT_CREATE_PLAN(q->num_channels, q->X, q->x, FFT_DIR_FORWARD, FFT_METHOD); else q->fft = FFT_CREATE_PLAN(q->num_channels, q->X, q->x, FFT_DIR_BACKWARD, FFT_METHOD); // reset filterbank object FIRPFBCH(_reset)(q); // return filterbank object return q; }
// create FFT plan for regular DFT // _nfft : FFT size // _x : input array [size: _nfft x 1] // _y : output array [size: _nfft x 1] // _dir : fft direction: {LIQUID_FFT_FORWARD, LIQUID_FFT_BACKWARD} // _method : fft method FFT(plan) FFT(_create_plan_dft)(unsigned int _nfft, TC * _x, TC * _y, int _dir, int _flags) { // allocate plan and initialize all internal arrays to NULL FFT(plan) q = (FFT(plan)) malloc(sizeof(struct FFT(plan_s))); q->nfft = _nfft; q->x = _x; q->y = _y; q->flags = _flags; q->type = (_dir == LIQUID_FFT_FORWARD) ? LIQUID_FFT_FORWARD : LIQUID_FFT_BACKWARD; q->direction = (_dir == LIQUID_FFT_FORWARD) ? LIQUID_FFT_FORWARD : LIQUID_FFT_BACKWARD; q->method = LIQUID_FFT_METHOD_DFT; q->data.dft.twiddle = NULL; q->data.dft.dotprod = NULL; // check size, use specific codelet for small DFTs if (q->nfft == 2) q->execute = FFT(_execute_dft_2); else if (q->nfft == 3) q->execute = FFT(_execute_dft_3); else if (q->nfft == 4) q->execute = FFT(_execute_dft_4); else if (q->nfft == 5) q->execute = FFT(_execute_dft_5); else if (q->nfft == 6) q->execute = FFT(_execute_dft_6); else if (q->nfft == 7) q->execute = FFT(_execute_dft_7); else if (q->nfft == 8) q->execute = FFT(_execute_dft_8); else if (q->nfft == 16) q->execute = FFT(_execute_dft_16); else { q->execute = FFT(_execute_dft); // initialize twiddle factors q->data.dft.twiddle = (TC *) malloc(q->nfft * sizeof(TC)); // create dotprod objects q->data.dft.dotprod = (DOTPROD()*) malloc(q->nfft * sizeof(DOTPROD())); // create dotprod objects // twiddles: exp(-j*2*pi*W/n), W= // 0 0 0 0 0... // 0 1 2 3 4... // 0 2 4 6 8... // 0 3 6 9 12... // ... // Note that first row/column is zero, no multiplication necessary. // Create dotprod for first row anyway because it's still faster... unsigned int i; unsigned int k; T d = (q->direction == LIQUID_FFT_FORWARD) ? -1.0 : 1.0; for (i=0; i<q->nfft; i++) { // initialize twiddle factors // NOTE: no need to compute first twiddle because exp(-j*2*pi*0) = 1 for (k=1; k<q->nfft; k++) q->data.dft.twiddle[k-1] = cexpf(_Complex_I*d*2*M_PI*(T)(k*i) / (T)(q->nfft)); // create dotprod object q->data.dft.dotprod[i] = DOTPROD(_create)(q->data.dft.twiddle, q->nfft-1); } } return q; }