// execute synchronizer, receiving payload // _q : frame synchronizer object // _x : input sample // _sym : demodulated symbol void flexframesync_execute_rxpayload(flexframesync _q, float complex _x) { // mix signal down float complex y; nco_crcf_mix_down(_q->nco_coarse, _x*0.5f/_q->gamma_hat, &y); nco_crcf_step(_q->nco_coarse); // update symbol synchronizer float complex mf_out = 0.0f; int sample_available = flexframesync_update_symsync(_q, y, &mf_out); // compute output if timeout if (sample_available) { // push through fine-tuned nco nco_crcf_mix_down(_q->nco_fine, mf_out, &mf_out); // save payload symbols for callback (up to 256 values) if (_q->payload_counter < 256) _q->payload_sym[_q->payload_counter] = mf_out; // demodulate unsigned int sym_out = 0; modem_demodulate(_q->demod_payload, mf_out, &sym_out); _q->payload_mod[_q->payload_counter] = (unsigned char)sym_out; // update phase-locked loop and fine-tuned NCO float phase_error = modem_get_demodulator_phase_error(_q->demod_payload); nco_crcf_pll_step(_q->nco_fine, phase_error); nco_crcf_step(_q->nco_fine); // increment counter _q->payload_counter++; if (_q->payload_counter == _q->payload_mod_len) { // decode payload and invoke callback flexframesync_decode_payload(_q); // invoke callback if (_q->callback != NULL) { // set framestats internals _q->framestats.evm = 20*log10f(sqrtf(_q->framestats.evm / FLEXFRAME_H_SYM)); _q->framestats.rssi = 20*log10f(_q->gamma_hat); _q->framestats.cfo = nco_crcf_get_frequency(_q->nco_coarse) + nco_crcf_get_frequency(_q->nco_fine) / 2.0f; //(float)(_q->k); _q->framestats.framesyms = _q->payload_sym; _q->framestats.num_framesyms = _q->payload_mod_len > 256 ? 256 : _q->payload_mod_len; _q->framestats.mod_scheme = _q->ms_payload; _q->framestats.mod_bps = _q->bps_payload; _q->framestats.check = _q->check; _q->framestats.fec0 = _q->fec0; _q->framestats.fec1 = _q->fec1; // invoke callback method _q->callback(_q->header, _q->header_valid, _q->payload_dec, _q->payload_dec_len, _q->payload_valid, _q->framestats, _q->userdata); } // reset frame synchronizer flexframesync_reset(_q); return; } } }
// Helper function to keep code base small void modem_test_demodstats(modulation_scheme _ms) { // generate mod/demod modem mod = modem_create(_ms); modem demod = modem_create(_ms); // run the test unsigned int i, s, M = 1 << modem_get_bps(mod); float complex x; float complex x_hat; // rotated symbol float demodstats; float phi = 0.01f; for (i=0; i<M; i++) { // reset modem objects modem_reset(mod); modem_reset(demod); // modulate symbol modem_modulate(mod, i, &x); // ignore rare condition where modulated symbol is (0,0) // (e.g. APSK-8) if (cabsf(x) < 1e-3f) continue; // add phase offsets x_hat = x * cexpf( phi*_Complex_I); // demod positive phase signal, and ensure demodulator // maps to appropriate symbol modem_demodulate(demod, x_hat, &s); if (s != i) AUTOTEST_WARN("modem_test_demodstats(), output symbol does not match"); demodstats = modem_get_demodulator_phase_error(demod); CONTEND_EXPRESSION(demodstats > 0.0f); } // repeat with negative phase error for (i=0; i<M; i++) { // reset modem objects modem_reset(mod); modem_reset(demod); // modulate symbol modem_modulate(mod, i, &x); // ignore rare condition where modulated symbol is (0,0) // (e.g. APSK-8) if (cabsf(x) < 1e-3f) continue; // add phase offsets x_hat = x * cexpf(-phi*_Complex_I); // demod positive phase signal, and ensure demodulator // maps to appropriate symbol modem_demodulate(demod, x_hat, &s); if (s != i) AUTOTEST_WARN("modem_test_demodstats(), output symbol does not match"); demodstats = modem_get_demodulator_phase_error(demod); CONTEND_EXPRESSION(demodstats < 0.0f); } // clean up allocated objects up modem_destroy(mod); modem_destroy(demod); }
// execute synchronizer, receiving header // _q : frame synchronizer object // _x : input sample void flexframesync_execute_rxheader(flexframesync _q, float complex _x) { // mix signal down float complex y; nco_crcf_mix_down(_q->nco_coarse, _x*0.5f/_q->gamma_hat, &y); nco_crcf_step(_q->nco_coarse); // update symbol synchronizer float complex mf_out = 0.0f; int sample_available = flexframesync_update_symsync(_q, y, &mf_out); // compute output if timeout if (sample_available) { // push through fine-tuned nco nco_crcf_mix_down(_q->nco_fine, mf_out, &mf_out); #if DEBUG_FLEXFRAMESYNC if (_q->debug_enabled) _q->header_sym[_q->header_counter] = mf_out; #endif // demodulate unsigned int sym_out = 0; #if DEMOD_HEADER_SOFT unsigned char bpsk_soft_bit = 0; modem_demodulate_soft(_q->demod_header, mf_out, &sym_out, &bpsk_soft_bit); _q->header_mod[_q->header_counter] = bpsk_soft_bit; #else modem_demodulate(_q->demod_header, mf_out, &sym_out); _q->header_mod[_q->header_counter] = (unsigned char)sym_out; #endif // update phase-locked loop and fine-tuned NCO float phase_error = modem_get_demodulator_phase_error(_q->demod_header); nco_crcf_pll_step(_q->nco_fine, phase_error); nco_crcf_step(_q->nco_fine); // update error vector magnitude float evm = modem_get_demodulator_evm(_q->demod_header); _q->framestats.evm += evm*evm; // increment counter _q->header_counter++; if (_q->header_counter == FLEXFRAME_H_SYM) { // decode header and invoke callback flexframesync_decode_header(_q); // invoke callback if header is invalid if (!_q->header_valid && _q->callback != NULL) { // set framestats internals _q->framestats.evm = 20*log10f(sqrtf(_q->framestats.evm / FLEXFRAME_H_SYM)); _q->framestats.rssi = 20*log10f(_q->gamma_hat); _q->framestats.cfo = nco_crcf_get_frequency(_q->nco_coarse) + nco_crcf_get_frequency(_q->nco_fine) / 2.0f; //(float)(_q->k); _q->framestats.framesyms = NULL; _q->framestats.num_framesyms = 0; _q->framestats.mod_scheme = LIQUID_MODEM_UNKNOWN; _q->framestats.mod_bps = 0; _q->framestats.check = LIQUID_CRC_UNKNOWN; _q->framestats.fec0 = LIQUID_FEC_UNKNOWN; _q->framestats.fec1 = LIQUID_FEC_UNKNOWN; // invoke callback method _q->callback(_q->header, _q->header_valid, NULL, 0, 0, _q->framestats, _q->userdata); } if (!_q->header_valid) { flexframesync_reset(_q); return; } // update state _q->state = STATE_RXPAYLOAD; } } }
int main(int argc, char*argv[]) { srand( time(NULL) ); // parameters float phase_offset = M_PI/10; float frequency_offset = 0.001f; float SNRdB = 30.0f; float pll_bandwidth = 0.02f; modulation_scheme ms = LIQUID_MODEM_QPSK; unsigned int n=256; // number of iterations int dopt; while ((dopt = getopt(argc,argv,"uhs:b:n:P:F:m:")) != EOF) { switch (dopt) { case 'u': case 'h': usage(); return 0; case 's': SNRdB = atof(optarg); break; case 'b': pll_bandwidth = atof(optarg); break; case 'n': n = atoi(optarg); break; case 'P': phase_offset = atof(optarg); break; case 'F': frequency_offset= atof(optarg); break; case 'm': ms = liquid_getopt_str2mod(optarg); if (ms == LIQUID_MODEM_UNKNOWN) { fprintf(stderr,"error: %s, unknown/unsupported modulation scheme \"%s\"\n", argv[0], optarg); return 1; } break; default: exit(1); } } unsigned int d=n/32; // print every "d" lines FILE * fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid, "%% %s : auto-generated file\n", OUTPUT_FILENAME); fprintf(fid, "clear all;\n"); fprintf(fid, "phi=zeros(1,%u);\n",n); fprintf(fid, "r=zeros(1,%u);\n",n); // objects nco_crcf nco_tx = nco_crcf_create(LIQUID_VCO); nco_crcf nco_rx = nco_crcf_create(LIQUID_VCO); modem mod = modem_create(ms); modem demod = modem_create(ms); unsigned int bps = modem_get_bps(mod); // initialize objects nco_crcf_set_phase(nco_tx, phase_offset); nco_crcf_set_frequency(nco_tx, frequency_offset); nco_crcf_pll_set_bandwidth(nco_rx, pll_bandwidth); float noise_power = powf(10.0f, -SNRdB/20.0f); // print parameters printf("PLL example :\n"); printf("modem : %u-%s\n", 1<<bps, modulation_types[ms].name); printf("frequency offset: %6.3f, phase offset: %6.3f, SNR: %6.2fdB, pll b/w: %6.3f\n", frequency_offset, phase_offset, SNRdB, pll_bandwidth); // run loop unsigned int i, M=1<<bps, sym_in, sym_out, num_errors=0; float phase_error; float complex x, r, v, noise; for (i=0; i<n; i++) { // generate random symbol sym_in = rand() % M; // modulate modem_modulate(mod, sym_in, &x); // channel //r = nco_crcf_cexpf(nco_tx); nco_crcf_mix_up(nco_tx, x, &r); // add complex white noise crandnf(&noise); r += noise * noise_power; // //v = nco_crcf_cexpf(nco_rx); nco_crcf_mix_down(nco_rx, r, &v); // demodulate modem_demodulate(demod, v, &sym_out); num_errors += count_bit_errors(sym_in, sym_out); // error estimation //phase_error = cargf(r*conjf(v)); phase_error = modem_get_demodulator_phase_error(demod); // perfect error estimation //phase_error = nco_tx->theta - nco_rx->theta; // print every line in a format that octave can read fprintf(fid, "phi(%u) = %10.6E;\n", i+1, phase_error); fprintf(fid, "r(%u) = %10.6E + j*%10.6E;\n", i+1, crealf(v), cimagf(v)); if ((i+1)%d == 0 || i==n-1) { printf(" %4u: e_hat : %6.3f, phase error : %6.3f, freq error : %6.3f\n", i+1, // iteration phase_error, // estimated phase error nco_crcf_get_phase(nco_tx) - nco_crcf_get_phase(nco_rx),// true phase error nco_crcf_get_frequency(nco_tx) - nco_crcf_get_frequency(nco_rx)// true frequency error ); } // update tx nco object nco_crcf_step(nco_tx); // update pll nco_crcf_pll_step(nco_rx, phase_error); // update rx nco object nco_crcf_step(nco_rx); } fprintf(fid, "figure;\n"); fprintf(fid, "plot(1:length(phi),phi,'LineWidth',2,'Color',[0 0.25 0.5]);\n"); fprintf(fid, "xlabel('Symbol Index');\n"); fprintf(fid, "ylabel('Phase Error [radians]');\n"); fprintf(fid, "grid on;\n"); fprintf(fid, "t0 = round(0.25*length(r));\n"); fprintf(fid, "figure;\n"); fprintf(fid, "plot(r(1:t0),'x','Color',[0.6 0.6 0.6],r(t0:end),'x','Color',[0 0.25 0.5]);\n"); fprintf(fid, "grid on;\n"); fprintf(fid, "axis([-1.5 1.5 -1.5 1.5]);\n"); fprintf(fid, "axis('square');\n"); fprintf(fid, "xlabel('In-Phase');\n"); fprintf(fid, "ylabel('Quadrature');\n"); fprintf(fid, "legend(['first 25%%'],['last 75%%'],1);\n"); fclose(fid); printf("results written to %s.\n",OUTPUT_FILENAME); nco_crcf_destroy(nco_tx); nco_crcf_destroy(nco_rx); modem_destroy(mod); modem_destroy(demod); printf("bit errors: %u / %u\n", num_errors, bps*n); printf("done.\n"); return 0; }
// execute synchronizer, receiving payload // _q : frame synchronizer object // _x : input sample // _sym : demodulated symbol void flexframesync_execute_rxpayload(flexframesync _q, float complex _x) { // step synchronizer float complex mf_out = 0.0f; int sample_available = flexframesync_step(_q, _x, &mf_out); // compute output if timeout if (sample_available) { // TODO: clean this up // mix down with fine-tuned oscillator nco_crcf_mix_down(_q->pll, mf_out, &mf_out); // track phase, accumulate error-vector magnitude unsigned int sym; modem_demodulate(_q->payload_demod, mf_out, &sym); float phase_error = modem_get_demodulator_phase_error(_q->payload_demod); float evm = modem_get_demodulator_evm (_q->payload_demod); nco_crcf_pll_step(_q->pll, phase_error); nco_crcf_step(_q->pll); _q->framesyncstats.evm += evm*evm; // save payload symbols (modem input/output) _q->payload_sym[_q->symbol_counter] = mf_out; // increment counter _q->symbol_counter++; if (_q->symbol_counter == _q->payload_sym_len) { // decode payload _q->payload_valid = qpacketmodem_decode(_q->payload_decoder, _q->payload_sym, _q->payload_dec); // update statistics _q->framedatastats.num_frames_detected++; _q->framedatastats.num_headers_valid++; _q->framedatastats.num_payloads_valid += _q->payload_valid; _q->framedatastats.num_bytes_received += _q->payload_dec_len; // invoke callback if (_q->callback != NULL) { // set framestats internals int ms = qpacketmodem_get_modscheme(_q->payload_decoder); _q->framesyncstats.evm = 10*log10f(_q->framesyncstats.evm / (float)_q->payload_sym_len); _q->framesyncstats.rssi = 20*log10f(_q->gamma_hat); _q->framesyncstats.cfo = nco_crcf_get_frequency(_q->mixer); _q->framesyncstats.framesyms = _q->payload_sym; _q->framesyncstats.num_framesyms = _q->payload_sym_len; _q->framesyncstats.mod_scheme = ms; _q->framesyncstats.mod_bps = modulation_types[ms].bps; _q->framesyncstats.check = qpacketmodem_get_crc(_q->payload_decoder); _q->framesyncstats.fec0 = qpacketmodem_get_fec0(_q->payload_decoder); _q->framesyncstats.fec1 = qpacketmodem_get_fec1(_q->payload_decoder); // invoke callback method _q->callback(_q->header_dec, _q->header_valid, _q->payload_dec, _q->payload_dec_len, _q->payload_valid, _q->framesyncstats, _q->userdata); } // reset frame synchronizer flexframesync_reset(_q); return; } } }
int main(int argc, char*argv[]) { //srand(time(NULL)); // options unsigned int num_symbols=800; // number of symbols to observe float SNRdB = 30.0f; // signal-to-noise ratio [dB] float fc = 0.002f; // carrier offset unsigned int hc_len=5; // channel filter length unsigned int k=2; // matched filter samples/symbol unsigned int m=3; // matched filter delay (symbols) float beta=0.3f; // matched filter excess bandwidth factor unsigned int p=3; // equalizer length (symbols, hp_len = 2*k*p+1) float mu = 0.08f; // equalizer learning rate // modulation type/depth modulation_scheme ms = LIQUID_MODEM_QPSK; int dopt; while ((dopt = getopt(argc,argv,"hn:s:c:k:m:b:p:u:M:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'n': num_symbols = atoi(optarg); break; case 's': SNRdB = atof(optarg); break; case 'c': hc_len = atoi(optarg); break; case 'k': k = atoi(optarg); break; case 'm': m = atoi(optarg); break; case 'b': beta = atof(optarg); break; case 'p': p = atoi(optarg); break; case 'u': mu = atof(optarg); break; case 'M': ms = liquid_getopt_str2mod(optarg); if (ms == LIQUID_MODEM_UNKNOWN) { fprintf(stderr,"error: %s, unknown/unsupported modulation scheme '%s'\n", argv[0], optarg); return 1; } break; default: exit(1); } } // validate input if (num_symbols == 0) { fprintf(stderr,"error: %s, number of symbols must be greater than zero\n", argv[0]); exit(1); } else if (hc_len == 0) { fprintf(stderr,"error: %s, channel must have at least 1 tap\n", argv[0]); exit(1); } else if (k < 2) { fprintf(stderr,"error: %s, samples/symbol must be at least 2\n", argv[0]); exit(1); } else if (m == 0) { fprintf(stderr,"error: %s, filter semi-length must be at least 1 symbol\n", argv[0]); exit(1); } else if (beta < 0.0f || beta > 1.0f) { fprintf(stderr,"error: %s, filter excess bandwidth must be in [0,1]\n", argv[0]); exit(1); } else if (p == 0) { fprintf(stderr,"error: %s, equalizer semi-length must be at least 1 symbol\n", argv[0]); exit(1); } else if (mu < 0.0f || mu > 1.0f) { fprintf(stderr,"error: %s, equalizer learning rate must be in [0,1]\n", argv[0]); exit(1); } // derived values unsigned int hm_len = 2*k*m+1; // matched filter length unsigned int hp_len = 2*k*p+1; // equalizer filter length unsigned int num_samples = k*num_symbols; // bookkeeping variables float complex syms_tx[num_symbols]; // transmitted data symbols float complex x[num_samples]; // interpolated time series float complex y[num_samples]; // channel output float complex z[num_samples]; // equalized output float complex syms_rx[num_symbols]; // received data symbols float hm[hm_len]; // matched filter response float complex hc[hc_len]; // channel filter coefficients float complex hp[hp_len]; // equalizer filter coefficients unsigned int i; // generate matched filter response liquid_firdes_rnyquist(LIQUID_FIRFILT_RRC, k, m, beta, 0.0f, hm); firinterp_crcf interp = firinterp_crcf_create(k, hm, hm_len); // create the modem objects modem mod = modem_create(ms); modem demod = modem_create(ms); unsigned int M = 1 << modem_get_bps(mod); // generate channel impulse response, filter hc[0] = 1.0f; for (i=1; i<hc_len; i++) hc[i] = 0.09f*(randnf() + randnf()*_Complex_I); firfilt_cccf fchannel = firfilt_cccf_create(hc, hc_len); // generate random symbols for (i=0; i<num_symbols; i++) modem_modulate(mod, rand()%M, &syms_tx[i]); // interpolate for (i=0; i<num_symbols; i++) firinterp_crcf_execute(interp, syms_tx[i], &x[i*k]); // push through channel float nstd = powf(10.0f, -SNRdB/20.0f); for (i=0; i<num_samples; i++) { firfilt_cccf_push(fchannel, x[i]); firfilt_cccf_execute(fchannel, &y[i]); // add carrier offset and noise y[i] *= cexpf(_Complex_I*2*M_PI*fc*i); y[i] += nstd*(randnf() + randnf()*_Complex_I)*M_SQRT1_2; } // push through equalizer // create equalizer, intialized with square-root Nyquist filter eqlms_cccf eq = eqlms_cccf_create_rnyquist(LIQUID_FIRFILT_RRC, k, p, beta, 0.0f); eqlms_cccf_set_bw(eq, mu); // get initialized weights eqlms_cccf_get_weights(eq, hp); // filtered error vector magnitude (emperical RMS error) float evm_hat = 0.03f; // nco/pll for phase recovery nco_crcf nco = nco_crcf_create(LIQUID_VCO); nco_crcf_pll_set_bandwidth(nco, 0.02f); float complex d_hat = 0.0f; unsigned int num_symbols_rx = 0; for (i=0; i<num_samples; i++) { // print filtered evm (emperical rms error) if ( ((i+1)%50)==0 ) printf("%4u : rms error = %12.8f dB\n", i+1, 10*log10(evm_hat)); eqlms_cccf_push(eq, y[i]); eqlms_cccf_execute(eq, &d_hat); // store output z[i] = d_hat; // decimate by k if ( (i%k) != 0 ) continue; // update equalizer independent of the signal: estimate error // assuming constant modulus signal eqlms_cccf_step(eq, d_hat/cabsf(d_hat), d_hat); // apply carrier recovery float complex v; nco_crcf_mix_down(nco, d_hat, &v); // save resulting data symbol if (num_symbols_rx < num_symbols) syms_rx[num_symbols_rx++] = v; // demodulate unsigned int sym_out; // output symbol float complex d_prime; // estimated input sample modem_demodulate(demod, v, &sym_out); modem_get_demodulator_sample(demod, &d_prime); float phase_error = modem_get_demodulator_phase_error(demod); // update pll nco_crcf_pll_step(nco, phase_error); // update rx nco object nco_crcf_step(nco); // update filtered evm estimate float evm = crealf( (d_prime-v)*conjf(d_prime-v) ); evm_hat = 0.98f*evm_hat + 0.02f*evm; } // get equalizer weights eqlms_cccf_get_weights(eq, hp); // destroy objects eqlms_cccf_destroy(eq); nco_crcf_destroy(nco); firinterp_crcf_destroy(interp); firfilt_cccf_destroy(fchannel); modem_destroy(mod); modem_destroy(demod); // // export output // 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,"k = %u;\n", k); fprintf(fid,"m = %u;\n", m); fprintf(fid,"num_symbols = %u;\n", num_symbols); fprintf(fid,"num_samples = num_symbols*k;\n"); // save transmit matched-filter response fprintf(fid,"hm_len = 2*k*m+1;\n"); fprintf(fid,"hm = zeros(1,hm_len);\n"); for (i=0; i<hm_len; i++) fprintf(fid,"hm(%4u) = %12.4e;\n", i+1, hm[i]); // save channel impulse response fprintf(fid,"hc_len = %u;\n", hc_len); fprintf(fid,"hc = zeros(1,hc_len);\n"); for (i=0; i<hc_len; i++) fprintf(fid,"hc(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(hc[i]), cimagf(hc[i])); // save equalizer response fprintf(fid,"hp_len = %u;\n", hp_len); fprintf(fid,"hp = zeros(1,hp_len);\n"); for (i=0; i<hp_len; i++) fprintf(fid,"hp(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(hp[i]), cimagf(hp[i])); // save sample sets fprintf(fid,"x = zeros(1,num_samples);\n"); fprintf(fid,"y = zeros(1,num_samples);\n"); fprintf(fid,"z = zeros(1,num_samples);\n"); for (i=0; i<num_samples; i++) { fprintf(fid,"x(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i])); fprintf(fid,"y(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i])); fprintf(fid,"z(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(z[i]), cimagf(z[i])); } fprintf(fid,"syms_rx = zeros(1,num_symbols);\n"); for (i=0; i<num_symbols; i++) { fprintf(fid,"syms_rx(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(syms_rx[i]), cimagf(syms_rx[i])); } // plot time response fprintf(fid,"t = 0:(num_samples-1);\n"); fprintf(fid,"tsym = 1:k:num_samples;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(t,real(z),...\n"); fprintf(fid," t(tsym),real(z(tsym)),'x');\n"); // plot constellation fprintf(fid,"syms_rx_0 = syms_rx(1:(length(syms_rx)/2));\n"); fprintf(fid,"syms_rx_1 = syms_rx((length(syms_rx)/2):end);\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(real(syms_rx_0),imag(syms_rx_0),'x','Color',[1 1 1]*0.7,...\n"); fprintf(fid," real(syms_rx_1),imag(syms_rx_1),'x','Color',[1 1 1]*0.0);\n"); fprintf(fid,"xlabel('In-Phase');\n"); fprintf(fid,"ylabel('Quadrature');\n"); fprintf(fid,"axis([-1 1 -1 1]*1.5);\n"); fprintf(fid,"axis square;\n"); fprintf(fid,"grid on;\n"); // compute composite response fprintf(fid,"g = real(conv(conv(hm,hc),hp));\n"); // plot responses fprintf(fid,"nfft = 1024;\n"); fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"Hm = 20*log10(abs(fftshift(fft(hm/k,nfft))));\n"); fprintf(fid,"Hc = 20*log10(abs(fftshift(fft(hc, nfft))));\n"); fprintf(fid,"Hp = 20*log10(abs(fftshift(fft(hp, nfft))));\n"); fprintf(fid,"G = 20*log10(abs(fftshift(fft(g/k, nfft))));\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(f,Hm, f,Hc, f,Hp, f,G,'-k','LineWidth',2, [-0.5/k 0.5/k],[-6.026 -6.026],'or');\n"); fprintf(fid,"xlabel('Normalized Frequency');\n"); fprintf(fid,"ylabel('Power Spectral Density');\n"); fprintf(fid,"legend('transmit','channel','equalizer','composite','half-power points','location','northeast');\n"); fprintf(fid,"axis([-0.5 0.5 -12 8]);\n"); fprintf(fid,"grid on;\n"); fclose(fid); printf("results written to '%s'\n", OUTPUT_FILENAME); return 0; }
int main(int argc, char*argv[]) { srand( time(NULL) ); // options int verbose = 1; // verbose output flag float phi_max_abs = M_PI/4.0f; // absolute maximum phase offset unsigned int num_phi = 21; // number of phase steps unsigned int num_trials = 1000; // number of trials float SNRdB = 12.0f; // signal-to-noise ratio [dB] modulation_scheme ms = LIQUID_MODEM_QPSK; char filename[256] = ""; // output filename int dopt; while ((dopt = getopt(argc,argv,"uhvqn:t:s:P:m:o:")) != EOF) { switch (dopt) { case 'u': case 'h': usage(); return 0; case 'v': verbose = 1; break; case 'q': verbose = 0; break; case 'n': num_phi = atoi(optarg); break; case 't': num_trials = atoi(optarg); break; case 's': SNRdB = atof(optarg); break; case 'P': phi_max_abs = atof(optarg); break; case 'm': ms = liquid_getopt_str2mod(optarg); if (ms == LIQUID_MODEM_UNKNOWN) { fprintf(stderr,"error: %s, unknown/unsupported modulation scheme \"%s\"\n", argv[0], optarg); return 1; } break; case 'o': strncpy(filename,optarg,255); break; default: exit(1); } } // validate input if (phi_max_abs <= 0.0f) { fprintf(stderr,"error: %s, maximum absolute phase offset must be greater than 0\n", argv[0]); exit(1); } else if (num_phi < 3) { fprintf(stderr,"error: %s, number of phase steps must be at least 3\n", argv[0]); exit(1); fprintf(stderr,"error: %s, number of trials must be greater than 0\n", argv[0]); exit(1); } unsigned int bps = modulation_types[ms].bps; // generate filenames if ( strcmp(filename,"")==0 ) sprintf(filename,"figures.gen/modem_phase_error_%s%u.dat", modulation_types[ms].name, 1<<bps); // derived values float phi_min = 0.0f; //phi_max_abs; float phi_max = phi_max_abs; float phi_step = (phi_max - phi_min) / (float)(num_phi-1); float nstd = powf(10.0f, -SNRdB/20.0f); // arrays float phi_hat_mean[num_phi]; // phase error estimate float phi_hat_mean_smooth[num_phi]; // phase error estimate (smoothed) // create modulator/demodulator modem mod = modem_create(ms); modem demod = modem_create(ms); unsigned int M = 1 << bps; // unsigned int i; unsigned int sym_in; unsigned int sym_out; unsigned int n=0; // trials counter float complex x; float phi_hat; float phi=0.0f; for (i=0; i<num_phi; i++) { phi = phi_min + i*phi_step; phi_hat_mean[i] = 0.0f; // reset number of trials n = 0; do { for (sym_in=0; sym_in<M; sym_in++) { // modulate modem_modulate(mod, sym_in, &x); // channel (phase offset) x *= cexpf(_Complex_I*phi); x += nstd * (randnf() + _Complex_I*randnf()) * M_SQRT1_2; // demodulate modem_demodulate(demod, x, &sym_out); // get error phi_hat = modem_get_demodulator_phase_error(demod); // accumulate average phi_hat_mean[i] += phi_hat; } n += M; } while (n < num_trials); // scale by bps^2 //phi_hat_mean[i] *= bps*bps; // normalize mean by number of trials phi_hat_mean[i] /= (float) (n); // print results if (verbose) printf("%6u / %6u : phi=%12.8f, phi-hat=%12.8f\n", i+1, num_phi, phi, phi_hat_mean[i]); } // compute smoothed curve float phi_hat_tmp[num_phi]; memmove(phi_hat_mean_smooth, phi_hat_mean, num_phi*sizeof(float)); unsigned int j; for (j=0; j<5; j++) { memmove(phi_hat_tmp, phi_hat_mean_smooth, num_phi*sizeof(float)); for (i=0; i<num_phi; i++) { if (i==0 || i == num_phi-1) { phi_hat_mean_smooth[i] = phi_hat_tmp[i]; } else { phi_hat_mean_smooth[i] = 0.20f*phi_hat_tmp[i-1] + 0.60f*phi_hat_tmp[i ] + 0.20f*phi_hat_tmp[i+1]; } } } // destroy objects modem_destroy(mod); modem_destroy(demod); // // export output file // FILE * fid = fopen(filename,"w"); if (!fid) { fprintf(stderr,"error: %s, cannot open '%s' for writing\n", argv[0], filename); exit(1); } fprintf(fid, "# %s : auto-generated file\n", filename); fprintf(fid, "# \n"); // low SNR fprintf(fid, "# %12s %12s %12s\n", "phi", "phi-hat", "phi-hat-smooth"); for (i=0; i<num_phi; i++) { phi = phi_min + i*phi_step; fprintf(fid," %12.8f %12.8f %12.8f\n", phi, phi_hat_mean[i], phi_hat_mean_smooth[i]); } fclose(fid); if (verbose) printf("results written to '%s'\n", filename); return 0; }