// Helper function to keep code base small void firfilt_crcf_bench(struct rusage *_start, struct rusage *_finish, unsigned long int *_num_iterations, unsigned int _n) { // adjust number of iterations: // cycles/trial ~ 107 + 4.3*_n *_num_iterations *= 1000; *_num_iterations /= (unsigned int)(107+4.3*_n); // generate coefficients float h[_n]; unsigned long int i; for (i=0; i<_n; i++) h[i] = randnf(); // create filter object firfilt_crcf f = firfilt_crcf_create(h,_n); // generate input vector float complex x[4]; for (i=0; i<4; i++) x[i] = randnf() + _Complex_I*randnf(); // output vector float complex y[4]; // start trials getrusage(RUSAGE_SELF, _start); for (i=0; i<(*_num_iterations); i++) { firfilt_crcf_push(f, x[0]); firfilt_crcf_execute(f, &y[0]); firfilt_crcf_push(f, x[1]); firfilt_crcf_execute(f, &y[1]); firfilt_crcf_push(f, x[2]); firfilt_crcf_execute(f, &y[2]); firfilt_crcf_push(f, x[3]); firfilt_crcf_execute(f, &y[3]); } getrusage(RUSAGE_SELF, _finish); *_num_iterations *= 4; firfilt_crcf_destroy(f); }
// demodulate array of samples (coherent) void cpfskdem_demodulate_coherent(cpfskdem _q, float complex _y, unsigned int * _s, unsigned int * _nw) { // clear output counter *_nw = 0; // push input sample through filter firfilt_crcf_push(_q->data.coherent.mf, _y); #if DEBUG_CPFSKDEM // compute output sample float complex zp; firfilt_crcf_execute(_q->data.coherent.mf, &zp); printf("y(end+1) = %12.8f + 1i*%12.8f;\n", crealf(_y), cimagf(_y)); printf("z(end+1) = %12.8f + 1i*%12.8f;\n", crealf(zp), cimagf(zp)); #endif // decimate output _q->counter++; if ( (_q->counter % _q->k)==0 ) { // reset sample counter _q->counter = 0; // compute output sample float complex z; firfilt_crcf_execute(_q->data.coherent.mf, &z); // compute instantaneous frequency scaled by modulation index // TODO: pre-compute scaling factor float phi_hat = cargf(conjf(_q->z_prime) * z) / (_q->h * M_PI); // estimate transmitted symbol float v = (phi_hat + (_q->M-1.0))*0.5f; unsigned int sym_out = ((int) roundf(v)) % _q->M; // save current point _q->z_prime = z; #if 1 // print result to screen printf(" %3u : %12.8f + j%12.8f, <f=%8.4f : %8.4f> (%1u)\n", _q->index++, crealf(z), cimagf(z), phi_hat, v, sym_out); #endif // save output *_s = sym_out; *_nw = 1; } }
// demodulate array of samples (coherent) unsigned int cpfskdem_demodulate_coherent(cpfskdem _q, float complex * _y) { unsigned int i; unsigned int sym_out = 0; for (i=0; i<_q->k; i++) { // push input sample through filter firfilt_crcf_push(_q->data.coherent.mf, _y[i]); #if DEBUG_CPFSKDEM // compute output sample float complex zp; firfilt_crcf_execute(_q->data.coherent.mf, &zp); printf("y(end+1) = %12.8f + 1i*%12.8f;\n", crealf(_y), cimagf(_y)); printf("z(end+1) = %12.8f + 1i*%12.8f;\n", crealf(zp), cimagf(zp)); #endif // decimate output if ( i == 0 ) { // compute output sample float complex z; firfilt_crcf_execute(_q->data.coherent.mf, &z); // compute instantaneous frequency scaled by modulation index // TODO: pre-compute scaling factor float phi_hat = cargf(conjf(_q->z_prime) * z) / (_q->h * M_PI); // estimate transmitted symbol float v = (phi_hat + (_q->M-1.0))*0.5f; sym_out = ((int) roundf(v)) % _q->M; // save current point _q->z_prime = z; #if DEBUG_CPFSKDEM // print result to screen printf(" %3u : %12.8f + j%12.8f, <f=%8.4f : %8.4f> (%1u)\n", _q->index++, crealf(z), cimagf(z), phi_hat, v, sym_out); #endif } } return sym_out; }
int main(int argc, char*argv[]) { // options unsigned int bps = 1; // number of bits/symbol float h = 0.5f; // modulation index (h=1/2 for MSK) unsigned int k = 4; // filter samples/symbol unsigned int m = 3; // filter delay (symbols) float beta = 0.35f; // GMSK bandwidth-time factor unsigned int num_symbols = 20; // number of data symbols float SNRdB = 40.0f; // signal-to-noise ratio [dB] float cfo = 0.0f; // carrier frequency offset float cpo = 0.0f; // carrier phase offset float tau = 0.0f; // fractional symbol timing offset int filter_type = LIQUID_CPFSK_SQUARE; int dopt; while ((dopt = getopt(argc,argv,"ht:p:H:k:m:b:n:s:F:P:T:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 't': if (strcmp(optarg,"square")==0) { filter_type = LIQUID_CPFSK_SQUARE; } else if (strcmp(optarg,"rcos-full")==0) { filter_type = LIQUID_CPFSK_RCOS_FULL; } else if (strcmp(optarg,"rcos-half")==0) { filter_type = LIQUID_CPFSK_RCOS_PARTIAL; } else if (strcmp(optarg,"gmsk")==0) { filter_type = LIQUID_CPFSK_GMSK; } else { fprintf(stderr,"error: %s, unknown filter type '%s'\n", argv[0], optarg); exit(1); } break; case 'p': bps = atoi(optarg); break; case 'H': h = atof(optarg); break; case 'k': k = atoi(optarg); break; case 'm': m = atoi(optarg); break; case 'b': beta = atof(optarg); break; case 'n': num_symbols = atoi(optarg); break; case 's': SNRdB = atof(optarg); break; case 'F': cfo = atof(optarg); break; case 'P': cpo = atof(optarg); break; case 'T': tau = atof(optarg); break; default: exit(1); } } unsigned int i; // derived values unsigned int num_samples = k*num_symbols; unsigned int M = 1 << bps; // constellation size float nstd = powf(10.0f, -SNRdB/20.0f); // arrays unsigned int sym_in[num_symbols]; // input symbols float complex x[num_samples]; // transmitted signal float complex y[num_samples]; // received signal unsigned int sym_out[num_symbols]; // output symbols // create modem objects cpfskmod mod = cpfskmod_create(bps, h, k, m, beta, filter_type); cpfskdem dem = cpfskdem_create(bps, h, k, m, beta, filter_type); // print modulator cpfskmod_print(mod); // generate message signal for (i=0; i<num_symbols; i++) sym_in[i] = rand() % M; // modulate signal for (i=0; i<num_symbols; i++) cpfskmod_modulate(mod, sym_in[i], &x[k*i]); // push through channel float sample_offset = -tau * k; int sample_delay = (int)roundf(sample_offset); float dt = sample_offset - (float)sample_delay; printf("symbol delay : %f\n", tau); printf("sample delay : %f = %d + %f\n", sample_offset, sample_delay, dt); firfilt_crcf fchannel = firfilt_crcf_create_kaiser(8*k+2*sample_delay+1, 0.45f, 40.0f, dt); for (i=0; i<num_samples; i++) { // push through channel delay firfilt_crcf_push(fchannel, x[i]); firfilt_crcf_execute(fchannel, &y[i]); // add carrier frequency/phase offset y[i] *= cexpf(_Complex_I*(cfo*i + cpo)); // add noise y[i] += nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2; } firfilt_crcf_destroy(fchannel); // demodulate signal unsigned int nw=0; cpfskdem_demodulate(dem, y, num_samples, sym_out, &nw); printf("demodulator wrote %u symbols\n", nw); // destroy modem objects cpfskmod_destroy(mod); cpfskdem_destroy(dem); // compute power spectral density of transmitted signal unsigned int nfft = 1024; float psd[nfft]; spgramcf periodogram = spgramcf_create_kaiser(nfft, nfft/2, 8.0f); spgramcf_estimate_psd(periodogram, x, num_samples, psd); spgramcf_destroy(periodogram); // // 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,"k = %u;\n", k); fprintf(fid,"h = %f;\n", h); fprintf(fid,"num_symbols = %u;\n", num_symbols); fprintf(fid,"num_samples = %u;\n", num_samples); fprintf(fid,"nfft = %u;\n", nfft); fprintf(fid,"delay = %u; %% receive filter delay\n", m); fprintf(fid,"x = zeros(1,num_samples);\n"); fprintf(fid,"y = zeros(1,num_samples);\n"); for (i=0; i<num_samples; i++) { fprintf(fid,"x(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(x[i]), cimagf(x[i])); fprintf(fid,"y(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(y[i]), cimagf(y[i])); } // save power spectral density fprintf(fid,"psd = zeros(1,nfft);\n"); for (i=0; i<nfft; i++) fprintf(fid,"psd(%4u) = %12.8f;\n", i+1, psd[i]); fprintf(fid,"t=[0:(num_samples-1)]/k;\n"); fprintf(fid,"i = 1:k:num_samples;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(3,4,1:3);\n"); fprintf(fid," plot(t,real(x),'-', t(i),real(x(i)),'ob',...\n"); fprintf(fid," t,imag(x),'-', t(i),imag(x(i)),'og');\n"); fprintf(fid," axis([0 num_symbols -1.2 1.2]);\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('x(t)');\n"); fprintf(fid," grid on;\n"); fprintf(fid,"subplot(3,4,5:7);\n"); fprintf(fid," plot(t,real(y),'-', t(i),real(y(i)),'ob',...\n"); fprintf(fid," t,imag(y),'-', t(i),imag(y(i)),'og');\n"); fprintf(fid," axis([0 num_symbols -1.2 1.2]);\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('y(t)');\n"); fprintf(fid," grid on;\n"); // plot I/Q constellations fprintf(fid,"subplot(3,4,4);\n"); fprintf(fid," plot(real(x),imag(x),'-',real(x(i)),imag(x(i)),'rs','MarkerSize',4);\n"); fprintf(fid," xlabel('I');\n"); fprintf(fid," ylabel('Q');\n"); fprintf(fid," axis([-1 1 -1 1]*1.2);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); fprintf(fid,"subplot(3,4,8);\n"); fprintf(fid," plot(real(y),imag(y),'-',real(y(i)),imag(y(i)),'rs','MarkerSize',4);\n"); fprintf(fid," xlabel('I');\n"); fprintf(fid," ylabel('Q');\n"); fprintf(fid," axis([-1 1 -1 1]*1.2);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); // plot PSD fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"subplot(3,4,9:12);\n"); fprintf(fid," plot(f,psd,'LineWidth',1.5);\n"); fprintf(fid," axis([-0.5 0.5 -60 20]);\n"); fprintf(fid," xlabel('Normalized Frequency [f/F_s]');\n"); fprintf(fid," ylabel('PSD [dB]');\n"); fprintf(fid," grid on;\n"); fclose(fid); printf("results written to '%s'\n", OUTPUT_FILENAME); return 0; }
int main() { // options unsigned int num_channels=4; // number of channels unsigned int p=3; // filter length (symbols) unsigned int num_symbols=6; // number of symbols // derived values unsigned int num_samples = num_channels * num_symbols; unsigned int i; unsigned int j; // generate synthesis filter // NOTE : these coefficients can be random; the purpose of this // exercise is to demonstrate mathematical equivalence unsigned int h_len = p*num_channels; float h[h_len]; for (i=0; i<h_len; i++) h[i] = randnf(); // generate analysis filter unsigned int g_len = p*num_channels; float g[g_len]; for (i=0; i<g_len; i++) g[i] = h[g_len-i-1]; // create synthesis/analysis filter objects firfilt_crcf fs = firfilt_crcf_create(h, h_len); firfilt_crcf fa = firfilt_crcf_create(g, g_len); // create synthesis/analysis filterbank channelizer objects firpfbch_crcf qs = firpfbch_crcf_create(LIQUID_SYNTHESIZER, num_channels, p, h); firpfbch_crcf qa = firpfbch_crcf_create(LIQUID_ANALYZER, num_channels, p, g); float complex x[num_samples]; // random input (noise) float complex Y0[num_symbols][num_channels]; // channelized output (filterbank) float complex Y1[num_symbols][num_channels]; // channelized output float complex z0[num_samples]; // time-domain output (filterbank) float complex z1[num_samples]; // time-domain output // generate input sequence (complex noise) for (i=0; i<num_samples; i++) x[i] = randnf() * cexpf(_Complex_I*randf()*2*M_PI); // // ANALYZERS // // // run analysis filter bank // for (i=0; i<num_symbols; i++) firpfbch_crcf_analyzer_execute(qa, &x[i*num_channels], &Y0[i][0]); // // run traditional down-converter (inefficient) // float dphi; // carrier frequency unsigned int n=0; for (i=0; i<num_channels; i++) { // reset filter firfilt_crcf_clear(fa); // set center frequency dphi = 2.0f * M_PI * (float)i / (float)num_channels; // reset symbol counter n=0; for (j=0; j<num_samples; j++) { // push down-converted sample into filter firfilt_crcf_push(fa, x[j]*cexpf(-_Complex_I*j*dphi)); // compute output at the appropriate sample time assert(n<num_symbols); if ( ((j+1)%num_channels)==0 ) { firfilt_crcf_execute(fa, &Y1[n][i]); n++; } } assert(n==num_symbols); } // // SYNTHESIZERS // // // run synthesis filter bank // for (i=0; i<num_symbols; i++) firpfbch_crcf_synthesizer_execute(qs, &Y0[i][0], &z0[i*num_channels]); // // run traditional up-converter (inefficient) // // clear output array for (i=0; i<num_samples; i++) z1[i] = 0.0f; float complex y_hat; for (i=0; i<num_channels; i++) { // reset filter firfilt_crcf_clear(fs); // set center frequency dphi = 2.0f * M_PI * (float)i / (float)num_channels; // reset input symbol counter n=0; for (j=0; j<num_samples; j++) { // interpolate sequence if ( (j%num_channels)==0 ) { assert(n<num_symbols); firfilt_crcf_push(fs, Y1[n][i]); n++; } else { firfilt_crcf_push(fs, 0); } firfilt_crcf_execute(fs, &y_hat); // accumulate up-converted sample z1[j] += y_hat * cexpf(_Complex_I*j*dphi); } assert(n==num_symbols); } // destroy objects firfilt_crcf_destroy(fs); firfilt_crcf_destroy(fa); firpfbch_crcf_destroy(qs); firpfbch_crcf_destroy(qa); // // RESULTS // // // analyzers // // print filterbank channelizer printf("\n"); printf("filterbank channelizer:\n"); for (i=0; i<num_symbols; i++) { printf("%3u: ", i); for (j=0; j<num_channels; j++) { printf(" %8.5f+j%8.5f, ", crealf(Y0[i][j]), cimagf(Y0[i][j])); } printf("\n"); } // print traditional channelizer printf("\n"); printf("traditional channelizer:\n"); for (i=0; i<num_symbols; i++) { printf("%3u: ", i); for (j=0; j<num_channels; j++) { printf(" %8.5f+j%8.5f, ", crealf(Y1[i][j]), cimagf(Y1[i][j])); } printf("\n"); } float mse_analyzer[num_channels]; float complex d; for (i=0; i<num_channels; i++) { mse_analyzer[i] = 0.0f; for (j=0; j<num_symbols; j++) { d = Y0[j][i] - Y1[j][i]; mse_analyzer[i] += crealf(d*conjf(d)); } mse_analyzer[i] /= num_symbols; } printf("\n"); printf("rmse: "); for (i=0; i<num_channels; i++) printf("%12.4e ", sqrt(mse_analyzer[i])); printf("\n"); // // synthesizers // printf("\n"); printf("output: filterbank: traditional:\n"); for (i=0; i<num_samples; i++) { printf("%3u: %10.5f+%10.5fj %10.5f+%10.5fj\n", i, crealf(z0[i]), cimagf(z0[i]), crealf(z1[i]), cimagf(z1[i])); } float mse_synthesizer = 0.0f; for (i=0; i<num_samples; i++) { d = z0[i] - z1[i]; mse_synthesizer += crealf(d*conjf(d)); } mse_synthesizer /= num_samples; printf("\n"); printf("rmse: %12.4e\n", sqrtf(mse_synthesizer)); // // EXPORT DATA TO FILE // FILE * fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s: auto-generated file\n\n", OUTPUT_FILENAME); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"num_channels=%u;\n", num_channels); fprintf(fid,"num_symbols=%u;\n", num_symbols); fprintf(fid,"num_samples = num_channels*num_symbols;\n"); fprintf(fid,"x = zeros(1,num_samples);\n"); fprintf(fid,"y0 = zeros(num_symbols,num_channels);\n"); fprintf(fid,"y1 = zeros(num_symbols,num_channels);\n"); fprintf(fid,"z0 = zeros(1,num_samples);\n"); fprintf(fid,"z1 = zeros(1,num_samples);\n"); // input for (i=0; i<num_samples; i++) fprintf(fid,"x(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimag(x[i])); // analysis for (i=0; i<num_symbols; i++) { for (j=0; j<num_channels; j++) { fprintf(fid,"y0(%4u,%4u) = %12.4e + j*%12.4e;\n", i+1, j+1, crealf(Y0[i][j]), cimag(Y0[i][j])); fprintf(fid,"y1(%4u,%4u) = %12.4e + j*%12.4e;\n", i+1, j+1, crealf(Y1[i][j]), cimag(Y1[i][j])); } } // synthesis for (i=0; i<num_samples; i++) { fprintf(fid,"z0(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(z0[i]), cimag(z0[i])); fprintf(fid,"z1(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(z1[i]), cimag(z1[i])); } fprintf(fid,"z0 = z0 / num_channels;\n"); fprintf(fid,"z1 = z1 / num_channels;\n"); // plot results fprintf(fid,"\n\n"); fprintf(fid,"ts = 0:(num_symbols-1);\n"); fprintf(fid,"for i=1:num_channels,\n"); fprintf(fid,"figure;\n"); fprintf(fid,"title(['channel ' num2str(i)]);\n"); fprintf(fid,"subplot(2,1,1);\n"); fprintf(fid," plot(ts,real(y0(:,i)),'-x', ts,real(y1(:,i)),'s');\n"); //fprintf(fid," axis([0 (num_symbols-1) -2 2]);\n"); fprintf(fid,"subplot(2,1,2);\n"); fprintf(fid," plot(ts,imag(y0(:,i)),'-x', ts,imag(y1(:,i)),'s');\n"); //fprintf(fid," axis([0 (num_symbols-1) -2 2]);\n"); fprintf(fid,"end;\n"); fprintf(fid,"t = 0:(num_samples-1);\n"); fprintf(fid,"figure;\n"); fprintf(fid,"title('composite');\n"); fprintf(fid,"subplot(2,1,1);\n"); fprintf(fid," plot(t,real(z0),'-x', t,real(z1),'s');\n"); fprintf(fid,"subplot(2,1,2);\n"); fprintf(fid," plot(t,imag(z0),'-x', t,imag(z1),'s');\n"); fclose(fid); printf("results written to '%s'\n", OUTPUT_FILENAME); printf("done.\n"); return 0; }
int main() { // options unsigned int num_channels=4; // number of channels unsigned int m=5; // filter delay unsigned int num_symbols=12; // number of symbols // derived values unsigned int num_samples = num_channels * num_symbols; unsigned int i; unsigned int j; // generate filter // NOTE : these coefficients can be random; the purpose of this // exercise is to demonstrate mathematical equivalence unsigned int h_len = 2*m*num_channels; float h[h_len]; for (i=0; i<h_len; i++) h[i] = randnf(); //for (i=0; i<h_len; i++) h[i] = 0.1f*i; //for (i=0; i<h_len; i++) h[i] = (i<=m) ? 1.0f : 0.0f; //for (i=0; i<h_len; i++) h[i] = 1.0f; // create filterbank manually dotprod_crcf dp[num_channels]; // vector dot products windowcf w[num_channels]; // window buffers #if DEBUG // print coefficients printf("h_prototype:\n"); for (i=0; i<h_len; i++) printf(" h[%3u] = %12.8f\n", i, h[i]); #endif // create objects unsigned int h_sub_len = 2*m; float h_sub[h_sub_len]; for (i=0; i<num_channels; i++) { // sub-sample prototype filter, loading coefficients in // reverse order #if 0 for (j=0; j<h_sub_len; j++) h_sub[j] = h[j*num_channels+i]; #else for (j=0; j<h_sub_len; j++) h_sub[h_sub_len-j-1] = h[j*num_channels+i]; #endif // create window buffer and dotprod objects dp[i] = dotprod_crcf_create(h_sub, h_sub_len); w[i] = windowcf_create(h_sub_len); #if DEBUG printf("h_sub[%u] : \n", i); for (j=0; j<h_sub_len; j++) printf(" h[%3u] = %12.8f\n", j, h_sub[j]); #endif } // generate DFT object float complex x[num_channels]; // time-domain buffer float complex X[num_channels]; // freq-domain buffer #if 0 fftplan fft = fft_create_plan(num_channels, X, x, LIQUID_FFT_BACKWARD, 0); #else fftplan fft = fft_create_plan(num_channels, X, x, LIQUID_FFT_FORWARD, 0); #endif // generate filter object firfilt_crcf f = firfilt_crcf_create(h, h_len); float complex y[num_samples]; // time-domain input float complex Y0[num_symbols][num_channels]; // channelized output float complex Y1[num_symbols][num_channels]; // channelized output // generate input sequence (complex noise) for (i=0; i<num_samples; i++) y[i] = randnf() * cexpf(_Complex_I*randf()*2*M_PI); // // run analysis filter bank // #if 0 unsigned int filter_index = 0; #else unsigned int filter_index = num_channels-1; #endif float complex y_hat; // input sample float complex * r; // read pointer for (i=0; i<num_symbols; i++) { // load buffers for (j=0; j<num_channels; j++) { // grab sample y_hat = y[i*num_channels + j]; // push sample into buffer at filter index windowcf_push(w[filter_index], y_hat); // decrement filter index filter_index = (filter_index + num_channels - 1) % num_channels; //filter_index = (filter_index + 1) % num_channels; } // execute filter outputs, reversing order of output (not // sure why this is necessary) for (j=0; j<num_channels; j++) { windowcf_read(w[j], &r); dotprod_crcf_execute(dp[j], r, &X[num_channels-j-1]); } // execute DFT, store result in buffer 'x' fft_execute(fft); // move to output array for (j=0; j<num_channels; j++) Y0[i][j] = x[j]; } // // run traditional down-converter (inefficient) // float dphi; // carrier frequency unsigned int n=0; for (i=0; i<num_channels; i++) { // reset filter firfilt_crcf_reset(f); // set center frequency dphi = 2.0f * M_PI * (float)i / (float)num_channels; // reset symbol counter n=0; for (j=0; j<num_samples; j++) { // push down-converted sample into filter firfilt_crcf_push(f, y[j]*cexpf(-_Complex_I*j*dphi)); // compute output at the appropriate sample time assert(n<num_symbols); if ( ((j+1)%num_channels)==0 ) { firfilt_crcf_execute(f, &Y1[n][i]); n++; } } assert(n==num_symbols); } // destroy objects for (i=0; i<num_channels; i++) { dotprod_crcf_destroy(dp[i]); windowcf_destroy(w[i]); } fft_destroy_plan(fft); firfilt_crcf_destroy(f); // print filterbank channelizer printf("\n"); printf("filterbank channelizer:\n"); for (i=0; i<num_symbols; i++) { printf("%3u: ", i); for (j=0; j<num_channels; j++) { printf(" %8.5f+j%8.5f, ", crealf(Y0[i][j]), cimagf(Y0[i][j])); } printf("\n"); } // print traditional channelizer printf("\n"); printf("traditional channelizer:\n"); for (i=0; i<num_symbols; i++) { printf("%3u: ", i); for (j=0; j<num_channels; j++) { printf(" %8.5f+j%8.5f, ", crealf(Y1[i][j]), cimagf(Y1[i][j])); } printf("\n"); } // // compare results // float mse[num_channels]; float complex d; for (i=0; i<num_channels; i++) { mse[i] = 0.0f; for (j=0; j<num_symbols; j++) { d = Y0[j][i] - Y1[j][i]; mse[i] += crealf(d*conjf(d)); } mse[i] /= num_symbols; } printf("\n"); printf("rmse: "); for (i=0; i<num_channels; i++) printf("%12.4e ", sqrt(mse[i])); printf("\n"); printf("done.\n"); return 0; }
int main(int argc, char*argv[]) { // options unsigned int sequence_len = 80; // number of sync symbols unsigned int k = 2; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float gamma = 10.0f; // channel gain float tau = -0.3f; // fractional sample timing offset float dphi = -0.01f; // carrier frequency offset float phi = 0.5f; // carrier phase offset float SNRdB = 20.0f; // signal-to-noise ratio [dB] float threshold = 0.5f; // detection threshold int dopt; while ((dopt = getopt(argc,argv,"hn:k:m:b:F:T:S:t:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'n': sequence_len = atoi(optarg); break; case 'k': k = atoi(optarg); break; case 'm': m = atoi(optarg); break; case 'b': beta = atof(optarg); break; case 'F': dphi = atof(optarg); break; case 'T': tau = atof(optarg); break; case 'S': SNRdB = atof(optarg); break; case 't': threshold = atof(optarg); break; default: exit(1); } } unsigned int i; // validate input if (tau < -0.5f || tau > 0.5f) { fprintf(stderr,"error: %s, fractional sample offset must be in [-0.5,0.5]\n", argv[0]); exit(1); } // generate synchronization sequence (QPSK symbols) float complex sequence[sequence_len]; for (i=0; i<sequence_len; i++) { sequence[i] = (rand() % 2 ? 1.0f : -1.0f) * M_SQRT1_2 + (rand() % 2 ? 1.0f : -1.0f) * M_SQRT1_2 * _Complex_I; } // float tau_hat = 0.0f; float gamma_hat = 0.0f; float dphi_hat = 0.0f; float phi_hat = 0.0f; int frame_detected = 0; // create detector qdetector_cccf q = qdetector_cccf_create_linear(sequence, sequence_len, ftype, k, m, beta); qdetector_cccf_set_threshold(q, threshold); qdetector_cccf_print(q); // unsigned int seq_len = qdetector_cccf_get_seq_len(q); unsigned int buf_len = qdetector_cccf_get_buf_len(q); unsigned int num_samples = 2*buf_len; // double buffer length to ensure detection unsigned int num_symbols = buf_len; // arrays float complex y[num_samples]; // received signal float complex syms_rx[num_symbols]; // recovered symbols // get pointer to sequence and generate full sequence float complex * v = (float complex*) qdetector_cccf_get_sequence(q); unsigned int filter_delay = 15; firfilt_crcf filter = firfilt_crcf_create_kaiser(2*filter_delay+1, 0.4f, 60.0f, -tau); float nstd = 0.1f; for (i=0; i<num_samples; i++) { // add delay firfilt_crcf_push(filter, i < seq_len ? v[i] : 0); firfilt_crcf_execute(filter, &y[i]); // channel gain y[i] *= gamma; // carrier offset y[i] *= cexpf(_Complex_I*(dphi*i + phi)); // noise y[i] += nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2; } firfilt_crcf_destroy(filter); // run detection on sequence for (i=0; i<num_samples; i++) { v = qdetector_cccf_execute(q,y[i]); if (v != NULL) { printf("\nframe detected!\n"); frame_detected = 1; // get statistics tau_hat = qdetector_cccf_get_tau(q); gamma_hat = qdetector_cccf_get_gamma(q); dphi_hat = qdetector_cccf_get_dphi(q); phi_hat = qdetector_cccf_get_phi(q); break; } } unsigned int num_syms_rx = 0; // output symbol counter unsigned int counter = 0; // decimation counter if (frame_detected) { // recover symbols firfilt_crcf mf = firfilt_crcf_create_rnyquist(ftype, k, m, beta, tau_hat); firfilt_crcf_set_scale(mf, 1.0f / (float)(k*gamma_hat)); nco_crcf nco = nco_crcf_create(LIQUID_VCO); nco_crcf_set_frequency(nco, dphi_hat); nco_crcf_set_phase (nco, phi_hat); for (i=0; i<buf_len; i++) { // float complex sample; nco_crcf_mix_down(nco, v[i], &sample); nco_crcf_step(nco); // apply decimator firfilt_crcf_push(mf, sample); counter++; if (counter == k-1) firfilt_crcf_execute(mf, &syms_rx[num_syms_rx++]); counter %= k; } nco_crcf_destroy(nco); firfilt_crcf_destroy(mf); } // destroy objects qdetector_cccf_destroy(q); // print results printf("\n"); printf("frame detected : %s\n", frame_detected ? "yes" : "no"); printf(" gamma hat : %8.3f, actual=%8.3f (error=%8.3f)\n", gamma_hat, gamma, gamma_hat - gamma); printf(" tau hat : %8.3f, actual=%8.3f (error=%8.3f) samples\n", tau_hat, tau, tau_hat - tau ); printf(" dphi hat : %8.5f, actual=%8.5f (error=%8.5f) rad/sample\n", dphi_hat, dphi, dphi_hat - dphi ); printf(" phi hat : %8.5f, actual=%8.5f (error=%8.5f) radians\n", phi_hat, phi, phi_hat - phi ); printf(" symbols rx : %u\n", num_syms_rx); printf("\n"); // // 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,"sequence_len= %u;\n", sequence_len); fprintf(fid,"num_samples = %u;\n", num_samples); fprintf(fid,"y = zeros(1,num_samples);\n"); for (i=0; i<num_samples; i++) fprintf(fid,"y(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(y[i]), cimagf(y[i])); fprintf(fid,"num_syms_rx = %u;\n", num_syms_rx); fprintf(fid,"syms_rx = zeros(1,num_syms_rx);\n"); for (i=0; i<num_syms_rx; i++) fprintf(fid,"syms_rx(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(syms_rx[i]), cimagf(syms_rx[i])); fprintf(fid,"t=[0:(num_samples-1)];\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(4,1,1);\n"); fprintf(fid," plot(t,real(y), t,imag(y));\n"); fprintf(fid," grid on;\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('received signal');\n"); fprintf(fid,"subplot(4,1,2:4);\n"); fprintf(fid," plot(real(syms_rx), imag(syms_rx), 'x');\n"); fprintf(fid," axis([-1 1 -1 1]*1.5);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); fprintf(fid," xlabel('real');\n"); fprintf(fid," ylabel('imag');\n"); fclose(fid); printf("results written to '%s'\n", OUTPUT_FILENAME); return 0; }
int main(int argc, char*argv[]) { // options unsigned int k = 8; // filter samples/symbol unsigned int bps= 1; // number of bits/symbol float h = 0.5f; // modulation index (h=1/2 for MSK) unsigned int num_data_symbols = 20; // number of data symbols float SNRdB = 80.0f; // signal-to-noise ratio [dB] float cfo = 0.0f; // carrier frequency offset float cpo = 0.0f; // carrier phase offset float tau = 0.0f; // fractional symbol offset enum { TXFILT_SQUARE=0, TXFILT_RCOS_FULL, TXFILT_RCOS_HALF, TXFILT_GMSK, } tx_filter_type = TXFILT_SQUARE; float gmsk_bt = 0.35f; // GMSK bandwidth-time factor int dopt; while ((dopt = getopt(argc,argv,"ht:k:b:H:B:n:s:F:P:T:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 't': if (strcmp(optarg,"square")==0) { tx_filter_type = TXFILT_SQUARE; } else if (strcmp(optarg,"rcos-full")==0) { tx_filter_type = TXFILT_RCOS_FULL; } else if (strcmp(optarg,"rcos-half")==0) { tx_filter_type = TXFILT_RCOS_HALF; } else if (strcmp(optarg,"gmsk")==0) { tx_filter_type = TXFILT_GMSK; } else { fprintf(stderr,"error: %s, unknown filter type '%s'\n", argv[0], optarg); exit(1); } break; case 'k': k = atoi(optarg); break; case 'b': bps = atoi(optarg); break; case 'H': h = atof(optarg); break; case 'B': gmsk_bt = atof(optarg); break; case 'n': num_data_symbols = atoi(optarg); break; case 's': SNRdB = atof(optarg); break; case 'F': cfo = atof(optarg); break; case 'P': cpo = atof(optarg); break; case 'T': tau = atof(optarg); break; default: exit(1); } } unsigned int i; // derived values unsigned int num_symbols = num_data_symbols; unsigned int num_samples = k*num_symbols; unsigned int M = 1 << bps; // constellation size float nstd = powf(10.0f, -SNRdB/20.0f); // arrays unsigned char sym_in[num_symbols]; // input symbols float phi[num_samples]; // transmitted phase float complex x[num_samples]; // transmitted signal float complex y[num_samples]; // received signal float complex z[num_samples]; // output... //unsigned char sym_out[num_symbols]; // output symbols unsigned int ht_len = 0; unsigned int tx_delay = 0; float * ht = NULL; switch (tx_filter_type) { case TXFILT_SQUARE: // regular MSK ht_len = k; tx_delay = 1; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = h * M_PI / (float)k; break; case TXFILT_RCOS_FULL: // full-response raised-cosine pulse ht_len = k; tx_delay = 1; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = h * M_PI / (float)k * (1.0f - cosf(2.0f*M_PI*i/(float)ht_len)); break; case TXFILT_RCOS_HALF: // partial-response raised-cosine pulse ht_len = 3*k; tx_delay = 2; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = 0.0f; for (i=0; i<2*k; i++) ht[i+k/2] = h * 0.5f * M_PI / (float)k * (1.0f - cosf(2.0f*M_PI*i/(float)(2*k))); break; case TXFILT_GMSK: ht_len = 2*k*3+1+k; tx_delay = 4; ht = (float*) malloc(ht_len *sizeof(float)); for (i=0; i<ht_len; i++) ht[i] = 0.0f; liquid_firdes_gmsktx(k,3,gmsk_bt,0.0f,&ht[k/2]); for (i=0; i<ht_len; i++) ht[i] *= h * 2.0f / (float)k; break; default: fprintf(stderr,"error: %s, invalid tx filter type\n", argv[0]); exit(1); } for (i=0; i<ht_len; i++) printf("ht(%3u) = %12.8f;\n", i+1, ht[i]); firinterp_rrrf interp_tx = firinterp_rrrf_create(k, ht, ht_len); // generate symbols and interpolate // phase-accumulating filter (trapezoidal integrator) float b[2] = {0.5f, 0.5f}; if (tx_filter_type == TXFILT_SQUARE) { // square filter: rectangular integration with one sample of delay b[0] = 0.0f; b[1] = 1.0f; } float a[2] = {1.0f, -1.0f}; iirfilt_rrrf integrator = iirfilt_rrrf_create(b,2,a,2); float theta = 0.0f; for (i=0; i<num_symbols; i++) { sym_in[i] = rand() % M; float v = 2.0f*sym_in[i] - (float)(M-1); // +/-1, +/-3, ... +/-(M-1) firinterp_rrrf_execute(interp_tx, v, &phi[k*i]); // accumulate phase unsigned int j; for (j=0; j<k; j++) { iirfilt_rrrf_execute(integrator, phi[i*k+j], &theta); x[i*k+j] = cexpf(_Complex_I*theta); } } iirfilt_rrrf_destroy(integrator); // push through channel for (i=0; i<num_samples; i++) { // add carrier frequency/phase offset y[i] = x[i]*cexpf(_Complex_I*(cfo*i + cpo)); // add noise y[i] += nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2; } // create decimator unsigned int m = 3; float bw = 0.0f; float beta = 0.0f; firfilt_crcf decim_rx = NULL; switch (tx_filter_type) { case TXFILT_SQUARE: //bw = 0.9f / (float)k; bw = 0.4f; decim_rx = firfilt_crcf_create_kaiser(2*k*m+1, bw, 60.0f, 0.0f); firfilt_crcf_set_scale(decim_rx, 2.0f * bw); break; case TXFILT_RCOS_FULL: if (M==2) { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k,m,0.5f,0); firfilt_crcf_set_scale(decim_rx, 1.33f / (float)k); } else { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k/2,2*m,0.9f,0); firfilt_crcf_set_scale(decim_rx, 3.25f / (float)k); } break; case TXFILT_RCOS_HALF: if (M==2) { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k,m,0.3f,0); firfilt_crcf_set_scale(decim_rx, 1.10f / (float)k); } else { decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k/2,2*m,0.27f,0); firfilt_crcf_set_scale(decim_rx, 2.90f / (float)k); } break; case TXFILT_GMSK: bw = 0.5f / (float)k; // TODO: figure out beta value here beta = (M == 2) ? 0.8*gmsk_bt : 1.0*gmsk_bt; decim_rx = firfilt_crcf_create_rnyquist(LIQUID_FIRFILT_GMSKRX,k,m,beta,0); firfilt_crcf_set_scale(decim_rx, 2.0f * bw); break; default: fprintf(stderr,"error: %s, invalid tx filter type\n", argv[0]); exit(1); } printf("bw = %f\n", bw); // run receiver unsigned int n=0; unsigned int num_errors = 0; unsigned int num_symbols_checked = 0; float complex z_prime = 0.0f; for (i=0; i<num_samples; i++) { // push through filter firfilt_crcf_push(decim_rx, y[i]); firfilt_crcf_execute(decim_rx, &z[i]); // decimate output if ( (i%k)==0 ) { // compute instantaneous frequency scaled by modulation index float phi_hat = cargf(conjf(z_prime) * z[i]) / (h * M_PI); // estimate transmitted symbol float v = (phi_hat + (M-1.0))*0.5f; unsigned int sym_out = ((int) roundf(v)) % M; // save current point z_prime = z[i]; // print result to screen printf("%3u : %12.8f + j%12.8f, <f=%8.4f : %8.4f> (%1u)", n, crealf(z[i]), cimagf(z[i]), phi_hat, v, sym_out); if (n >= m+tx_delay) { num_errors += (sym_out == sym_in[n-m-tx_delay]) ? 0 : 1; num_symbols_checked++; printf(" (%1u)\n", sym_in[n-m-tx_delay]); } else { printf("\n"); } n++; } } // print number of errors printf("errors : %3u / %3u\n", num_errors, num_symbols_checked); // destroy objects firinterp_rrrf_destroy(interp_tx); firfilt_crcf_destroy(decim_rx); // compute power spectral density of transmitted signal unsigned int nfft = 1024; float psd[nfft]; spgramcf periodogram = spgramcf_create_kaiser(nfft, nfft/2, 8.0f); spgramcf_estimate_psd(periodogram, y, num_samples, psd); spgramcf_destroy(periodogram); // // 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,"k = %u;\n", k); fprintf(fid,"h = %f;\n", h); fprintf(fid,"num_symbols = %u;\n", num_symbols); fprintf(fid,"num_samples = %u;\n", num_samples); fprintf(fid,"nfft = %u;\n", nfft); fprintf(fid,"delay = %u; %% receive filter delay\n", tx_delay); fprintf(fid,"x = zeros(1,num_samples);\n"); fprintf(fid,"y = zeros(1,num_samples);\n"); fprintf(fid,"z = zeros(1,num_samples);\n"); fprintf(fid,"phi = zeros(1,num_samples);\n"); for (i=0; i<num_samples; i++) { fprintf(fid,"x(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(x[i]), cimagf(x[i])); fprintf(fid,"y(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(y[i]), cimagf(y[i])); fprintf(fid,"z(%4u) = %12.8f + j*%12.8f;\n", i+1, crealf(z[i]), cimagf(z[i])); fprintf(fid,"phi(%4u) = %12.8f;\n", i+1, phi[i]); } // save PSD fprintf(fid,"psd = zeros(1,nfft);\n"); for (i=0; i<nfft; i++) fprintf(fid,"psd(%4u) = %12.8f;\n", i+1, psd[i]); fprintf(fid,"t=[0:(num_samples-1)]/k;\n"); fprintf(fid,"i = 1:k:num_samples;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(3,4,1:3);\n"); fprintf(fid," plot(t,real(x),'-', t(i),real(x(i)),'bs','MarkerSize',4,...\n"); fprintf(fid," t,imag(x),'-', t(i),imag(x(i)),'gs','MarkerSize',4);\n"); fprintf(fid," axis([0 num_symbols -1.2 1.2]);\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('x(t)');\n"); fprintf(fid," grid on;\n"); fprintf(fid,"subplot(3,4,5:7);\n"); fprintf(fid," plot(t-delay,real(z),'-', t(i)-delay,real(z(i)),'bs','MarkerSize',4,...\n"); fprintf(fid," t-delay,imag(z),'-', t(i)-delay,imag(z(i)),'gs','MarkerSize',4);\n"); fprintf(fid," axis([0 num_symbols -1.2 1.2]);\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('\"matched\" filter output');\n"); fprintf(fid," grid on;\n"); // plot I/Q constellations fprintf(fid,"subplot(3,4,4);\n"); fprintf(fid," plot(real(y),imag(y),'-',real(y(i)),imag(y(i)),'rs','MarkerSize',3);\n"); fprintf(fid," xlabel('I');\n"); fprintf(fid," ylabel('Q');\n"); fprintf(fid," axis([-1 1 -1 1]*1.2);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); fprintf(fid,"subplot(3,4,8);\n"); fprintf(fid," plot(real(z),imag(z),'-',real(z(i)),imag(z(i)),'rs','MarkerSize',3);\n"); fprintf(fid," xlabel('I');\n"); fprintf(fid," ylabel('Q');\n"); fprintf(fid," axis([-1 1 -1 1]*1.2);\n"); fprintf(fid," axis square;\n"); fprintf(fid," grid on;\n"); // plot PSD fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"subplot(3,4,9:12);\n"); fprintf(fid," plot(f,psd,'LineWidth',1.5);\n"); fprintf(fid," axis([-0.5 0.5 -40 20]);\n"); fprintf(fid," xlabel('Normalized Frequency [f/F_s]');\n"); fprintf(fid," ylabel('PSD [dB]');\n"); fprintf(fid," grid on;\n"); #if 0 fprintf(fid,"figure;\n"); fprintf(fid," %% compute instantaneous received frequency\n"); fprintf(fid," freq_rx = arg( conj(z(:)) .* circshift(z(:),-1) )';\n"); fprintf(fid," freq_rx(1:(k*delay)) = 0;\n"); fprintf(fid," freq_rx(end) = 0;\n"); fprintf(fid," %% compute instantaneous tx/rx phase\n"); if (tx_filter_type == TXFILT_SQUARE) { fprintf(fid," theta_tx = filter([0 1],[1 -1],phi)/(h*pi);\n"); fprintf(fid," theta_rx = filter([0 1],[1 -1],freq_rx)/(h*pi);\n"); } else { fprintf(fid," theta_tx = filter([0.5 0.5],[1 -1],phi)/(h*pi);\n"); fprintf(fid," theta_rx = filter([0.5 0.5],[1 -1],freq_rx)/(h*pi);\n"); } fprintf(fid," %% plot instantaneous tx/rx phase\n"); fprintf(fid," plot(t, theta_tx,'-b', t(i), theta_tx(i),'sb',...\n"); fprintf(fid," t-delay,theta_rx,'-r', t(i)-delay,theta_rx(i),'sr');\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('instantaneous phase/(h \\pi)');\n"); fprintf(fid," legend('transmitted','syms','received/filtered','syms','location','northwest');\n"); fprintf(fid," grid on;\n"); #else // plot filter response fprintf(fid,"ht_len = %u;\n", ht_len); fprintf(fid,"ht = zeros(1,ht_len);\n"); for (i=0; i<ht_len; i++) fprintf(fid,"ht(%4u) = %12.8f;\n", i+1, ht[i]); fprintf(fid,"gt1 = filter([0.5 0.5],[1 -1],ht) / (pi*h);\n"); fprintf(fid,"gt2 = filter([0.0 1.0],[1 -1],ht) / (pi*h);\n"); fprintf(fid,"tfilt = [0:(ht_len-1)]/k - delay + 0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(tfilt,ht, '-x','MarkerSize',4,...\n"); fprintf(fid," tfilt,gt1,'-x','MarkerSize',4,...\n"); fprintf(fid," tfilt,gt2,'-x','MarkerSize',4);\n"); fprintf(fid,"axis([tfilt(1) tfilt(end) -0.1 1.1]);\n"); fprintf(fid,"legend('pulse','trap. int.','rect. int.','location','northwest');\n"); fprintf(fid,"grid on;\n"); #endif fclose(fid); printf("results written to '%s'\n", OUTPUT_FILENAME); // free allocated filter memory free(ht); return 0; }
// // AUTOTEST: validate analysis correctness // void autotest_firpfbch_crcf_analysis() { float tol = 1e-4f; // error tolerance unsigned int num_channels=4; // number of channels unsigned int p=5; // filter length (symbols) unsigned int num_symbols=12; // number of symbols // derived values unsigned int num_samples = num_channels * num_symbols; unsigned int i; unsigned int j; // generate filter // NOTE : these coefficients can be random; the purpose of this // exercise is to demonstrate mathematical equivalence unsigned int h_len = p*num_channels; float h[h_len]; for (i=0; i<h_len; i++) h[i] = randnf(); // create filterbank object firpfbch_crcf q = firpfbch_crcf_create(LIQUID_ANALYZER, num_channels, p, h); // generate filter object firfilt_crcf f = firfilt_crcf_create(h, h_len); // allocate memory for arrays float complex y[num_samples]; // time-domain input float complex Y0[num_symbols][num_channels]; // channelized output float complex Y1[num_symbols][num_channels]; // channelized output // generate input sequence (complex noise) for (i=0; i<num_samples; i++) y[i] = randnf() * cexpf(_Complex_I*randf()*2*M_PI); // // run analysis filter bank // for (i=0; i<num_symbols; i++) firpfbch_crcf_analyzer_execute(q, &y[i*num_channels], &Y0[i][0]); // // run traditional down-converter (inefficient) // float dphi; // carrier frequency unsigned int n=0; for (i=0; i<num_channels; i++) { // reset filter firfilt_crcf_clear(f); // set center frequency dphi = 2.0f * M_PI * (float)i / (float)num_channels; // reset symbol counter n=0; for (j=0; j<num_samples; j++) { // push down-converted sample into filter firfilt_crcf_push(f, y[j]*cexpf(-_Complex_I*j*dphi)); // compute output at the appropriate sample time assert(n<num_symbols); if ( ((j+1)%num_channels)==0 ) { firfilt_crcf_execute(f, &Y1[n][i]); n++; } } assert(n==num_symbols); } // destroy objects firfilt_crcf_destroy(f); firpfbch_crcf_destroy(q); if (liquid_autotest_verbose) { // print filterbank channelizer printf("\n"); printf("filterbank channelizer:\n"); for (i=0; i<num_symbols; i++) { printf("%3u: ", i); for (j=0; j<num_channels; j++) { printf(" %8.5f+j%8.5f, ", crealf(Y0[i][j]), cimagf(Y0[i][j])); } printf("\n"); } // print traditional channelizer printf("\n"); printf("traditional channelizer:\n"); for (i=0; i<num_symbols; i++) { printf("%3u: ", i); for (j=0; j<num_channels; j++) { printf(" %8.5f+j%8.5f, ", crealf(Y1[i][j]), cimagf(Y1[i][j])); } printf("\n"); } } // compare results for (i=0; i<num_symbols; i++) { for (j=0; j<num_channels; j++) { CONTEND_DELTA( crealf(Y0[i][j]), crealf(Y1[i][j]), tol ); CONTEND_DELTA( cimagf(Y0[i][j]), cimagf(Y1[i][j]), tol ); } } }
int main(int argc, char*argv[]) { // options unsigned int h_len = 51; // doppler filter length float fd = 0.1f; // maximum doppler frequency float K = 2.0f; // Rice fading factor float omega = 1.0f; // mean power float theta = 0.0f; // angle of arrival unsigned int num_samples=1200; // number of samples int dopt; while ((dopt = getopt(argc,argv,"hn:f:K:O:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'n': num_samples = atoi(optarg); break; case 'f': fd = atof(optarg); break; case 'K': K = atof(optarg); break; case 'O': omega = atof(optarg); break; default: exit(1); } } // validate input if (K < 0.0f) { fprintf(stderr,"error: %s, fading factor K must be greater than zero\n", argv[0]); exit(1); } else if (omega < 0.0f) { fprintf(stderr,"error: %s, signal power Omega must be greater than zero\n", argv[0]); exit(1); } else if (fd <= 0.0f || fd >= 0.5f) { fprintf(stderr,"error: %s, Doppler frequency must be in (0,0.5)\n", argv[0]); exit(1); } else if (h_len < 4) { fprintf(stderr,"error: %s, Doppler filter length too small\n", argv[0]); exit(1); } else if (num_samples == 0) { fprintf(stderr,"error: %s, number of samples must be greater than zero\n", argv[0]); exit(1); } unsigned int i; // allocate array for output samples float complex * y = (float complex*) malloc(num_samples*sizeof(float complex)); // generate Doppler filter coefficients float h[h_len]; liquid_firdes_doppler(h_len, fd, K, theta, h); // normalize filter coefficients such that output Gauss random // variables have unity variance float std = 0.0f; for (i=0; i<h_len; i++) std += h[i]*h[i]; std = sqrtf(std); for (i=0; i<h_len; i++) h[i] /= std; // create Doppler filter from coefficients firfilt_crcf fdoppler = firfilt_crcf_create(h,h_len); // generate complex circular Gauss random variables float complex v; // circular Gauss random variable (uncorrelated) float complex x; // circular Gauss random variable (correlated w/ Doppler filter) float s = sqrtf((omega*K)/(K+1.0)); float sig = sqrtf(0.5f*omega/(K+1.0)); for (i=0; i<num_samples; i++) { // generate complex Gauss random variable crandnf(&v); // push through Doppler filter firfilt_crcf_push(fdoppler, v); firfilt_crcf_execute(fdoppler, &x); // convert result to random variable with Rice-K distribution y[i] = _Complex_I*( crealf(x)*sig + s ) + ( cimagf(x)*sig ); } // destroy filter object firfilt_crcf_destroy(fdoppler); // export results to file FILE * fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s, auto-generated file\n\n",OUTPUT_FILENAME); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"\n"); fprintf(fid,"h_len = %u;\n", h_len); fprintf(fid,"num_samples = %u;\n", num_samples); // save filter coefficients for (i=0; i<h_len; i++) fprintf(fid,"h(%6u) = %12.4e;\n", i+1, h[i]); // save samples for (i=0; i<num_samples; i++) fprintf(fid,"y(%6u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i])); // plot power spectral density of filter fprintf(fid,"nfft = min(1024, 2^(ceil(log2(h_len))+4));\n"); fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"H = 20*log10(abs(fftshift(fft(h,nfft))));\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(f,H);\n"); fprintf(fid,"axis([-0.5 0.5 -80 20]);\n"); fprintf(fid,"xlabel('Normalized Frequency [f/F_s]');\n"); fprintf(fid,"ylabel('Filter Power Spectral Density [dB]');\n"); fprintf(fid,"grid on;\n"); // plot fading profile fprintf(fid,"figure;\n"); fprintf(fid,"t = 0:(num_samples-1);\n"); fprintf(fid,"plot(t,20*log10(abs(y)));\n"); fprintf(fid,"xlabel('Normalized Time [t F_s]');\n"); fprintf(fid,"ylabel('Fading Power Envelope [dB]');\n"); fprintf(fid,"axis([0 num_samples -40 10]);\n"); fprintf(fid,"grid on;\n"); // plot distribution fprintf(fid,"[nn xx] = hist(abs(y),15);\n"); fprintf(fid,"bin_width = xx(2) - xx(1);\n"); fprintf(fid,"ymax = max(abs(y));\n"); fprintf(fid,"s = %12.4e;\n", s); fprintf(fid,"sig = %12.4e;\n", sig); fprintf(fid,"yp = 1.1*ymax*[1:500]/500;\n"); fprintf(fid,"pdf = (yp/sig^2) .* exp(-(yp.^2+s^2)/(2*sig^2)) .* besseli(0,yp*s/sig^2);\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(yp,pdf,'-', xx,nn/(num_samples*bin_width),'x');\n"); fprintf(fid,"xlabel('Fading Magnitude');\n"); fprintf(fid,"ylabel('Probability Density');\n"); fprintf(fid,"legend('theory','data','location','northeast');\n"); // close output file fclose(fid); printf("results written to %s\n", OUTPUT_FILENAME); // clean up allocated arrays free(y); printf("done.\n"); return 0; }
// 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 ); }
int main(int argc, char*argv[]) { // options unsigned int num_channels=6; // number of channels (must be even) unsigned int m=4; // filter delay unsigned int num_symbols=4*m; // number of symbols // validate input if (num_channels%2) { fprintf(stderr,"error: %s, number of channels must be even\n", argv[0]); exit(1); } // derived values unsigned int num_samples = num_channels * num_symbols; unsigned int i; unsigned int j; // generate filter // NOTE : these coefficients can be random; the purpose of this // exercise is to demonstrate mathematical equivalence #if 0 unsigned int h_len = 2*m*num_channels; float h[h_len]; for (i=0; i<h_len; i++) h[i] = randnf(); #else unsigned int h_len = 2*m*num_channels+1; float h[h_len]; // NOTE: 81.29528 dB > beta = 8.00000 (6 channels, m=4) liquid_firdes_kaiser(h_len, 1.0f/(float)num_channels, 81.29528f, 0.0f, h); #endif // normalize float hsum = 0.0f; for (i=0; i<h_len; i++) hsum += h[i]; for (i=0; i<h_len; i++) h[i] = h[i] * num_channels / hsum; // sub-sampled filters for M=6 channels, m=4, beta=8.0 // -3.2069e-19 -6.7542e-04 -1.3201e-03 2.2878e-18 3.7613e-03 5.8033e-03 // -7.2899e-18 -1.2305e-02 -1.7147e-02 1.6510e-17 3.1187e-02 4.0974e-02 // -3.0032e-17 -6.8026e-02 -8.6399e-02 4.6273e-17 1.3732e-01 1.7307e-01 // -6.2097e-17 -2.8265e-01 -3.7403e-01 7.3699e-17 8.0663e-01 1.6438e+00 // 2.0001e+00 1.6438e+00 8.0663e-01 7.3699e-17 -3.7403e-01 -2.8265e-01 // -6.2097e-17 1.7307e-01 1.3732e-01 4.6273e-17 -8.6399e-02 -6.8026e-02 // -3.0032e-17 4.0974e-02 3.1187e-02 1.6510e-17 -1.7147e-02 -1.2305e-02 // -7.2899e-18 5.8033e-03 3.7613e-03 2.2878e-18 -1.3201e-03 -6.7542e-04 // create filterbank manually dotprod_crcf dp[num_channels]; // vector dot products windowcf w[num_channels]; // window buffers #if DEBUG // print coefficients printf("h_prototype:\n"); for (i=0; i<h_len; i++) printf(" h[%3u] = %12.8f\n", i, h[i]); #endif // create objects unsigned int h_sub_len = 2*m; float h_sub[h_sub_len]; for (i=0; i<num_channels; i++) { // sub-sample prototype filter #if 0 for (j=0; j<h_sub_len; j++) h_sub[j] = h[j*num_channels+i]; #else // load coefficients in reverse order for (j=0; j<h_sub_len; j++) h_sub[h_sub_len-j-1] = h[j*num_channels+i]; #endif // create window buffer and dotprod objects dp[i] = dotprod_crcf_create(h_sub, h_sub_len); w[i] = windowcf_create(h_sub_len); #if DEBUG printf("h_sub[%u] : \n", i); for (j=0; j<h_sub_len; j++) printf(" h[%3u] = %12.8f\n", j, h_sub[j]); #endif } // generate DFT object float complex x[num_channels]; // time-domain buffer float complex X[num_channels]; // freq-domain buffer #if 1 fftplan fft = fft_create_plan(num_channels, X, x, LIQUID_FFT_BACKWARD, 0); #else fftplan fft = fft_create_plan(num_channels, X, x, LIQUID_FFT_FORWARD, 0); #endif float complex y[num_samples]; // time-domain input float complex Y0[2*num_symbols][num_channels]; // channelizer output float complex Y1[2*num_symbols][num_channels]; // conventional output // generate input sequence for (i=0; i<num_samples; i++) { //y[i] = randnf() * cexpf(_Complex_I*randf()*2*M_PI); y[i] = (i==0) ? 1.0f : 0.0f; y[i] = cexpf(_Complex_I*sqrtf(2.0f)*i*i); printf("y[%3u] = %12.8f + %12.8fj\n", i, crealf(y[i]), cimagf(y[i])); } // // run analysis filter bank // #if 0 unsigned int filter_index = 0; #else unsigned int filter_index = num_channels/2-1; #endif float complex y_hat; // input sample float complex * r; // buffer read pointer int toggle = 0; // flag indicating buffer/filter alignment // for (i=0; i<2*num_symbols; i++) { // load buffers in blocks of num_channels/2 for (j=0; j<num_channels/2; j++) { // grab sample y_hat = y[i*num_channels/2 + j]; // push sample into buffer at filter index windowcf_push(w[filter_index], y_hat); // decrement filter index filter_index = (filter_index + num_channels - 1) % num_channels; //filter_index = (filter_index + 1) % num_channels; } // execute filter outputs // reversing order of output (not sure why this is necessary) unsigned int offset = toggle ? num_channels/2 : 0; toggle = 1-toggle; for (j=0; j<num_channels; j++) { unsigned int buffer_index = (offset+j)%num_channels; unsigned int dotprod_index = j; windowcf_read(w[buffer_index], &r); //dotprod_crcf_execute(dp[dotprod_index], r, &X[num_channels-j-1]); dotprod_crcf_execute(dp[dotprod_index], r, &X[buffer_index]); } printf("***** i = %u\n", i); for (j=0; j<num_channels; j++) printf(" v2[%4u] = %12.8f + %12.8fj\n", j, crealf(X[j]), cimagf(X[j])); // execute DFT, store result in buffer 'x' fft_execute(fft); // scale fft output for (j=0; j<num_channels; j++) x[j] *= 1.0f / (num_channels); // move to output array for (j=0; j<num_channels; j++) Y0[i][j] = x[j]; } // destroy objects for (i=0; i<num_channels; i++) { dotprod_crcf_destroy(dp[i]); windowcf_destroy(w[i]); } fft_destroy_plan(fft); // // run traditional down-converter (inefficient) // // generate filter object firfilt_crcf f = firfilt_crcf_create(h, h_len); float dphi; // carrier frequency unsigned int n=0; for (i=0; i<num_channels; i++) { // reset filter firfilt_crcf_clear(f); // set center frequency dphi = 2.0f * M_PI * (float)i / (float)num_channels; // reset symbol counter n=0; for (j=0; j<num_samples; j++) { // push down-converted sample into filter firfilt_crcf_push(f, y[j]*cexpf(-_Complex_I*j*dphi)); // compute output at the appropriate sample time assert(n<2*num_symbols); if ( ((j+1)%(num_channels/2))==0 ) { firfilt_crcf_execute(f, &Y1[n][i]); n++; } } assert(n==2*num_symbols); } firfilt_crcf_destroy(f); // print filterbank channelizer printf("\n"); printf("filterbank channelizer:\n"); for (i=0; i<2*num_symbols; i++) { printf("%2u:", i); for (j=0; j<num_channels; j++) { printf("%6.3f+%6.3fj, ", crealf(Y0[i][j]), cimagf(Y0[i][j])); } printf("\n"); } #if 0 // print traditional channelizer printf("\n"); printf("traditional channelizer:\n"); for (i=0; i<2*num_symbols; i++) { printf("%2u:", i); for (j=0; j<num_channels; j++) { printf("%6.3f+%6.3fj, ", crealf(Y1[i][j]), cimagf(Y1[i][j])); } printf("\n"); } // // compare results // float mse[num_channels]; float complex d; for (i=0; i<num_channels; i++) { mse[i] = 0.0f; for (j=0; j<2*num_symbols; j++) { d = Y0[j][i] - Y1[j][i]; mse[i] += crealf(d*conjf(d)); } mse[i] /= num_symbols; } printf("\n"); printf(" e:"); for (i=0; i<num_channels; i++) printf("%12.4e ", sqrt(mse[i])); printf("\n"); #endif printf("done.\n"); return 0; }