// generate short sequence symbols // _p : subcarrier allocation array // _M : total number of subcarriers // _S0 : output symbol (freq) // _s0 : output symbol (time) // _M_S0 : total number of enabled subcarriers in S0 void ofdmframe_init_S0(unsigned char * _p, unsigned int _M, float complex * _S0, float complex * _s0, unsigned int * _M_S0) { unsigned int i; // compute m-sequence length unsigned int m = liquid_nextpow2(_M); if (m < 4) m = 4; else if (m > 8) m = 8; // generate m-sequence generator object msequence ms = msequence_create_default(m); unsigned int s; unsigned int M_S0 = 0; // short sequence for (i=0; i<_M; i++) { // generate symbol //s = msequence_generate_symbol(ms,1); s = msequence_generate_symbol(ms,3) & 0x01; if (_p[i] == OFDMFRAME_SCTYPE_NULL) { // NULL subcarrier _S0[i] = 0.0f; } else { if ( (i%2) == 0 ) { // even subcarrer _S0[i] = s ? 1.0f : -1.0f; M_S0++; } else { // odd subcarrer (ignore) _S0[i] = 0.0f; } } } // destroy objects msequence_destroy(ms); // ensure at least one subcarrier was enabled if (M_S0 == 0) { fprintf(stderr,"error: ofdmframe_init_S0(), no subcarriers enabled; check allocation\n"); exit(1); } // set return value(s) *_M_S0 = M_S0; // run inverse fft to get time-domain sequence fft_run(_M, _S0, _s0, FFT_REVERSE, 0); // normalize time-domain sequence level float g = 1.0f / sqrtf(M_S0); for (i=0; i<_M; i++) _s0[i] *= g; }
// generate long sequence symbols // _p : subcarrier allocation array // _M : total number of subcarriers // _S1 : output symbol (freq) // _s1 : output symbol (time) // _M_S1 : total number of enabled subcarriers in S1 void ofdmframe_init_S1(unsigned char * _p, unsigned int _M, float complex * _S1, float complex * _s1, unsigned int * _M_S1) { unsigned int i; // compute m-sequence length unsigned int m = liquid_nextpow2(_M); if (m < 4) m = 4; else if (m > 8) m = 8; // increase m such that the resulting S1 sequence will // differ significantly from S0 with the same subcarrier // allocation array m++; // generate m-sequence generator object msequence ms = msequence_create_default(m); unsigned int s; unsigned int M_S1 = 0; // long sequence for (i=0; i<_M; i++) { // generate symbol //s = msequence_generate_symbol(ms,1); s = msequence_generate_symbol(ms,3) & 0x01; if (_p[i] == OFDMFRAME_SCTYPE_NULL) { // NULL subcarrier _S1[i] = 0.0f; } else { _S1[i] = s ? 1.0f : -1.0f; M_S1++; } } // destroy objects msequence_destroy(ms); // ensure at least one subcarrier was enabled if (M_S1 == 0) { fprintf(stderr,"error: ofdmframe_init_S1(), no subcarriers enabled; check allocation\n"); exit(1); } // set return value(s) *_M_S1 = M_S1; // run inverse fft to get time-domain sequence fft_run(_M, _S1, _s1, FFT_REVERSE, 0); // normalize time-domain sequence level float g = 1.0f / sqrtf(M_S1); for (i=0; i<_M; i++) _s1[i] *= g; }
// generate long sequence symbols // _p : subcarrier allocation array // _num_subcarriers : total number of subcarriers // _S1 : output symbol // _M_S1 : total number of enabled subcarriers in S1 void ofdmoqamframe_init_S1(unsigned char * _p, unsigned int _num_subcarriers, float complex * _S1, unsigned int * _M_S1) { unsigned int i; // compute m-sequence length unsigned int m = liquid_nextpow2(_num_subcarriers); if (m < 4) m = 4; else if (m > 8) m = 8; // increase m such that the resulting S1 sequence will // differ significantly from S0 with the same subcarrier // allocation array m++; // generate m-sequence generator object msequence ms = msequence_create_default(m); unsigned int s; unsigned int M_S1 = 0; // long sequence for (i=0; i<_num_subcarriers; i++) { // generate symbol //s = msequence_generate_symbol(ms,1); s = msequence_generate_symbol(ms,3) & 0x01; if (_p[i] == OFDMOQAMFRAME_SCTYPE_NULL) { // NULL subcarrier _S1[i] = 0.0f; } else { _S1[i] = s ? 1.0f : -1.0f; M_S1++; // rotate by pi/2 on odd subcarriers _S1[i] *= (i%2)==0 ? 1.0f : _Complex_I; } } // destroy objects msequence_destroy(ms); // ensure at least one subcarrier was enabled if (M_S1 == 0) { fprintf(stderr,"error: ofdmoqamframe_init_S1(), no subcarriers enabled; check allocation\n"); exit(1); } // set return value(s) *_M_S1 = M_S1; }
// generate short sequence symbols // _p : subcarrier allocation array // _num_subcarriers : total number of subcarriers // _S0 : output symbol // _M_S0 : total number of enabled subcarriers in S0 void ofdmoqamframe_init_S0(unsigned char * _p, unsigned int _num_subcarriers, float complex * _S0, unsigned int * _M_S0) { unsigned int i; // compute m-sequence length unsigned int m = liquid_nextpow2(_num_subcarriers); if (m < 4) m = 4; else if (m > 8) m = 8; // generate m-sequence generator object msequence ms = msequence_create_default(m); unsigned int s; unsigned int M_S0 = 0; // short sequence for (i=0; i<_num_subcarriers; i++) { // generate symbol //s = msequence_generate_symbol(ms,1); s = msequence_generate_symbol(ms,3) & 0x01; if (_p[i] == OFDMOQAMFRAME_SCTYPE_NULL) { // NULL subcarrier _S0[i] = 0.0f; } else { if ( (i%2) == 0 ) { // even subcarrer _S0[i] = s ? 1.0f : -1.0f; M_S0++; } else { // odd subcarrer (ignore) _S0[i] = 0.0f; } } } // destroy objects msequence_destroy(ms); // ensure at least one subcarrier was enabled if (M_S0 == 0) { fprintf(stderr,"error: ofdmoqamframe_init_S0(), no subcarriers enabled; check allocation\n"); exit(1); } // set return value(s) *_M_S0 = M_S0; }
// create arbitrary digital modem object MODEM() MODEM(_create_arbitrary)(TC * _table, unsigned int _M) { // strip out bits/symbol unsigned int m = liquid_nextpow2(_M); if ( (1<<m) != _M ) { // TODO : eventually support non radix-2 constellation sizes fprintf(stderr,"error: modem_create_arbitrary(), input constellation size must be power of 2\n"); exit(1); } // create arbitrary modem object, not initialized MODEM() q = MODEM(_create_arb)(m); // initialize object from table MODEM(_arb_init)(q, _table, _M); // return object return q; }
// // AUTOTEST : test multi-stage arbitrary resampler // void autotest_msresamp_crcf() { // options unsigned int m = 13; // filter semi-length (filter delay) float r=0.127115323f; // resampling rate (output/input) float As=60.0f; // resampling filter stop-band attenuation [dB] unsigned int n=1200; // number of input samples float fx=0.0254230646f; // complex input sinusoid frequency (0.2*r) //float bw=0.45f; // resampling filter bandwidth //unsigned int npfb=64; // number of filters in bank (timing resolution) unsigned int i; // number of input samples (zero-padded) unsigned int nx = n + m; // output buffer with extra padding for good measure unsigned int y_len = (unsigned int) ceilf(1.1 * nx * r) + 4; // arrays float complex x[nx]; float complex y[y_len]; // create resampler msresamp_crcf q = msresamp_crcf_create(r,As); // generate input signal float wsum = 0.0f; for (i=0; i<nx; i++) { // compute window float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f; // apply window to complex sinusoid x[i] = cexpf(_Complex_I*2*M_PI*fx*i) * w; // accumulate window wsum += w; } // resample unsigned int ny=0; unsigned int nw; for (i=0; i<nx; i++) { // execute resampler, storing in output buffer msresamp_crcf_execute(q, &x[i], 1, &y[ny], &nw); // increment output size ny += nw; } // clean up allocated objects msresamp_crcf_destroy(q); // // analyze resulting signal // // check that the actual resampling rate is close to the target float r_actual = (float)ny / (float)nx; float fy = fx / r; // expected output frequency // run FFT and ensure that carrier has moved and that image // frequencies and distortion have been adequately suppressed unsigned int nfft = 1 << liquid_nextpow2(ny); float complex yfft[nfft]; // fft input float complex Yfft[nfft]; // fft output for (i=0; i<nfft; i++) yfft[i] = i < ny ? y[i] : 0.0f; fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0); fft_shift(Yfft, nfft); // run FFT shift // find peak frequency float Ypeak = 0.0f; float fpeak = 0.0f; float max_sidelobe = -1e9f; // maximum side-lobe [dB] float main_lobe_width = 0.07f; // TODO: figure this out from Kaiser's equations for (i=0; i<nfft; i++) { // normalized output frequency float f = (float)i/(float)nfft - 0.5f; // scale FFT output appropriately Yfft[i] /= (r * wsum); float Ymag = 20*log10f( cabsf(Yfft[i]) ); // find frequency location of maximum magnitude if (Ymag > Ypeak || i==0) { Ypeak = Ymag; fpeak = f; } // find peak side-lobe value, ignoring frequencies // within a certain range of signal frequency if ( fabsf(f-fy) > main_lobe_width ) max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe; } if (liquid_autotest_verbose) { // print results printf(" desired resampling rate : %12.8f\n", r); printf(" measured resampling rate : %12.8f (%u/%u)\n", r_actual, ny, nx); printf(" peak spectrum : %12.8f dB (expected 0.0 dB)\n", Ypeak); printf(" peak frequency : %12.8f (expected %-12.8f)\n", fpeak, fy); printf(" max sidelobe : %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As); } CONTEND_DELTA( r_actual, r, 0.01f ); // check actual output sample rate CONTEND_DELTA( Ypeak, 0.0f, 0.25f ); // peak should be about 0 dB CONTEND_DELTA( fpeak, fy, 0.01f ); // peak frequency should be nearly 0.2 CONTEND_LESS_THAN( max_sidelobe, -As ); // maximum side-lobe should be sufficiently low #if 0 // export results for debugging char filename[] = "msresamp_crcf_autotest.m"; FILE*fid = fopen(filename,"w"); fprintf(fid,"%% %s: auto-generated file\n",filename); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"r = %12.8f;\n", r); fprintf(fid,"nx = %u;\n", nx); fprintf(fid,"ny = %u;\n", ny); fprintf(fid,"nfft = %u;\n", nfft); fprintf(fid,"Y = zeros(1,nfft);\n"); for (i=0; i<nfft; i++) fprintf(fid,"Y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(Yfft[i]), cimagf(Yfft[i])); fprintf(fid,"\n\n"); fprintf(fid,"%% plot frequency-domain result\n"); fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(f,20*log10(abs(Y)),'Color',[0.25 0.5 0.0],'LineWidth',2);\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"xlabel('normalized frequency');\n"); fprintf(fid,"ylabel('PSD [dB]');\n"); fprintf(fid,"axis([-0.5 0.5 -120 20]);\n"); fclose(fid); printf("results written to %s\n",filename); #endif }
int main(int argc, char*argv[]) { // options float r = 1.1f; // resampling rate (output/input) unsigned int m = 13; // resampling filter semi-length (filter delay) float As = 60.0f; // resampling filter stop-band attenuation [dB] float bw = 0.45f; // resampling filter bandwidth unsigned int npfb = 64; // number of filters in bank (timing resolution) unsigned int n = 400; // number of input samples float fc = 0.044f; // complex sinusoid frequency int dopt; while ((dopt = getopt(argc,argv,"hr:m:b:s:p:n:f:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'r': r = atof(optarg); break; case 'm': m = atoi(optarg); break; case 'b': bw = atof(optarg); break; case 's': As = atof(optarg); break; case 'p': npfb = atoi(optarg); break; case 'n': n = atoi(optarg); break; case 'f': fc = atof(optarg); break; default: exit(1); } } // validate input if (r <= 0.0f) { fprintf(stderr,"error: %s, resampling rate must be greater than zero\n", argv[0]); exit(1); } else if (m == 0) { fprintf(stderr,"error: %s, filter semi-length must be greater than zero\n", argv[0]); exit(1); } else if (bw == 0.0f || bw >= 0.5f) { fprintf(stderr,"error: %s, filter bandwidth must be in (0,0.5)\n", argv[0]); exit(1); } else if (As < 0.0f) { fprintf(stderr,"error: %s, filter stop-band attenuation must be greater than zero\n", argv[0]); exit(1); } else if (npfb == 0) { fprintf(stderr,"error: %s, filter bank size must be greater than zero\n", argv[0]); exit(1); } else if (n == 0) { fprintf(stderr,"error: %s, number of input samples must be greater than zero\n", argv[0]); exit(1); } unsigned int i; // number of input samples (zero-padded) unsigned int nx = n + m; // output buffer with extra padding for good measure unsigned int y_len = (unsigned int) ceilf(1.1 * nx * r) + 4; // arrays float complex x[nx]; float complex y[y_len]; // create resampler resamp_crcf q = resamp_crcf_create(r,m,bw,As,npfb); // generate input signal float wsum = 0.0f; for (i=0; i<nx; i++) { // compute window float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f; // apply window to complex sinusoid x[i] = cexpf(_Complex_I*2*M_PI*fc*i) * w; // accumulate window wsum += w; } // resample unsigned int ny=0; #if 0 // execute one sample at a time unsigned int nw; for (i=0; i<nx; i++) { // execute resampler, storing in output buffer resamp_crcf_execute(q, x[i], &y[ny], &nw); // increment output size ny += nw; } #else // execute on block of samples resamp_crcf_execute_block(q, x, nx, y, &ny); #endif // clean up allocated objects resamp_crcf_destroy(q); // // analyze resulting signal // // check that the actual resampling rate is close to the target float r_actual = (float)ny / (float)nx; float fy = fc / r; // expected output frequency // run FFT and ensure that carrier has moved and that image // frequencies and distortion have been adequately suppressed unsigned int nfft = 1 << liquid_nextpow2(ny); float complex yfft[nfft]; // fft input float complex Yfft[nfft]; // fft output for (i=0; i<nfft; i++) yfft[i] = i < ny ? y[i] : 0.0f; fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0); fft_shift(Yfft, nfft); // run FFT shift // find peak frequency float Ypeak = 0.0f; float fpeak = 0.0f; float max_sidelobe = -1e9f; // maximum side-lobe [dB] float main_lobe_width = 0.07f; // TODO: figure this out from Kaiser's equations for (i=0; i<nfft; i++) { // normalized output frequency float f = (float)i/(float)nfft - 0.5f; // scale FFT output appropriately float Ymag = 20*log10f( cabsf(Yfft[i] / (r * wsum)) ); // find frequency location of maximum magnitude if (Ymag > Ypeak || i==0) { Ypeak = Ymag; fpeak = f; } // find peak side-lobe value, ignoring frequencies // within a certain range of signal frequency if ( fabsf(f-fy) > main_lobe_width ) max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe; } // print results and check frequency location printf(" desired resampling rate : %12.8f\n", r); printf(" measured resampling rate : %12.8f (%u/%u)\n", r_actual, ny, nx); printf(" peak spectrum : %12.8f dB (expected 0.0 dB)\n", Ypeak); printf(" peak frequency : %12.8f (expected %-12.8f)\n", fpeak, fy); printf(" max sidelobe : %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As); // // export results // FILE * fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s: auto-generated file\n",OUTPUT_FILENAME); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"m=%u;\n", m); fprintf(fid,"npfb=%u;\n", npfb); fprintf(fid,"r=%12.8f;\n", r); fprintf(fid,"nx = %u;\n", nx); fprintf(fid,"x = zeros(1,nx);\n"); for (i=0; i<nx; i++) fprintf(fid,"x(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i])); fprintf(fid,"ny = %u;\n", ny); fprintf(fid,"y = zeros(1,ny);\n"); for (i=0; i<ny; i++) fprintf(fid,"y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i])); fprintf(fid,"\n\n"); fprintf(fid,"%% plot frequency-domain result\n"); fprintf(fid,"nfft=2^nextpow2(max(nx,ny));\n"); fprintf(fid,"%% estimate PSD, normalize by array length\n"); fprintf(fid,"X=20*log10(abs(fftshift(fft(x,nfft)/length(x))));\n"); fprintf(fid,"Y=20*log10(abs(fftshift(fft(y,nfft)/length(y))));\n"); fprintf(fid,"G=max(X);\n"); fprintf(fid,"X=X-G;\n"); fprintf(fid,"Y=Y-G;\n"); fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"if r>1, fx = f/r; fy = f; %% interpolated\n"); fprintf(fid,"else, fx = f; fy = f*r; %% decimated\n"); fprintf(fid,"end;\n"); fprintf(fid,"plot(fx,X,'Color',[0.5 0.5 0.5],fy,Y,'LineWidth',2);\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"xlabel('normalized frequency');\n"); fprintf(fid,"ylabel('PSD [dB]');\n"); fprintf(fid,"legend('original','resampled','location','northeast');"); fprintf(fid,"axis([-0.5 0.5 -120 20]);\n"); fprintf(fid,"\n\n"); fprintf(fid,"%% plot time-domain result\n"); fprintf(fid,"tx=[0:(length(x)-1)];\n"); fprintf(fid,"ty=[0:(length(y)-1)]/r-m;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(2,1,1);\n"); fprintf(fid," plot(tx,real(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n"); fprintf(fid," ty,real(y),'-s','Color',[0.5 0 0], 'MarkerSize',1);\n"); fprintf(fid," legend('original','resampled','location','northeast');"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('real');\n"); fprintf(fid,"subplot(2,1,2);\n"); fprintf(fid," plot(tx,imag(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n"); fprintf(fid," ty,imag(y),'-s','Color',[0 0.5 0], 'MarkerSize',1);\n"); fprintf(fid," legend('original','resampled','location','northeast');"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('imag');\n"); fclose(fid); printf("results written to %s\n",OUTPUT_FILENAME); printf("done.\n"); return 0; }
// // AUTOTEST: nextpow2 // void autotest_nextpow2() { CONTEND_EQUALITY(liquid_nextpow2(1), 0); CONTEND_EQUALITY(liquid_nextpow2(2), 1); CONTEND_EQUALITY(liquid_nextpow2(3), 2); CONTEND_EQUALITY(liquid_nextpow2(4), 2); CONTEND_EQUALITY(liquid_nextpow2(5), 3); CONTEND_EQUALITY(liquid_nextpow2(6), 3); CONTEND_EQUALITY(liquid_nextpow2(7), 3); CONTEND_EQUALITY(liquid_nextpow2(8), 3); CONTEND_EQUALITY(liquid_nextpow2(9), 4); CONTEND_EQUALITY(liquid_nextpow2(10), 4); CONTEND_EQUALITY(liquid_nextpow2(11), 4); CONTEND_EQUALITY(liquid_nextpow2(12), 4); CONTEND_EQUALITY(liquid_nextpow2(13), 4); CONTEND_EQUALITY(liquid_nextpow2(14), 4); CONTEND_EQUALITY(liquid_nextpow2(15), 4); CONTEND_EQUALITY(liquid_nextpow2(67), 7); CONTEND_EQUALITY(liquid_nextpow2(179), 8); CONTEND_EQUALITY(liquid_nextpow2(888), 10); }
RESAMP() RESAMP(_create)(float _r, unsigned int _h_len, float _fc, float _As, unsigned int _npfb) { // validate input if (_r <= 0) { fprintf(stderr,"error: resamp_%s_create(), resampling rate must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_h_len == 0) { fprintf(stderr,"error: resamp_%s_create(), filter length must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_npfb == 0) { fprintf(stderr,"error: resamp_%s_create(), number of filter banks must be greater than zero\n", EXTENSION_FULL); exit(1); } else if (_fc <= 0.0f || _fc >= 0.5f) { fprintf(stderr,"error: resamp_%s_create(), filter cutoff must be in (0,0.5)\n", EXTENSION_FULL); exit(1); } else if (_As <= 0.0f) { fprintf(stderr,"error: resamp_%s_create(), filter stop-band suppression must be greater than zero\n", EXTENSION_FULL); exit(1); } RESAMP() q = (RESAMP()) malloc(sizeof(struct RESAMP(_s))); q->r = _r; q->As = _As; q->fc = _fc; q->h_len = _h_len; q->b = 0; q->del = 1.0f / q->r; #if RESAMP_USE_FIXED_POINT_PHASE q->num_bits_npfb = liquid_nextpow2(_npfb); q->npfb = 1<<q->num_bits_npfb; q->num_bits_phase = 20; q->max_phase = 1 << q->num_bits_phase; q->num_shift_bits = q->num_bits_phase - q->num_bits_npfb; q->theta = 0; q->d_theta = (unsigned int)( q->max_phase * q->del ); #else q->npfb = _npfb; q->tau = 0.0f; q->bf = 0.0f; #endif // design filter unsigned int n = 2*_h_len*q->npfb+1; float hf[n]; TC h[n]; liquid_firdes_kaiser(n,q->fc/((float)(q->npfb)),q->As,0.0f,hf); // normalize filter coefficients by DC gain unsigned int i; float gain=0.0f; for (i=0; i<n; i++) gain += hf[i]; gain = (q->npfb)/(gain); // copy to type-specific array for (i=0; i<n; i++) h[i] = hf[i]*gain; q->f = FIRPFB(_create)(q->npfb,h,n-1); //for (i=0; i<n; i++) // PRINTVAL_TC(h[i],%12.8f); //exit(0); return q; }
// autotest helper function // _n : sequence length // _dt : fractional sample offset // _dphi : carrier frequency offset void detector_cccf_runtest(unsigned int _n, float _dt, float _dphi) { // TODO: validate input unsigned int i; // fixed values float noise_floor = -80.0f; // noise floor [dB] float SNRdB = 30.0f; // signal-to-noise ratio [dB] unsigned int m = 11; // resampling filter semi-length float threshold = 0.3f; // detection threshold // derived values unsigned int num_samples = _n + 2*m + 1; float nstd = powf(10.0f, noise_floor/20.0f); float gamma = powf(10.0f, (SNRdB + noise_floor)/20.0f); float delay = (float)(_n + m) + _dt; // expected delay // arrays float complex s[_n]; // synchronization pattern (samples) float complex x[num_samples]; // resampled signal with noise and offsets // generate synchronization pattern (two samples per symbol) unsigned int n2 = (_n - (_n%2)) / 2; // n2 = floor(n/2) unsigned int mm = liquid_nextpow2(n2); // mm = ceil( log2(n2) ) msequence ms = msequence_create_default(mm); float complex v = 0.0f; for (i=0; i<_n; i++) { if ( (i%2)==0 ) v = msequence_advance(ms) ? 1.0f : -1.0f; s[i] = v; } msequence_destroy(ms); // create fractional sample interpolator firfilt_crcf finterp = firfilt_crcf_create_kaiser(2*m+1, 0.45f, 40.0f, _dt); // generate sequence for (i=0; i<num_samples; i++) { // add fractional sample timing offset if (i < _n) firfilt_crcf_push(finterp, s[i]); else firfilt_crcf_push(finterp, 0.0f); // compute output firfilt_crcf_execute(finterp, &x[i]); // add channel gain x[i] *= gamma; // add carrier offset x[i] *= cexpf(_Complex_I*_dphi*i); // add noise x[i] += nstd * ( randnf() + _Complex_I*randnf() ) * M_SQRT1_2; } // destroy fractional sample interpolator firfilt_crcf_destroy(finterp); // create detector detector_cccf sync = detector_cccf_create(s, _n, threshold, 2*_dphi); // push signal through detector float tau_hat = 0.0f; // fractional sample offset estimate float dphi_hat = 0.0f; // carrier offset estimate float gamma_hat = 1.0f; // signal level estimate (linear) float delay_hat = 0.0f; // total delay offset estimate int signal_detected = 0; // signal detected flag for (i=0; i<num_samples; i++) { // correlate int detected = detector_cccf_correlate(sync, x[i], &tau_hat, &dphi_hat, &gamma_hat); if (detected) { signal_detected = 1; delay_hat = (float)i + (float)tau_hat; if (liquid_autotest_verbose) { printf("****** preamble found, tau_hat=%8.6f, dphi_hat=%8.6f, gamma_hat=%8.6f\n", tau_hat, dphi_hat, gamma_hat); } } } // destroy objects detector_cccf_destroy(sync); // // run tests // // convert to dB gamma = 20*log10f(gamma); gamma_hat = 20*log10f(gamma_hat); if (liquid_autotest_verbose) { printf("detector autotest [%3u]: signal detected? %s\n", _n, signal_detected ? "yes" : "no"); printf(" dphi : estimate = %12.6f (expected %12.6f)\n", dphi_hat, _dphi); printf(" delay : estimate = %12.6f (expected %12.6f)\n", delay_hat, delay); printf(" gamma : estimate = %12.6f (expected %12.6f)\n", gamma_hat, gamma); } // ensure signal was detected CONTEND_EXPRESSION( signal_detected ); // check carrier offset estimate CONTEND_DELTA( dphi_hat, _dphi, 0.01f ); // check delay estimate CONTEND_DELTA( delay_hat, delay, 0.2f ); // check signal level estimate CONTEND_DELTA( gamma_hat, gamma, 2.0f ); }