void DestroyMinimumPhaseAnalysis(MinimumPhaseAnalysis *minimum_phase) { fft_destroy_plan(minimum_phase->forward_fft); fft_destroy_plan(minimum_phase->inverse_fft); delete[] minimum_phase->cepstrum; delete[] minimum_phase->log_spectrum; delete[] minimum_phase->minimum_phase_spectrum; }
// Compute spectral response void liquid_doc_freqz(float * _b, unsigned int _nb, float * _a, unsigned int _na, unsigned int _nfft, float complex * _H) { unsigned int i; float complex x[_nfft]; float complex B[_nfft]; float complex A[_nfft]; float complex X[_nfft]; fftplan fft = fft_create_plan(_nfft,x,X,FFT_FORWARD,0); // numerator for (i=0; i<_nfft; i++) x[i] = i < _nb ? _b[i] : 0.0f; fft_execute(fft); memmove(B,X,_nfft*sizeof(float complex)); // denominator for (i=0; i<_nfft; i++) x[i] = i < _na ? _a[i] : 0.0f; fft_execute(fft); memmove(A,X,_nfft*sizeof(float complex)); fft_destroy_plan(fft); for (i=0; i<_nfft; i++) X[i] = B[i] / A[i]; memmove(_H, X, _nfft*sizeof(float complex)); }
// autotest helper function void fft_r2r_test(float * _x, float * _test, unsigned int _n, unsigned int _kind) { int _flags = 0; float tol=1e-4f; unsigned int i; float y[_n]; // compute real even/odd FFT fftplan q = fft_create_plan_r2r_1d(_n, _x, y, _kind, _flags); fft_execute(q); // print results if (liquid_autotest_verbose) { printf("%12s %12s\n", "expected", "actual"); for (i=0; i<_n; i++) printf("%12.8f %12.8f\n", _test[i], y[i]); } // validate results for (i=0; i<_n; i++) CONTEND_DELTA( y[i], _test[i], tol); // destroy plans fft_destroy_plan(q); }
// autotest helper function // _x : fft input array // _test : expected fft output // _n : fft size void fft_test(float complex * _x, float complex * _test, unsigned int _n) { int _method = 0; float tol=2e-4f; unsigned int i; float complex y[_n], z[_n]; // compute FFT fftplan pf = fft_create_plan(_n, _x, y, LIQUID_FFT_FORWARD, _method); fft_execute(pf); // compute IFFT fftplan pr = fft_create_plan(_n, y, z, LIQUID_FFT_BACKWARD, _method); fft_execute(pr); // normalize inverse for (i=0; i<_n; i++) z[i] /= (float) _n; // validate results float fft_error, ifft_error; for (i=0; i<_n; i++) { fft_error = cabsf( y[i] - _test[i] ); ifft_error = cabsf( _x[i] - z[i] ); CONTEND_DELTA( fft_error, 0, tol); CONTEND_DELTA( ifft_error, 0, tol); } // destroy plans fft_destroy_plan(pf); fft_destroy_plan(pr); }
void liquid_doc_compute_psdcf(float complex * _x, unsigned int _n, float complex * _X, unsigned int _nfft, liquid_doc_psdwindow _wtype, int _normalize) { unsigned int i; // compute window and norm float w[_n]; float wnorm=0.0f; for (i=0; i<_n; i++) { switch (_wtype) { case LIQUID_DOC_PSDWINDOW_NONE: w[i] = 1.0f; break; case LIQUID_DOC_PSDWINDOW_HANN: w[i] = hann(i,_n); break; case LIQUID_DOC_PSDWINDOW_HAMMING: w[i] = hamming(i,_n); break; break; default: fprintf(stderr,"error: liquid_doc_compute_psd(), invalid window type\n"); exit(1); } wnorm += w[i]; } wnorm /= (float)(_n); float complex x[_nfft]; fftplan fft = fft_create_plan(_nfft,x,_X,FFT_FORWARD,0); for (i=0; i<_nfft; i++) { x[i] = i < _n ? _x[i] * w[i] / wnorm : 0.0f; } fft_execute(fft); fft_destroy_plan(fft); // normalize spectrum by maximum if (_normalize) { float X_max = 0.0f; for (i=0; i<_nfft; i++) X_max = cabsf(_X[i]) > X_max ? cabsf(_X[i]) : X_max; for (i=0; i<_nfft; i++) _X[i] /= X_max; } }
void WorldSynthesis::_destroy() { if(_minimumPhase) { DestroyMinimumPhaseAnalysis(_minimumPhase); } delete _minimumPhase; delete[] _spectrum; delete[] _impulse; if(_plan) { fft_destroy_plan(*_plan); _plan = NULL; } _fftLength = 0; _spectrum = NULL; _impulse = NULL; }
void SpectrumVisualProcessor::setup(unsigned int fftSize_in) { std::lock_guard < std::mutex > busy_lock(busy_run); fftSize = fftSize_in; fftSizeInternal = fftSize_in * SPECTRUM_VZM; lastDataSize = 0; int memSize = sizeof(liquid_float_complex) * fftSizeInternal; if (fftInput) { free(fftInput); } fftInput = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftInData) { free(fftInData); } fftInData = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftLastData) { free(fftLastData); } fftLastData = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftOutput) { free(fftOutput); } fftOutput = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftPlan) { fft_destroy_plan(fftPlan); } fftPlan = fft_create_plan(fftSizeInternal, fftInput, fftOutput, LIQUID_FFT_FORWARD, 0); }
void do_spectrum() { std::complex<float> *data_in; windowcf_read(w, &data_in); fftplan q = fft_create_plan(SPECTRUM_FFT_LENGTH,data_in,spectrum_fft_output,LIQUID_FFT_FORWARD,0); fft_execute(q); fft_destroy_plan(q); spectrum_fft_magnitude[0] = 0.0; //Do not consider DC comp int maxIdx = 0; float maxVal = 0; double total_power = 0; double psd = 0; for (int i = 1; i < SPECTRUM_FFT_LENGTH/2; i++) { spectrum_fft_magnitude[i] = std::norm(spectrum_fft_output[i]); total_power += spectrum_fft_magnitude[i]; if (spectrum_fft_magnitude[i] > maxVal) { maxIdx = i; maxVal = spectrum_fft_magnitude[i]; } } // TODO normalize so window size does not affect this threshold. psd = maxVal / total_power; if (shm_settings.auto_magnitude_thresholding == 0) { if (psd > shm_settings.spectrum_magnitude_threshold && (current_sample_count - old_spectrum_sample_count > shm_settings.spectrum_cooldown_samples)) { shm_results_spectrum.most_recent_ping_magnitude = maxVal; shm_results_spectrum.most_recent_ping_frequency = (double) SAMPLING_FREQUENCY / (double) SPECTRUM_FFT_LENGTH * (double) maxIdx; shm_results_spectrum.most_recent_ping_count++; old_spectrum_sample_count = current_sample_count; shm_setg(hydrophones_results_spectrum, shm_results_spectrum); } prev_psd = psd; } else { //IMPLEMENTE AUTO THRESHOLDING AND ASSOCIATED PING STUFF } }
// Helper function to keep code base small void fft_runbench(struct rusage * _start, struct rusage * _finish, unsigned long int * _num_iterations, unsigned int _nfft, int _direction) { // initialize arrays, plan float complex * x = (float complex *) malloc(_nfft*sizeof(float complex)); float complex * y = (float complex *) malloc(_nfft*sizeof(float complex)); int _method = 0; fftplan q = fft_create_plan(_nfft, x, y, _direction, _method); unsigned long int i; // initialize input with random values for (i=0; i<_nfft; i++) x[i] = randnf() + randnf()*_Complex_I; // scale number of iterations to keep execution time // relatively linear *_num_iterations /= _nfft; // start trials getrusage(RUSAGE_SELF, _start); for (i=0; i<(*_num_iterations); i++) { fft_execute(q); fft_execute(q); fft_execute(q); fft_execute(q); } getrusage(RUSAGE_SELF, _finish); *_num_iterations *= 4; fft_destroy_plan(q); free(x); free(y); }
// Helper function to keep code base small void fft_r2r_bench(struct rusage *_start, struct rusage *_finish, unsigned long int *_num_iterations, unsigned int _n, int _kind) { // initialize arrays, plan float x[_n], y[_n]; int _flags = 0; fftplan p = fft_create_plan_r2r_1d(_n, x, y, _kind, _flags); unsigned long int i; // initialize input with random values for (i=0; i<_n; i++) x[i] = randnf(); // scale number of iterations to keep execution time // relatively linear *_num_iterations /= _n * _n; *_num_iterations *= 10; *_num_iterations += 1; // start trials getrusage(RUSAGE_SELF, _start); for (i=0; i<(*_num_iterations); i++) { fft_execute(p); fft_execute(p); fft_execute(p); fft_execute(p); } getrusage(RUSAGE_SELF, _finish); *_num_iterations *= 4; fft_destroy_plan(p); }
void DestroyInverseRealFFT(InverseRealFFT *inverse_real_fft) { fft_destroy_plan(inverse_real_fft->inverse_fft); delete[] inverse_real_fft->spectrum; delete[] inverse_real_fft->waveform; }
void DestroyForwardRealFFT(ForwardRealFFT *forward_real_fft) { fft_destroy_plan(forward_real_fft->forward_fft); delete[] forward_real_fft->spectrum; delete[] forward_real_fft->waveform; }
int main() { // options unsigned int num_channels=64; // must be even number unsigned int num_symbols=16; // number of symbols unsigned int m=3; // filter delay (symbols) float beta = 0.9f; // filter excess bandwidth factor float phi = 0.0f; // carrier phase offset; float dphi = 0.04f; // carrier frequency offset // number of frames (compensate for filter delay) unsigned int num_frames = num_symbols + 2*m; unsigned int num_samples = num_channels * num_frames; unsigned int i; unsigned int j; // create filter prototype unsigned int h_len = 2*num_channels*m + 1; float h[h_len]; float complex hc[h_len]; float complex gc[h_len]; liquid_firdes_rkaiser(num_channels, m, beta, 0.0f, h); unsigned int g_len = 2*num_channels*m; for (i=0; i<g_len; i++) { hc[i] = h[i]; gc[i] = h[g_len-i-1] * cexpf(_Complex_I*dphi*i); } // data arrays float complex s[num_channels]; // input symbols float complex y[num_samples]; // time-domain samples float complex Y0[num_frames][num_channels]; // channelized output float complex Y1[num_frames][num_channels]; // channelized output // create ofdm/oqam generator object and generate data ofdmoqam qs = ofdmoqam_create(num_channels, m, beta, 0.0f, LIQUID_SYNTHESIZER, 0); for (i=0; i<num_frames; i++) { for (j=0; j<num_channels; j++) { if (i<num_symbols) { #if 0 // QPSK on all subcarriers s[j] = (rand() % 2 ? 1.0f : -1.0f) + (rand() % 2 ? 1.0f : -1.0f) * _Complex_I; s[j] *= 1.0f / sqrtf(2.0f); #else // BPSK on even subcarriers s[j] = rand() % 2 ? 1.0f : -1.0f; s[j] *= (j%2)==0 ? 1.0f : 0.0f; #endif } else { s[j] = 0.0f; } } // run synthesizer ofdmoqam_execute(qs, s, &y[i*num_channels]); } ofdmoqam_destroy(qs); // channel for (i=0; i<num_samples; i++) y[i] *= cexpf(_Complex_I*(phi + dphi*i)); // // analysis filterbank (receiver) // // create filterbank manually dotprod_cccf 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 gc_sub_len = 2*m; float complex gc_sub[gc_sub_len]; for (i=0; i<num_channels; i++) { // sub-sample prototype filter, loading coefficients in // reverse order #if 0 for (j=0; j<gc_sub_len; j++) gc_sub[j] = h[j*num_channels+i]; #else for (j=0; j<gc_sub_len; j++) gc_sub[gc_sub_len-j-1] = gc[j*num_channels+i]; #endif // create window buffer and dotprod objects dp[i] = dotprod_cccf_create(gc_sub, gc_sub_len); w[i] = windowcf_create(gc_sub_len); #if DEBUG printf("gc_sub[%u] : \n", i); for (j=0; j<gc_sub_len; j++) printf(" g[%3u] = %12.8f + %12.8f\n", j, crealf(gc_sub[j]), cimagf(gc_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, FFT_REVERSE, 0); #else fftplan fft = fft_create_plan(num_channels, X, x, FFT_FORWARD, 0); #endif // // 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_frames; 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_cccf_execute(dp[j], r, &X[num_channels-j-1]); } #if 1 // compensate for carrier frequency offset (before transform) for (j=0; j<num_channels; j++) { X[j] *= cexpf(-_Complex_I*(dphi*i*num_channels)); } #endif // execute DFT, store result in buffer 'x' fft_execute(fft); #if 0 // compensate for carrier frequency offset (after transform) for (j=0; j<num_channels; j++) { x[j] *= cexpf(-_Complex_I*(dphi*i*num_channels)); } #endif // 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_cccf_destroy(dp[i]); windowcf_destroy(w[i]); } fft_destroy_plan(fft); #if 0 // 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"); } #endif // // export data // FILE*fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s: auto-generated file\n\n", OUTPUT_FILENAME); fprintf(fid,"clear all;\nclose all;\n\n"); fprintf(fid,"num_channels=%u;\n", num_channels); fprintf(fid,"num_symbols=%u;\n", num_symbols); fprintf(fid,"num_frames = %u;\n", num_frames); fprintf(fid,"num_samples = num_frames*num_channels;\n"); fprintf(fid,"y = zeros(1,%u);\n", num_samples); fprintf(fid,"Y0 = zeros(%u,%u);\n", num_frames, num_channels); fprintf(fid,"Y1 = zeros(%u,%u);\n", num_frames, num_channels); for (i=0; i<num_frames; 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]), cimagf(Y0[i][j])); fprintf(fid,"Y1(%4u,%4u) = %12.4e + j*%12.4e;\n", i+1, j+1, crealf(Y1[i][j]), cimagf(Y1[i][j])); } } // plot BPSK results fprintf(fid,"figure;\n"); fprintf(fid,"plot(Y0(:,1:2:end),'x');\n"); fprintf(fid,"axis([-1 1 -1 1]*1.2*sqrt(num_channels));\n"); fprintf(fid,"axis square;\n"); fprintf(fid,"grid on;\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 m = 3; // number of bits/symbol unsigned int k = 0; // filter samples/symbol unsigned int num_symbols = 200; // 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 float bandwidth = 0.20; // frequency spacing int dopt; while ((dopt = getopt(argc,argv,"hm:k:b:n:s:F:P:T:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'm': m = atoi(optarg); break; case 'k': k = atoi(optarg); break; case 'b': bandwidth = 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; unsigned int j; // derived values if (k == 0) k = 2 << m; // set samples per symbol if not otherwise specified unsigned int num_samples = k*num_symbols; unsigned int M = 1 << m; float nstd = powf(10.0f, -SNRdB/20.0f); float M2 = 0.5f*(float)(M-1); // validate input if (k < M) { fprintf(stderr,"errors: %s, samples/symbol must be at least modulation size (M=%u)\n", __FILE__,M); exit(1); } else if (k > 2048) { fprintf(stderr,"errors: %s, samples/symbol exceeds maximum (2048)\n", __FILE__); exit(1); } else if (M > 1024) { fprintf(stderr,"errors: %s, modulation size (M=%u) exceeds maximum (1024)\n", __FILE__, M); exit(1); } else if (bandwidth <= 0.0f || bandwidth >= 0.5f) { fprintf(stderr,"errors: %s, bandwidht must be in (0,0.5)\n", __FILE__); exit(1); } // compute demodulation FFT size such that FFT output bin frequencies are // as close to modulated frequencies as possible unsigned int K = 0; // demodulation FFT size float df = bandwidth / M2; // frequency spacing float err_min = 1e9f; unsigned int K_min = k; // minimum FFT size unsigned int K_max = k*4 < 16 ? 16 : k*4; // maximum FFT size unsigned int K_hat; for (K_hat=K_min; K_hat<=K_max; K_hat++) { // compute candidate FFT size float v = 0.5f*df * (float)K_hat; // bin spacing float err = fabsf( roundf(v) - v ); // fractional bin spacing // print results printf(" K_hat = %4u : v = %12.8f, err=%12.8f %s\n", K_hat, v, err, err < err_min ? "*" : ""); // save best result if (K_hat==K_min || err < err_min) { K = K_hat; err_min = err; } // perfect match; no need to continue searching if (err < 1e-6f) break; } // 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 // determine demodulation mapping between tones and frequency bins // TODO: use gray coding unsigned int demod_map[M]; for (i=0; i<M; i++) { // print frequency bins float freq = ((float)i - M2) * bandwidth / M2; float idx = freq * (float)K; unsigned int index = (unsigned int) (idx < 0 ? roundf(idx + K) : roundf(idx)); demod_map[i] = index; printf(" s=%3u, f = %12.8f, index=%3u\n", i, freq, index); } // check for uniqueness for (i=1; i<M; i++) { if (demod_map[i] == demod_map[i-1]) { fprintf(stderr,"warning: demod map is not unique; consider increasing bandwidth\n"); break; } } // generate message symbols and modulate // TODO: use gray coding for (i=0; i<num_symbols; i++) { // generate random symbol sym_in[i] = rand() % M; // compute frequency float dphi = 2*M_PI*((float)sym_in[i] - M2) * bandwidth / M2; // generate random phase float phi = randf() * 2 * M_PI; // modulate symbol for (j=0; j<k; j++) x[i*k+j] = cexpf(_Complex_I*phi + _Complex_I*j*dphi); } // push through channel for (i=0; i<num_samples; i++) y[i] = x[i] + nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2; #if 0 // demodulate signal: high SNR method float complex buf_time[k]; unsigned int n = 0; j = 0; for (i=0; i<num_samples; i++) { // start filling time buffer with samples (assume perfect symbol timing) buf_time[n++] = y[i]; // demodulate symbol if (n==k) { // reset counter n = 0; // estimate frequency float complex metric = 0; unsigned int s; for (s=1; s<k; s++) metric += buf_time[s] * conjf(buf_time[s-1]); float dphi_hat = cargf(metric) / (2*M_PI); unsigned int v=( (unsigned int) roundf(dphi_hat*M2/bandwidth + M2) ) % M; sym_out[j++] = v; printf("%3u : %12.8f : %u\n", j, dphi_hat, v); } } #else // demodulate signal: least-squares method float complex buf_time[K]; float complex buf_freq[K]; fftplan fft = fft_create_plan(K, buf_time, buf_freq, LIQUID_FFT_FORWARD, 0); for (i=0; i<K; i++) buf_time[i] = 0.0f; unsigned int n = 0; j = 0; for (i=0; i<num_samples; i++) { // start filling time buffer with samples (assume perfect symbol timing) buf_time[n++] = y[i]; // demodulate symbol if (n==k) { // reset counter n = 0; // compute transform, storing result in 'buf_freq' fft_execute(fft); // find maximum by looking at particular bins float vmax = 0; unsigned int s; unsigned int s_opt = 0; for (s=0; s<M; s++) { float v = cabsf( buf_freq[demod_map[s]] ); if (s==0 || v > vmax) { s_opt = s; vmax =v; } } // save best result sym_out[j++] = s_opt; } } // destroy fft object fft_destroy_plan(fft); #endif // count errors unsigned int num_symbol_errors = 0; for (i=0; i<num_symbols; i++) num_symbol_errors += (sym_in[i] == sym_out[i]) ? 0 : 1; printf("symbol errors: %u / %u\n", num_symbol_errors, num_symbols); // compute power spectral density of received signal unsigned int nfft = 1200; float psd[nfft]; spgramcf_estimate_psd(nfft, y, num_samples, psd); // // export results // // truncate to at most 10 symbols if (num_symbols > 10) num_symbols = 10; num_samples = k*num_symbols; 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,"M = %u;\n", M); fprintf(fid,"num_symbols = %u;\n", num_symbols); fprintf(fid,"num_samples = %u;\n", num_samples); fprintf(fid,"nfft = %u;\n", nfft); 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"); // plot time signal fprintf(fid,"subplot(2,1,1),\n"); fprintf(fid,"hold on;\n"); fprintf(fid," plot(t,real(y),'-', 'Color',[0 0.3 0.5]);\n"); fprintf(fid," plot(t,imag(y),'-', 'Color',[0 0.5 0.3]);\n"); fprintf(fid,"hold off;\n"); fprintf(fid,"ymax = ceil(max(abs(y))*5)/5;\n"); fprintf(fid,"axis([0 num_symbols -ymax ymax]);\n"); fprintf(fid,"xlabel('time');\n"); fprintf(fid,"ylabel('x(t)');\n"); fprintf(fid,"grid on;\n"); // plot PSD fprintf(fid,"subplot(2,1,2),\n"); fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"plot(f,psd,'LineWidth',1.5,'Color',[0.5 0 0]);\n"); fprintf(fid,"axis([-0.5 0.5 -40 40]);\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(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; }