void ofdmoqamframe64sync_execute_plcpshort(ofdmoqamframe64sync _q, float complex _x) { // run AGC, clip output float complex y; agc_crcf_execute(_q->sigdet, _x, &y); //if (agc_crcf_get_signal_level(_q->sigdet) < -15.0f) // return; if (cabsf(y) > 2.0f) y = 2.0f*liquid_cexpjf(cargf(y)); // auto-correlators //autocorr_cccf_push(_q->autocorr, _x); autocorr_cccf_push(_q->autocorr, y); autocorr_cccf_execute(_q->autocorr, &_q->rxx); #if DEBUG_OFDMOQAMFRAME64SYNC windowcf_push(_q->debug_rxx, _q->rxx); windowf_push(_q->debug_rssi, agc_crcf_get_signal_level(_q->sigdet)); #endif float rxx_mag = cabsf(_q->rxx); float threshold = (_q->rxx_thresh)*(_q->autocorr_length); if (rxx_mag > threshold) { // wait for auto-correlation to peak before changing state if (rxx_mag > _q->rxx_mag_max) { _q->rxx_mag_max = rxx_mag; return; } // estimate CFO _q->nu_hat = cargf(_q->rxx); if (_q->nu_hat > M_PI/2.0f) _q->nu_hat -= M_PI; if (_q->nu_hat < -M_PI/2.0f) _q->nu_hat += M_PI; _q->nu_hat *= 4.0f / (float)(_q->num_subcarriers); #if DEBUG_OFDMOQAMFRAME64SYNC_PRINT printf("rxx = |%12.8f| arg{%12.8f}\n", cabsf(_q->rxx),cargf(_q->rxx)); printf("nu_hat = %12.8f\n", _q->nu_hat); #endif nco_crcf_set_frequency(_q->nco_rx, _q->nu_hat); _q->state = OFDMOQAMFRAME64SYNC_STATE_PLCPLONG0; _q->g = agc_crcf_get_gain(_q->sigdet); #if DEBUG_OFDMOQAMFRAME64SYNC_PRINT printf("gain : %f\n", _q->g); #endif _q->timer=0; } }
// modulate sample // _q : frequency modulator object // _s : input symbol // _y : output sample array [size: _k x 1] void cpfskmod_modulate(cpfskmod _q, unsigned int _s, float complex * _y) { // run interpolator float v = 2.0f*_s - (float)(_q->M) + 1.0f; firinterp_rrrf_execute(_q->interp, v, _q->phase_interp); // integrate phase state unsigned int i; float theta; for (i=0; i<_q->k; i++) { // push phase through integrator iirfilt_rrrf_execute(_q->integrator, _q->phase_interp[i], &theta); // compute output _y[i] = liquid_cexpjf(theta); } }
// 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); } }
// recover symbol, correcting for gain, pilot phase, etc. void ofdmframesync_rxsymbol(ofdmframesync _q) { // apply gain unsigned int i; for (i=0; i<_q->M; i++) _q->X[i] *= _q->R[i]; // polynomial curve-fit float x_phase[_q->M_pilot]; float y_phase[_q->M_pilot]; float p_phase[2]; unsigned int n=0; unsigned int k; float complex pilot = 1.0f; for (i=0; i<_q->M; i++) { // start at mid-point (effective fftshift) k = (i + _q->M2) % _q->M; if (_q->p[k]==OFDMFRAME_SCTYPE_PILOT) { if (n == _q->M_pilot) { fprintf(stderr,"warning: ofdmframesync_rxsymbol(), pilot subcarrier mismatch\n"); return; } pilot = (msequence_advance(_q->ms_pilot) ? 1.0f : -1.0f); #if 0 printf("pilot[%3u] = %12.4e + j*%12.4e (expected %12.4e + j*%12.4e)\n", k, crealf(_q->X[k]), cimagf(_q->X[k]), crealf(pilot), cimagf(pilot)); #endif // store resulting... x_phase[n] = (k > _q->M2) ? (float)k - (float)(_q->M) : (float)k; y_phase[n] = cargf(_q->X[k]*conjf(pilot)); // update counter n++; } } if (n != _q->M_pilot) { fprintf(stderr,"warning: ofdmframesync_rxsymbol(), pilot subcarrier mismatch\n"); return; } // try to unwrap phase for (i=1; i<_q->M_pilot; i++) { while ((y_phase[i] - y_phase[i-1]) > M_PI) y_phase[i] -= 2*M_PI; while ((y_phase[i] - y_phase[i-1]) < -M_PI) y_phase[i] += 2*M_PI; } // fit phase to 1st-order polynomial (2 coefficients) polyf_fit(x_phase, y_phase, _q->M_pilot, p_phase, 2); // filter slope estimate (timing offset) float alpha = 0.3f; p_phase[1] = alpha*p_phase[1] + (1-alpha)*_q->p1_prime; _q->p1_prime = p_phase[1]; #if DEBUG_OFDMFRAMESYNC if (_q->debug_enabled) { // save pilots memmove(_q->px, x_phase, _q->M_pilot*sizeof(float)); memmove(_q->py, y_phase, _q->M_pilot*sizeof(float)); // NOTE : swapping values for octave _q->p_phase[0] = p_phase[1]; _q->p_phase[1] = p_phase[0]; windowf_push(_q->debug_pilot_0, p_phase[0]); windowf_push(_q->debug_pilot_1, p_phase[1]); } #endif // compensate for phase offset // TODO : find more computationally efficient way to do this for (i=0; i<_q->M; i++) { // only apply to data/pilot subcarriers if (_q->p[i] == OFDMFRAME_SCTYPE_NULL) { _q->X[i] = 0.0f; } else { float fx = (i > _q->M2) ? (float)i - (float)(_q->M) : (float)i; float theta = polyf_val(p_phase, 2, fx); _q->X[i] *= liquid_cexpjf(-theta); } } // adjust NCO frequency based on differential phase if (_q->num_symbols > 0) { // compute phase error (unwrapped) float dphi_prime = p_phase[0] - _q->phi_prime; while (dphi_prime > M_PI) dphi_prime -= M_2_PI; while (dphi_prime < -M_PI) dphi_prime += M_2_PI; // adjust NCO proportionally to phase error nco_crcf_adjust_frequency(_q->nco_rx, 1e-3f*dphi_prime); } // set internal phase state _q->phi_prime = p_phase[0]; //printf("%3u : theta : %12.8f, nco freq: %12.8f\n", _q->num_symbols, p_phase[0], nco_crcf_get_frequency(_q->nco_rx)); // increment symbol counter _q->num_symbols++; #if 0 for (i=0; i<_q->M_pilot; i++) printf("x_phase(%3u) = %12.8f; y_phase(%3u) = %12.8f;\n", i+1, x_phase[i], i+1, y_phase[i]); printf("poly : p0=%12.8f, p1=%12.8f\n", p_phase[0], p_phase[1]); #endif }
// estimate complex equalizer gain from G0 and G1 using polynomial fit // _q : ofdmframesync object // _order : polynomial order void ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, unsigned int _order) { #if DEBUG_OFDMFRAMESYNC if (_q->debug_enabled) { // copy pre-smoothed gain memmove(_q->G_hat, _q->G, _q->M*sizeof(float complex)); } #endif // polynomial interpolation unsigned int i; unsigned int N = _q->M_pilot + _q->M_data; if (_order > N-1) _order = N-1; if (_order > 10) _order = 10; float x_freq[N]; float y_abs[N]; float y_arg[N]; float p_abs[_order+1]; float p_arg[_order+1]; unsigned int n=0; unsigned int k; for (i=0; i<_q->M; i++) { // start at mid-point (effective fftshift) k = (i + _q->M2) % _q->M; if (_q->p[k] != OFDMFRAME_SCTYPE_NULL) { if (n == N) { fprintf(stderr, "error: ofdmframesync_estimate_eqgain_poly(), pilot subcarrier mismatch\n"); exit(1); } // store resulting... x_freq[n] = (k > _q->M2) ? (float)k - (float)(_q->M) : (float)k; x_freq[n] = x_freq[n] / (float)(_q->M); y_abs[n] = cabsf(_q->G[k]); y_arg[n] = cargf(_q->G[k]); // update counter n++; } } if (n != N) { fprintf(stderr, "error: ofdmframesync_estimate_eqgain_poly(), pilot subcarrier mismatch\n"); exit(1); } // try to unwrap phase for (i=1; i<N; i++) { while ((y_arg[i] - y_arg[i-1]) > M_PI) y_arg[i] -= 2*M_PI; while ((y_arg[i] - y_arg[i-1]) < -M_PI) y_arg[i] += 2*M_PI; } // fit to polynomial polyf_fit(x_freq, y_abs, N, p_abs, _order+1); polyf_fit(x_freq, y_arg, N, p_arg, _order+1); // compute subcarrier gain for (i=0; i<_q->M; i++) { float freq = (i > _q->M2) ? (float)i - (float)(_q->M) : (float)i; freq = freq / (float)(_q->M); float A = polyf_val(p_abs, _order+1, freq); float theta = polyf_val(p_arg, _order+1, freq); _q->G[i] = (_q->p[i] == OFDMFRAME_SCTYPE_NULL) ? 0.0f : A * liquid_cexpjf(theta); } #if 0 for (i=0; i<N; i++) printf("x(%3u) = %12.8f; y_abs(%3u) = %12.8f; y_arg(%3u) = %12.8f;\n", i+1, x_freq[i], i+1, y_abs[i], i+1, y_arg[i]); for (i=0; i<=_order; i++) printf("p_abs(%3u) = %12.8f;\n", i+1, p_abs[i]); for (i=0; i<=_order; i++) printf("p_arg(%3u) = %12.8f;\n", i+1, p_arg[i]); #endif }
void ofdmframesync_execute_S1(ofdmframesync _q) { _q->timer--; if (_q->timer > 0) return; // increment number of symbols observed _q->num_symbols++; // run fft float complex * rc; windowcf_read(_q->input_buffer, &rc); // estimate S1 gain // TODO : add backoff in gain estimation ofdmframesync_estimate_gain_S1(_q, &rc[_q->cp_len], _q->G); // compute detector output float complex g_hat = 0.0f; unsigned int i; for (i=0; i<_q->M; i++) { //g_hat += _q->G[(i+1+_q->M)%_q->M]*conjf(_q->G[(i+_q->M)%_q->M]); g_hat += _q->G[(i+1)%_q->M]*conjf(_q->G[i]); } g_hat /= _q->M_S1; // normalize output g_hat *= _q->g0; // rotate by complex phasor relative to timing backoff g_hat *= liquid_cexpjf((float)(_q->backoff)*2.0f*M_PI/(float)(_q->M)); #if DEBUG_OFDMFRAMESYNC_PRINT printf(" g_hat : %12.4f <%12.8f>\n", cabsf(g_hat), cargf(g_hat)); #endif // check conditions for g_hat: // 1. magnitude should be large (near unity) when aligned // 2. phase should be very near zero (time aligned) if (cabsf(g_hat) > _q->plcp_sync_thresh && fabsf(cargf(g_hat)) < 0.1f*M_PI ) { //printf(" acquisition\n"); _q->state = OFDMFRAMESYNC_STATE_RXSYMBOLS; // reset timer _q->timer = _q->M + _q->cp_len + _q->backoff; _q->num_symbols = 0; // normalize gain by subcarriers, apply timing backoff correction float g = (float)(_q->M) / sqrtf(_q->M_pilot + _q->M_data); for (i=0; i<_q->M; i++) { _q->G[i] *= g; // gain due to relative subcarrier allocation _q->G[i] *= _q->B[i]; // timing backoff correction } #if 0 // TODO : choose number of taps more appropriately //unsigned int ntaps = _q->M / 4; unsigned int ntaps = (_q->M < 8) ? 2 : 8; // FIXME : this is by far the most computationally complex part of synchronization ofdmframesync_estimate_eqgain(_q, ntaps); #else unsigned int poly_order = 4; if (poly_order >= _q->M_pilot + _q->M_data) poly_order = _q->M_pilot + _q->M_data - 1; ofdmframesync_estimate_eqgain_poly(_q, poly_order); #endif #if 1 // compute composite gain unsigned int i; for (i=0; i<_q->M; i++) _q->R[i] = _q->B[i] / _q->G[i]; #endif return; #if 0 printf("exiting prematurely\n"); ofdmframesync_destroy(_q); exit(1); #endif } // check if we are stuck searching for the S1 symbol if (_q->num_symbols == 16) { #if DEBUG_OFDMFRAMESYNC_PRINT printf("could not find S1 symbol. bailing...\n"); #endif ofdmframesync_reset(_q); } // 'reset' timer (wait another half symbol) _q->timer = _q->M2; }
// create OFDM framing synchronizer object // _M : number of subcarriers, >10 typical // _cp_len : cyclic prefix length // _taper_len : taper length (OFDM symbol overlap) // _p : subcarrier allocation (null, pilot, data), [size: _M x 1] // _callback : user-defined callback function // _userdata : user-defined data pointer ofdmframesync ofdmframesync_create(unsigned int _M, unsigned int _cp_len, unsigned int _taper_len, unsigned char * _p, ofdmframesync_callback _callback, void * _userdata) { ofdmframesync q = (ofdmframesync) malloc(sizeof(struct ofdmframesync_s)); // validate input if (_M < 8) { fprintf(stderr,"warning: ofdmframesync_create(), less than 8 subcarriers\n"); } else if (_M % 2) { fprintf(stderr,"error: ofdmframesync_create(), number of subcarriers must be even\n"); exit(1); } else if (_cp_len > _M) { fprintf(stderr,"error: ofdmframesync_create(), cyclic prefix length cannot exceed number of subcarriers\n"); exit(1); } q->M = _M; q->cp_len = _cp_len; // derived values q->M2 = _M/2; // subcarrier allocation q->p = (unsigned char*) malloc((q->M)*sizeof(unsigned char)); if (_p == NULL) { ofdmframe_init_default_sctype(q->M, q->p); } else { memmove(q->p, _p, q->M*sizeof(unsigned char)); } // validate and count subcarrier allocation ofdmframe_validate_sctype(q->p, q->M, &q->M_null, &q->M_pilot, &q->M_data); if ( (q->M_pilot + q->M_data) == 0) { fprintf(stderr,"error: ofdmframesync_create(), must have at least one enabled subcarrier\n"); exit(1); } else if (q->M_data == 0) { fprintf(stderr,"error: ofdmframesync_create(), must have at least one data subcarriers\n"); exit(1); } else if (q->M_pilot < 2) { fprintf(stderr,"error: ofdmframesync_create(), must have at least two pilot subcarriers\n"); exit(1); } // create transform object q->X = (float complex*) malloc((q->M)*sizeof(float complex)); q->x = (float complex*) malloc((q->M)*sizeof(float complex)); q->fft = FFT_CREATE_PLAN(q->M, q->x, q->X, FFT_DIR_FORWARD, FFT_METHOD); // create input buffer the length of the transform q->input_buffer = windowcf_create(q->M + q->cp_len); // allocate memory for PLCP arrays q->S0 = (float complex*) malloc((q->M)*sizeof(float complex)); q->s0 = (float complex*) malloc((q->M)*sizeof(float complex)); q->S1 = (float complex*) malloc((q->M)*sizeof(float complex)); q->s1 = (float complex*) malloc((q->M)*sizeof(float complex)); ofdmframe_init_S0(q->p, q->M, q->S0, q->s0, &q->M_S0); ofdmframe_init_S1(q->p, q->M, q->S1, q->s1, &q->M_S1); // compute scaling factor q->g_data = sqrtf(q->M) / sqrtf(q->M_pilot + q->M_data); q->g_S0 = sqrtf(q->M) / sqrtf(q->M_S0); q->g_S1 = sqrtf(q->M) / sqrtf(q->M_S1); // gain q->g0 = 1.0f; q->G0 = (float complex*) malloc((q->M)*sizeof(float complex)); q->G1 = (float complex*) malloc((q->M)*sizeof(float complex)); q->G = (float complex*) malloc((q->M)*sizeof(float complex)); q->B = (float complex*) malloc((q->M)*sizeof(float complex)); q->R = (float complex*) malloc((q->M)*sizeof(float complex)); #if 1 memset(q->G0, 0x00, q->M*sizeof(float complex)); memset(q->G1, 0x00, q->M*sizeof(float complex)); memset(q->G , 0x00, q->M*sizeof(float complex)); memset(q->B, 0x00, q->M*sizeof(float complex)); #endif // timing backoff q->backoff = q->cp_len < 2 ? q->cp_len : 2; float phi = (float)(q->backoff)*2.0f*M_PI/(float)(q->M); unsigned int i; for (i=0; i<q->M; i++) q->B[i] = liquid_cexpjf(i*phi); // set callback data q->callback = _callback; q->userdata = _userdata; // // synchronizer objects // // numerically-controlled oscillator q->nco_rx = nco_crcf_create(LIQUID_NCO); // set pilot sequence q->ms_pilot = msequence_create_default(8); #if OFDMFRAMESYNC_ENABLE_SQUELCH // coarse detection q->squelch_threshold = -25.0f; q->squelch_enabled = 0; #endif // reset object ofdmframesync_reset(q); #if DEBUG_OFDMFRAMESYNC q->debug_enabled = 0; q->debug_objects_created = 0; q->debug_x = NULL; q->debug_rssi = NULL; q->debug_framesyms =NULL; q->G_hat = NULL; q->px = NULL; q->py = NULL; q->debug_pilot_0 = NULL; q->debug_pilot_1 = NULL; #endif // return object return q; }
void ofdmoqamframe64sync_rxpayload(ofdmoqamframe64sync _q, float complex * _Y0, float complex * _Y1) { unsigned int j=0; unsigned int t=0; int sctype; unsigned int pilot_phase = msequence_advance(_q->ms_pilot); // gain correction (equalizer) unsigned int i; for (i=0; i<_q->num_subcarriers; i++) { _Y0[i] *= _q->G[i];// * _q->zeta; _Y1[i] *= _q->G[i];// * _q->zeta; } // TODO : extract pilots for (i=0; i<_q->num_subcarriers; i++) { sctype = ofdmoqamframe64_getsctype(i); if (sctype==OFDMOQAMFRAME64_SCTYPE_PILOT) { // pilot subcarrier : use p/n sequence for pilot phase float complex y0 = ((i%2)==0 ? _Y0[i] : _Y1[i]) / _q->zeta; float complex y1 = ((i%2)==0 ? _Y1[i] : _Y0[i]) / _q->zeta; float complex p = (pilot_phase ? 1.0f : -1.0f); float phi_hat = ofdmoqamframe64sync_estimate_pilot_phase(y0,y1,p); _q->y_phase[t] = phi_hat; t++; #if DEBUG_OFDMOQAMFRAME64SYNC_PRINT printf("i = %u\n", i); printf("y0 = %12.8f + j*%12.8f;\n", crealf(y0),cimagf(y0)); printf("y1 = %12.8f + j*%12.8f;\n", crealf(y1),cimagf(y1)); printf("phi_hat = %12.8f\n", phi_hat); #endif /* printf("exiting prematurely\n"); ofdmoqamframe64sync_destroy(_q); exit(1); */ } } assert(t==4); // pilot phase correction /* _q->y_phase[0] = cargf(_q->X[11]); // -21 _q->y_phase[1] = cargf(_q->X[25]); // -7 _q->y_phase[2] = cargf(_q->X[39]); // 7 _q->y_phase[3] = cargf(_q->X[53]); // 21 */ // try to unwrap phase for (i=1; i<4; i++) { while ((_q->y_phase[i] - _q->y_phase[i-1]) > M_PI) _q->y_phase[i] -= 2*M_PI; while ((_q->y_phase[i] - _q->y_phase[i-1]) < -M_PI) _q->y_phase[i] += 2*M_PI; } // fit phase to 1st-order polynomial (2 coefficients) polyf_fit(_q->x_phase, _q->y_phase, 4, _q->p_phase, 2); //nco_crcf_step(_q->nco_pilot); float theta_hat = nco_crcf_get_phase(_q->nco_pilot); float phase_error = _q->p_phase[0] - theta_hat; while (phase_error > M_PI) phase_error -= 2.0f*M_PI; while (phase_error < -M_PI) phase_error += 2.0f*M_PI; pll_step(_q->pll_pilot, _q->nco_pilot, 0.1f*phase_error); #if DEBUG_OFDMOQAMFRAME64SYNC_PRINT // print phase results for (i=0; i<4; i++) { printf("x(%3u) = %12.8f; y(%3u) = %12.8f;\n", i+1, _q->x_phase[i], i+1, _q->y_phase[i]); } printf("p(1) = %12.8f\n", _q->p_phase[0]); printf("p(2) = %12.8f\n", _q->p_phase[1]); #endif #if DEBUG_OFDMOQAMFRAME64SYNC windowf_push(_q->debug_pilotphase, _q->p_phase[0]); windowf_push(_q->debug_pilotphase_hat, theta_hat); #endif // compensate for phase shift due to timing offset float theta; _q->p_phase[0] = theta_hat; _q->p_phase[1] = 0.0f; for (i=0; i<_q->num_subcarriers; i++) { // TODO : compute phase for delayed symbol (different from non-delayed symbol) theta = polyf_val(_q->p_phase, 2, (float)(i)-32.0f); _Y0[i] *= liquid_cexpjf(-theta); _Y1[i] *= liquid_cexpjf(-theta); } nco_crcf_step(_q->nco_pilot); // strip data subcarriers for (i=0; i<_q->num_subcarriers; i++) { sctype = ofdmoqamframe64_getsctype(i); if (sctype==OFDMOQAMFRAME64_SCTYPE_NULL) { // disabled subcarrier } else if (sctype==OFDMOQAMFRAME64_SCTYPE_PILOT) { // pilot subcarrier } else { // data subcarrier if ((i%2)==0) { // even subcarrier _q->data[j] = crealf(_Y0[i]) + cimagf(_Y1[i]) * _Complex_I; } else { // odd subcarrier _q->data[j] = cimagf(_Y0[i]) * _Complex_I + crealf(_Y1[i]); } // scaling factor _q->data[j] /= _q->zeta; j++; } } assert(j==48); #if DEBUG_OFDMOQAMFRAME64SYNC for (i=0; i<48; i++) windowcf_push(_q->debug_framesyms,_q->data[i]); #endif if (_q->callback != NULL) { int retval = _q->callback(_q->data, _q->userdata); if (retval == -1) { printf("exiting prematurely\n"); ofdmoqamframe64sync_destroy(_q); exit(0); } else if (retval == 1) { #if DEBUG_OFDMOQAMFRAME64SYNC_PRINT printf("resetting synchronizer\n"); #endif ofdmoqamframe64sync_reset(_q); } else { // do nothing } } }
if (q->m >= 3) MODEM(_demodsoft_gentab)(q, 2); return q; } // modulate PSK void MODEM(_modulate_psk)(MODEM() _q, unsigned int _sym_in, TC * _y) { // 'encode' input symbol (actually gray decoding) _sym_in = gray_decode(_sym_in); // compute output sample *_y = liquid_cexpjf(_sym_in * 2 * _q->data.psk.alpha ); } // demodulate PSK void MODEM(_demodulate_psk)(MODEM() _q, TC _x, unsigned int * _sym_out) { // compute angle and subtract phase offset, ensuring phase is in [-pi,pi) T theta = cargf(_x); theta -= _q->data.psk.d_phi; if (theta < -M_PI) theta += 2*M_PI; // demodulate on linearly-spaced array unsigned int s; // demodulated symbol