void gmskframesync_execute_rxpreamble(gmskframesync _q, float complex _x) { // validate input if (_q->preamble_counter == _q->preamble_len) { fprintf(stderr,"warning: gmskframesync_execute_rxpn(), p/n buffer already full!\n"); return; } // mix signal down float complex y; nco_crcf_mix_down(_q->nco_coarse, _x, &y); nco_crcf_step(_q->nco_coarse); // update instantanenous frequency estimate gmskframesync_update_fi(_q, y); // update symbol synchronizer float mf_out = 0.0f; int sample_available = gmskframesync_update_symsync(_q, _q->fi_hat, &mf_out); // compute output if timeout if (sample_available) { // save output in p/n symbols buffer _q->preamble_rx[ _q->preamble_counter ] = mf_out / (float)(_q->k); // update counter _q->preamble_counter++; if (_q->preamble_counter == _q->preamble_len) { gmskframesync_syncpn(_q); _q->state = STATE_RXHEADER; } } }
size_t demodulator_recv(demodulator *d, const sample_t *samples, size_t sample_len, float complex *symbols) { if (!d) { return 0; } if (sample_len % d->opt.samples_per_symbol != 0) { printf("must receive multiple of samples_per_symbol samples"); return 0; } float complex post_mixer[d->opt.samples_per_symbol]; size_t written = 0; for (size_t i = 0; i < sample_len; i += d->opt.samples_per_symbol) { for (size_t j = 0; j < d->opt.samples_per_symbol; j++) { nco_crcf_mix_down(d->nco, samples[i + j], &post_mixer[j]); nco_crcf_step(d->nco); } if (d->decim) { firdecim_crcf_execute(d->decim, &post_mixer[0], &symbols[(i / d->opt.samples_per_symbol)]); symbols[(i / d->opt.samples_per_symbol)] /= d->opt.samples_per_symbol; } else { symbols[i] = post_mixer[0]; } written++; } return written; }
// execute synchronizer, receiving p/n sequence // _q : frame synchronizer object // _x : input sample // _sym : demodulated symbol void flexframesync_execute_rxpn(flexframesync _q, float complex _x) { // validate input if (_q->pn_counter == 64) { fprintf(stderr,"warning: flexframesync_execute_rxpn(), p/n buffer already full!\n"); return; } // 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) { // save output in p/n symbols buffer _q->preamble_rx[ _q->pn_counter ] = mf_out; // update p/n counter _q->pn_counter++; if (_q->pn_counter == 64) { flexframesync_syncpn(_q); _q->state = STATE_RXHEADER; } } }
// execute framing synchronizer on input buffer // _q : framing synchronizer object // _buffer : input buffer [size: _n x 1] // _n : input buffer size void wlanframesync_execute(wlanframesync _q, liquid_float_complex * _buffer, unsigned int _n) { unsigned int i; float complex x; for (i=0; i<_n; i++) { x = _buffer[i]; // correct for carrier frequency offset (only if not in // initial 'seek PLCP' state) if (_q->state != WLANFRAMESYNC_STATE_SEEKPLCP) { nco_crcf_mix_down(_q->nco_rx, x, &x); nco_crcf_step(_q->nco_rx); } // save input sample to buffer windowcf_push(_q->input_buffer,x); #if DEBUG_WLANFRAMESYNC if (_q->debug_enabled) { // apply agc (estimate initial signal gain) float complex y; agc_crcf_execute(_q->agc_rx, x, &y); windowcf_push(_q->debug_x, x); windowf_push(_q->debug_rssi, agc_crcf_get_rssi(_q->agc_rx)); } #endif switch (_q->state) { case WLANFRAMESYNC_STATE_SEEKPLCP: wlanframesync_execute_seekplcp(_q); break; case WLANFRAMESYNC_STATE_RXSHORT0: wlanframesync_execute_rxshort0(_q); break; case WLANFRAMESYNC_STATE_RXSHORT1: wlanframesync_execute_rxshort1(_q); break; case WLANFRAMESYNC_STATE_RXLONG0: wlanframesync_execute_rxlong0(_q); break; case WLANFRAMESYNC_STATE_RXLONG1: wlanframesync_execute_rxlong1(_q); break; case WLANFRAMESYNC_STATE_RXSIGNAL: wlanframesync_execute_rxsignal(_q); break; case WLANFRAMESYNC_STATE_RXDATA: wlanframesync_execute_rxdata(_q); break; default:; // should never get to this point fprintf(stderr,"error: wlanframesync_execute(), invalid state\n"); exit(1); } } // for (i=0; i<_n; i++) }
void PfbSynthesizerComponent::process() { //Get input DataSets std::size_t curSize = 0; vector< DataSet< complex<float> >* > inSets(nChans_x); for(int i=0;i<nChans_x;i++) { stringstream ss; ss << "input"; ss << i; getInputDataSet(ss.str(), inSets[i]); std::size_t s = inSets[i]->data.size(); if(s != curSize && curSize > 0) LOG(LWARNING) << "Input channel sizes do not match."; curSize = s; } //Get output DataSet DataSet< complex<float> >* writeDataSet = NULL; getOutputDataSet("output1", writeDataSet, curSize*nChans_x); writeDataSet->sampleRate = inSets[0]->sampleRate*nChans_x; writeDataSet->timeStamp = inSets[0]->timeStamp; // Execute the synthesizer for(int i=0;i<curSize;i++) { for (int j=0; j<nChans_x; j++) buf[j] = inSets[j]->data[i]; firpfbch_crcf_synthesizer_execute(channelizer, &buf[0], &outBuf[0]); copy(outBuf.begin(), outBuf.end(), writeDataSet->data.begin()+i*nChans_x); } // mix signal down for(int i=0;i<writeDataSet->data.size();i++) { complex<float>* x = &writeDataSet->data[i]; nco_crcf_mix_down(nco, *x, x); nco_crcf_step(nco); } //Release the DataSets for(int i=0;i<nChans_x;i++) { stringstream ss; ss << "input"; ss << i; releaseInputDataSet(ss.str(), inSets[i]); } releaseOutputDataSet("output1", writeDataSet); }
// push buffered p/n sequence through synchronizer void gmskframesync_pushpn(gmskframesync _q) { unsigned int i; // reset filterbanks firpfb_rrrf_reset(_q->mf); firpfb_rrrf_reset(_q->dmf); // read buffer float complex * rc; windowcf_read(_q->buffer, &rc); // compute delay and filterbank index // tau_hat < 0 : delay = 2*k*m-1, index = round( tau_hat *npfb), flag = 0 // tau_hat > 0 : delay = 2*k*m-2, index = round((1-tau_hat)*npfb), flag = 0 assert(_q->tau_hat < 0.5f && _q->tau_hat > -0.5f); unsigned int delay = 2*_q->k*_q->m - 1; // samples to buffer before computing output _q->pfb_soft = -_q->tau_hat*_q->npfb; _q->pfb_index = (int) roundf(_q->pfb_soft); while (_q->pfb_index < 0) { delay -= 1; _q->pfb_index += _q->npfb; _q->pfb_soft += _q->npfb; } _q->pfb_timer = 0; // set coarse carrier frequency offset nco_crcf_set_frequency(_q->nco_coarse, _q->dphi_hat); unsigned int buffer_len = (_q->preamble_len + _q->m) * _q->k; for (i=0; i<buffer_len; i++) { if (i < delay) { float complex y; nco_crcf_mix_down(_q->nco_coarse, rc[i], &y); nco_crcf_step(_q->nco_coarse); // update instantanenous frequency estimate gmskframesync_update_fi(_q, y); // push initial samples into filterbanks firpfb_rrrf_push(_q->mf, _q->fi_hat); firpfb_rrrf_push(_q->dmf, _q->fi_hat); } else { // run remaining samples through p/n sequence recovery gmskframesync_execute_rxpreamble(_q, rc[i]); } } // set state (still need a few more samples before entire p/n // sequence has been received) _q->state = STATE_RXPREAMBLE; }
void ampmodem_demodulate(ampmodem _q, float complex _y, float *_x) { #if DEBUG_AMPMODEM windowcf_push(_q->debug_x, _y); #endif if (_q->suppressed_carrier) { // single side-band suppressed carrier if (_q->type != LIQUID_AMPMODEM_DSB) { *_x = crealf(_y); return; } // coherent demodulation // mix signal down float complex y_hat; nco_crcf_mix_down(_q->oscillator, _y, &y_hat); // compute phase error float phase_error = tanhf( crealf(y_hat) * cimagf(y_hat) ); #if DEBUG_AMPMODEM // compute frequency error float nco_freq = nco_crcf_get_frequency(_q->oscillator); float freq_error = nco_freq/(2*M_PI) - _q->fc/(2*M_PI); // retain phase and frequency errors windowf_push(_q->debug_phase_error, phase_error); windowf_push(_q->debug_freq_error, freq_error); #endif // adjust nco, pll objects nco_crcf_pll_step(_q->oscillator, phase_error); // step NCO nco_crcf_step(_q->oscillator); // set output *_x = crealf(y_hat); } else { // non-coherent demodulation (peak detector) float t = cabsf(_y); // remove DC bias _q->ssb_q_hat = ( _q->ssb_alpha)*t + (1 - _q->ssb_alpha)*_q->ssb_q_hat; *_x = 2.0f*(t - _q->ssb_q_hat); } }
void ofdmoqamframe64sync_execute(ofdmoqamframe64sync _q, float complex * _x, unsigned int _n) { unsigned int i; float complex x; for (i=0; i<_n; i++) { x = _x[i]; #if DEBUG_OFDMOQAMFRAME64SYNC windowcf_push(_q->debug_x,x); #endif _q->num_samples++; // coarse gain correction //x *= _q->g; // compensate for CFO nco_crcf_mix_down(_q->nco_rx, x, &x); // push sample into analysis filter banks float complex x_delay0; float complex x_delay1; windowcf_index(_q->input_buffer,0, &x_delay0); // full symbol delay windowcf_index(_q->input_buffer,32,&x_delay1); // half symbol delay windowcf_push(_q->input_buffer,x); firpfbch_analyzer_push(_q->ca0, x_delay0); // push input sample firpfbch_analyzer_push(_q->ca1, x_delay1); // push delayed sample switch (_q->state) { case OFDMOQAMFRAME64SYNC_STATE_PLCPSHORT: ofdmoqamframe64sync_execute_plcpshort(_q,x); break; case OFDMOQAMFRAME64SYNC_STATE_PLCPLONG0: ofdmoqamframe64sync_execute_plcplong0(_q,x); break; case OFDMOQAMFRAME64SYNC_STATE_PLCPLONG1: ofdmoqamframe64sync_execute_plcplong1(_q,x); break; case OFDMOQAMFRAME64SYNC_STATE_RXSYMBOLS: ofdmoqamframe64sync_execute_rxsymbols(_q,x); break; default:; } } // for (i=0; i<_n; i++) } // ofdmoqamframe64sync_execute()
void ofdmframesync_execute(ofdmframesync _q, float complex * _x, unsigned int _n) { unsigned int i; float complex x; for (i=0; i<_n; i++) { x = _x[i]; // correct for carrier frequency offset if (_q->state != OFDMFRAMESYNC_STATE_SEEKPLCP) { nco_crcf_mix_down(_q->nco_rx, x, &x); nco_crcf_step(_q->nco_rx); } // save input sample to buffer windowcf_push(_q->input_buffer,x); #if DEBUG_OFDMFRAMESYNC if (_q->debug_enabled) { windowcf_push(_q->debug_x, x); windowf_push(_q->debug_rssi, crealf(x)*crealf(x) + cimagf(x)*cimagf(x)); } #endif switch (_q->state) { case OFDMFRAMESYNC_STATE_SEEKPLCP: ofdmframesync_execute_seekplcp(_q); break; case OFDMFRAMESYNC_STATE_PLCPSHORT0: ofdmframesync_execute_S0a(_q); break; case OFDMFRAMESYNC_STATE_PLCPSHORT1: ofdmframesync_execute_S0b(_q); break; case OFDMFRAMESYNC_STATE_PLCPLONG: ofdmframesync_execute_S1(_q); break; case OFDMFRAMESYNC_STATE_RXSYMBOLS: ofdmframesync_execute_rxsymbols(_q); break; default:; } } // for (i=0; i<_n; i++) } // ofdmframesync_execute()
// once p/n symbols are buffered, estimate residual carrier // frequency and phase offsets, push through fine-tuned NCO void flexframesync_syncpn(flexframesync _q) { unsigned int i; // estimate residual carrier frequency offset from p/n symbols float complex dphi_metric = 0.0f; float complex r0 = 0.0f; float complex r1 = 0.0f; for (i=0; i<64; i++) { r0 = r1; r1 = _q->preamble_rx[i]*_q->preamble_pn[i]; dphi_metric += r1 * conjf(r0); } float dphi_hat = cargf(dphi_metric); // estimate carrier phase offset from p/n symbols float complex theta_metric = 0.0f; for (i=0; i<64; i++) theta_metric += _q->preamble_rx[i]*liquid_cexpjf(-dphi_hat*i)*_q->preamble_pn[i]; float theta_hat = cargf(theta_metric); // TODO: compute gain correction factor // initialize fine-tuned nco nco_crcf_set_frequency(_q->nco_fine, dphi_hat); nco_crcf_set_phase( _q->nco_fine, theta_hat); // correct for carrier offset, pushing through phase-locked loop for (i=0; i<64; i++) { // mix signal down nco_crcf_mix_down(_q->nco_fine, _q->preamble_rx[i], &_q->preamble_rx[i]); // push through phase-locked loop float phase_error = cimagf(_q->preamble_rx[i]*_q->preamble_pn[i]); nco_crcf_pll_step(_q->nco_fine, phase_error); #if DEBUG_FLEXFRAMESYNC //_q->debug_nco_phase[i] = nco_crcf_get_phase(_q->nco_fine); #endif // update nco phase nco_crcf_step(_q->nco_fine); } }
// step receiver mixer, matched filter, decimator // _q : frame synchronizer // _x : input sample // _y : output symbol int flexframesync_step(flexframesync _q, float complex _x, float complex * _y) { // mix sample down float complex v; nco_crcf_mix_down(_q->mixer, _x, &v); nco_crcf_step (_q->mixer); // push sample into filterbank firpfb_crcf_push (_q->mf, v); firpfb_crcf_execute(_q->mf, _q->pfb_index, &v); #if FLEXFRAMESYNC_ENABLE_EQ // push sample through equalizer eqlms_cccf_push(_q->equalizer, v); #endif // increment counter to determine if sample is available _q->mf_counter++; int sample_available = (_q->mf_counter >= 1) ? 1 : 0; // set output sample if available if (sample_available) { #if FLEXFRAMESYNC_ENABLE_EQ // compute equalizer output eqlms_cccf_execute(_q->equalizer, &v); #endif // set output *_y = v; // decrement counter by k=2 samples/symbol _q->mf_counter -= 2; } // return flag return sample_available; }
// // test nco mixing // void autotest_nco_mixing() { // frequency, phase float f = 0.1f; float phi = M_PI; // error tolerance (high for NCO) float tol = 0.05f; // initialize nco object nco_crcf p = nco_crcf_create(LIQUID_NCO); nco_crcf_set_frequency(p, f); nco_crcf_set_phase(p, phi); unsigned int i; float nco_i, nco_q; for (i=0; i<64; i++) { // generate sin/cos nco_crcf_sincos(p, &nco_q, &nco_i); // mix back to zero phase complex float nco_cplx_in = nco_i + _Complex_I*nco_q; complex float nco_cplx_out; nco_crcf_mix_down(p, nco_cplx_in, &nco_cplx_out); // assert mixer output is correct CONTEND_DELTA(crealf(nco_cplx_out), 1.0f, tol); CONTEND_DELTA(cimagf(nco_cplx_out), 0.0f, tol); //printf("%3u : %12.8f + j*%12.8f\n", i, crealf(nco_cplx_out), cimagf(nco_cplx_out)); // step nco nco_crcf_step(p); } // destroy NCO object nco_crcf_destroy(p); }
void ModemFMStereo::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitFMStereo *fmkit = (ModemKitFMStereo *)kit; size_t bufSize = input->data.size(); liquid_float_complex u, v, w, x, y; double audio_resample_ratio = fmkit->audioResampleRatio; if (demodOutputData.size() != bufSize) { if (demodOutputData.capacity() < bufSize) { demodOutputData.reserve(bufSize); } demodOutputData.resize(bufSize); } size_t audio_out_size = (size_t)ceil((double) (bufSize) * audio_resample_ratio) + 512; freqdem_demodulate_block(demodFM, &input->data[0], bufSize, &demodOutputData[0]); if (resampledOutputData.size() != audio_out_size) { if (resampledOutputData.capacity() < audio_out_size) { resampledOutputData.reserve(audio_out_size); } resampledOutputData.resize(audio_out_size); } unsigned int numAudioWritten; msresamp_rrrf_execute(fmkit->audioResampler, &demodOutputData[0], bufSize, &resampledOutputData[0], &numAudioWritten); if (demodStereoData.size() != bufSize) { if (demodStereoData.capacity() < bufSize) { demodStereoData.reserve(bufSize); } demodStereoData.resize(bufSize); } float phase_error = 0; for (size_t i = 0; i < bufSize; i++) { // real -> complex firhilbf_r2c_execute(fmkit->firStereoR2C, demodOutputData[i], &x); // 19khz pilot band-pass iirfilt_crcf_execute(fmkit->iirStereoPilot, x, &v); nco_crcf_cexpf(fmkit->stereoPilot, &w); w.imag = -w.imag; // conjf(w) // multiply u = v * conjf(w) u.real = v.real * w.real - v.imag * w.imag; u.imag = v.real * w.imag + v.imag * w.real; // cargf(u) phase_error = atan2f(u.imag,u.real); // step pll nco_crcf_pll_step(fmkit->stereoPilot, phase_error); nco_crcf_step(fmkit->stereoPilot); // 38khz down-mix nco_crcf_mix_down(fmkit->stereoPilot, x, &y); nco_crcf_mix_down(fmkit->stereoPilot, y, &x); // complex -> real firhilbf_c2r_execute(fmkit->firStereoC2R, x, &demodStereoData[i]); } // std::cout << "[PLL] phase error: " << phase_error; // std::cout << " freq:" << (((nco_crcf_get_frequency(stereoPilot) / (2.0 * M_PI)) * inp->sampleRate)) << std::endl; if (audio_out_size != resampledStereoData.size()) { if (resampledStereoData.capacity() < audio_out_size) { resampledStereoData.reserve(audio_out_size); } resampledStereoData.resize(audio_out_size); } msresamp_rrrf_execute(fmkit->stereoResampler, &demodStereoData[0], bufSize, &resampledStereoData[0], &numAudioWritten); audioOut->channels = 2; if (audioOut->data.capacity() < (numAudioWritten * 2)) { audioOut->data.reserve(numAudioWritten * 2); } audioOut->data.resize(numAudioWritten * 2); for (size_t i = 0; i < numAudioWritten; i++) { float l, r; float ld, rd; if (fmkit->demph) { iirfilt_rrrf_execute(fmkit->iirDemphL, 0.568f * (resampledOutputData[i] - (resampledStereoData[i])), &ld); iirfilt_rrrf_execute(fmkit->iirDemphR, 0.568f * (resampledOutputData[i] + (resampledStereoData[i])), &rd); firfilt_rrrf_push(fmkit->firStereoLeft, ld); firfilt_rrrf_execute(fmkit->firStereoLeft, &l); firfilt_rrrf_push(fmkit->firStereoRight, rd); firfilt_rrrf_execute(fmkit->firStereoRight, &r); } else { firfilt_rrrf_push(fmkit->firStereoLeft, 0.568f * (resampledOutputData[i] - (resampledStereoData[i]))); firfilt_rrrf_execute(fmkit->firStereoLeft, &l); firfilt_rrrf_push(fmkit->firStereoRight, 0.568f * (resampledOutputData[i] + (resampledStereoData[i]))); firfilt_rrrf_execute(fmkit->firStereoRight, &r); } audioOut->data[i * 2] = l; audioOut->data[i * 2 + 1] = r; } }
void gmskframesync_execute_rxpayload(gmskframesync _q, float complex _x) { // mix signal down float complex y; nco_crcf_mix_down(_q->nco_coarse, _x, &y); nco_crcf_step(_q->nco_coarse); // update instantanenous frequency estimate gmskframesync_update_fi(_q, y); // update symbol synchronizer float mf_out = 0.0f; int sample_available = gmskframesync_update_symsync(_q, _q->fi_hat, &mf_out); // compute output if timeout if (sample_available) { // demodulate unsigned char s = mf_out > 0.0f ? 1 : 0; // TODO: update evm // save payload _q->payload_byte <<= 1; _q->payload_byte |= s ? 0x01 : 0x00; _q->payload_enc[_q->payload_counter/8] = _q->payload_byte; // increment counter _q->payload_counter++; if (_q->payload_counter == 8*_q->payload_enc_len) { // decode payload _q->payload_valid = packetizer_decode(_q->p_payload, _q->payload_enc, _q->payload_dec); // invoke callback if (_q->callback != NULL) { // set framestats internals _q->framestats.rssi = 20*log10f(_q->gamma_hat); _q->framestats.evm = 0.0f; _q->framestats.framesyms = NULL; _q->framestats.num_framesyms = 0; _q->framestats.mod_scheme = LIQUID_MODEM_UNKNOWN; _q->framestats.mod_bps = 1; _q->framestats.check = _q->check; _q->framestats.fec0 = _q->fec0; _q->framestats.fec1 = _q->fec1; // invoke callback method _q->callback(_q->header_dec, _q->header_valid, _q->payload_dec, _q->payload_dec_len, _q->payload_valid, _q->framestats, _q->userdata); } // reset frame synchronizer gmskframesync_reset(_q); } } }
void gmskframesync_execute_rxheader(gmskframesync _q, float complex _x) { // mix signal down float complex y; nco_crcf_mix_down(_q->nco_coarse, _x, &y); nco_crcf_step(_q->nco_coarse); // update instantanenous frequency estimate gmskframesync_update_fi(_q, y); // update symbol synchronizer float mf_out = 0.0f; int sample_available = gmskframesync_update_symsync(_q, _q->fi_hat, &mf_out); // compute output if timeout if (sample_available) { // demodulate unsigned char s = mf_out > 0.0f ? 1 : 0; // TODO: update evm // save bit in buffer _q->header_mod[_q->header_counter] = s; // increment header counter _q->header_counter++; if (_q->header_counter == GMSKFRAME_H_SYM) { // decode header gmskframesync_decode_header(_q); // invoke callback if header is invalid if (!_q->header_valid && _q->callback != NULL) { // set framestats internals _q->framestats.rssi = 20*log10f(_q->gamma_hat); _q->framestats.evm = 0.0f; _q->framestats.framesyms = NULL; _q->framestats.num_framesyms = 0; _q->framestats.mod_scheme = LIQUID_MODEM_UNKNOWN; _q->framestats.mod_bps = 1; _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_dec, _q->header_valid, NULL, 0, 0, _q->framestats, _q->userdata); gmskframesync_reset(_q); } // reset if invalid if (!_q->header_valid) { gmskframesync_reset(_q); return; } // update state _q->state = STATE_RXPAYLOAD; } } }
// 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[]) { // 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; }
// 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; } } }
// 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)); // 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) ); // 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; }
int main() { // spectral periodogram options unsigned int nfft = 1200; // spectral periodogram FFT size unsigned int num_samples = 64000; // number of samples float fc = 0.20f; // carrier (relative to sampling rate) // create objects iirfilt_crcf filter_tx = iirfilt_crcf_create_lowpass(15, 0.05); nco_crcf mixer_tx = nco_crcf_create(LIQUID_VCO); nco_crcf mixer_rx = nco_crcf_create(LIQUID_VCO); iirfilt_crcf filter_rx = iirfilt_crcf_create_lowpass(7, 0.2); // set carrier frequencies nco_crcf_set_frequency(mixer_tx, fc * 2*M_PI); nco_crcf_set_frequency(mixer_rx, fc * 2*M_PI); // create objects for measuring power spectral density spgramcf spgram_tx = spgramcf_create_default(nfft); spgramf spgram_dac = spgramf_create_default(nfft); spgramcf spgram_rx = spgramcf_create_default(nfft); // run through loop one step at a time unsigned int i; for (i=0; i<num_samples; i++) { // STEP 1: generate input signal (filtered noise with offset tone) float complex v1 = (randnf() + randnf()*_Complex_I) + 3.0f*cexpf(-_Complex_I*0.2f*i); iirfilt_crcf_execute(filter_tx, v1, &v1); // save spectrum spgramcf_push(spgram_tx, v1); // STEP 2: mix signal up and save real part (DAC output) nco_crcf_mix_up(mixer_tx, v1, &v1); float v2 = crealf(v1); nco_crcf_step(mixer_tx); // save spectrum spgramf_push(spgram_dac, v2); // STEP 3: mix signal down and filter off image float complex v3; nco_crcf_mix_down(mixer_rx, v2, &v3); iirfilt_crcf_execute(filter_rx, v3, &v3); nco_crcf_step(mixer_rx); // save spectrum spgramcf_push(spgram_rx, v3); } // compute power spectral density output float psd_tx [nfft]; float psd_dac [nfft]; float psd_rx [nfft]; spgramcf_get_psd(spgram_tx, psd_tx); spgramf_get_psd( spgram_dac, psd_dac); spgramcf_get_psd(spgram_rx, psd_rx); // destroy objects spgramcf_destroy(spgram_tx); spgramf_destroy(spgram_dac); spgramcf_destroy(spgram_rx); iirfilt_crcf_destroy(filter_tx); nco_crcf_destroy(mixer_tx); nco_crcf_destroy(mixer_rx); iirfilt_crcf_destroy(filter_rx); // // export output file // 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\n"); fprintf(fid,"nfft = %u;\n", nfft); fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n"); fprintf(fid,"psd_tx = zeros(1,nfft);\n"); fprintf(fid,"psd_dac= zeros(1,nfft);\n"); fprintf(fid,"psd_rx = zeros(1,nfft);\n"); for (i=0; i<nfft; i++) { fprintf(fid,"psd_tx (%6u) = %12.4e;\n", i+1, psd_tx [i]); fprintf(fid,"psd_dac(%6u) = %12.4e;\n", i+1, psd_dac[i]); fprintf(fid,"psd_rx (%6u) = %12.4e;\n", i+1, psd_rx [i]); } fprintf(fid,"figure;\n"); fprintf(fid,"hold on;\n"); fprintf(fid," plot(f, psd_tx, '-', 'LineWidth',1.5,'Color',[0.7 0.7 0.7]);\n"); fprintf(fid," plot(f, psd_dac, '-', 'LineWidth',1.5,'Color',[0.0 0.5 0.3]);\n"); fprintf(fid," plot(f, psd_rx, '-', 'LineWidth',1.5,'Color',[0.0 0.3 0.5]);\n"); fprintf(fid,"hold off;\n"); fprintf(fid,"xlabel('Normalized Frequency [f/F_s]');\n"); fprintf(fid,"ylabel('Power Spectral Density [dB]');\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"axis([-0.5 0.5 -100 60]);\n"); fprintf(fid,"legend('transmit (complex)','DAC output (real)','receive (complex)','location','northeast');\n"); fclose(fid); printf("results written to %s.\n", OUTPUT_FILENAME); printf("done.\n"); return 0; }