From 6de0f000bac3b193fd05770bf726a25c7800d48a Mon Sep 17 00:00:00 2001 From: okcsampson Date: Wed, 14 Jun 2017 17:40:08 +0000 Subject: [PATCH] Add demod candidate to ofdm.c (untested) git-svn-id: https://svn.code.sf.net/p/freetel/code@3191 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/src/codec2_ofdm.h | 2 +- codec2-dev/src/ofdm.c | 256 ++++++++++++++++++++++++++++++++- codec2-dev/src/ofdm_internal.h | 4 +- 3 files changed, 257 insertions(+), 5 deletions(-) diff --git a/codec2-dev/src/codec2_ofdm.h b/codec2-dev/src/codec2_ofdm.h index 8484cd88..f5d9700f 100644 --- a/codec2-dev/src/codec2_ofdm.h +++ b/codec2-dev/src/codec2_ofdm.h @@ -4,7 +4,7 @@ AUTHORS.....: David Rowe & Steve Sampson DATE CREATED: June 2017 - External user references to the modem library. + External user references to the modem library. \*---------------------------------------------------------------------------*/ diff --git a/codec2-dev/src/ofdm.c b/codec2-dev/src/ofdm.c index 420cb942..f9ae1c12 100644 --- a/codec2-dev/src/ofdm.c +++ b/codec2-dev/src/ofdm.c @@ -49,6 +49,11 @@ static void qpsk_demod(complex float, int *); static void ofdm_txframe(struct OFDM *, complex float [OFDM_SAMPLESPERFRAME], complex float *); static int coarse_sync(struct OFDM *, complex float *, int); +/* Defines */ + +#define max( a, b ) ( ((a) > (b)) ? (a) : (b) ) +#define min( a, b ) ( ((a) < (b)) ? (a) : (b) ) + /* Constants */ /* @@ -403,7 +408,254 @@ void ofdm_mod(struct OFDM *ofdm, COMP result[OFDM_SAMPLESPERFRAME], const int *t } } -int *ofdm_demod(struct OFDM *ofdm, COMP *rxbuf_in) { -/* TODO */ +/* + * ------------------------------------------ + * ofdm_demod - Demodulates one frame of bits + * ------------------------------------------ + * + * For phase estimation we need to maintain buffer of 3 frames plus + * one pilot, so we have 4 pilots total. '^' is the start of current + * frame that we are demodulating. + * + * P DDD P DDD P DDD P + * ^ + * + * Then add one symbol either side to account for movement in + * sampling instant due to sample clock differences: + * + * D P DDD P DDD P DDD P D + * ^ + */ + +// UNTESTED + +void ofdm_demod(struct OFDM *ofdm, int *rx_bits, COMP *rxbuf_in) { + complex float acarrier[OFDM_NC + 2]; + complex float aphase_est_pilot_rect; + float aphase_est_pilot[OFDM_NC + 2]; + float aamp_est_pilot[OFDM_NC + 2]; + float freq_err_hz; + int i, j, k, rr, st, en, ft_est; + + /* shift the buffer left based on nin */ + + for (i = 0, j = ofdm->nin; i < (OFDM_RXBUF - ofdm->nin); i++, j++) { + ofdm->rxbuf[i] = ofdm->rxbuf[j]; + } + + /* insert latest input samples onto tail of rxbuf */ + + for (i = (OFDM_RXBUF - ofdm->nin), j = 0; i < OFDM_RXBUF; i++, j++) { + ofdm->rxbuf[i] = rxbuf_in[j].real + rxbuf_in[j].imag * I; + } + + /* + * get latest freq offset estimate + * + * ofdm->foff_est_hz will be 0.0 unless + * ofdm->foff_est_en is enabled + */ + + float woff_est = TAU * ofdm->foff_est_hz / OFDM_FS; + + /* update timing estimate -------------------------------------------------- */ + + if (ofdm->timing_en == true) { + /* update timing at start of every frame */ + + st = (OFDM_M + OFDM_NCP + OFDM_SAMPLESPERFRAME + 1) - floorf(OFDM_FTWINDOWWIDTH / 2) + (ofdm->timing_est - 1); + en = st + OFDM_SAMPLESPERFRAME - 1 + OFDM_M + OFDM_NCP + OFDM_FTWINDOWWIDTH-1; + + complex float work[(en - st)]; + + for (i = st, j = 0; i < en; i++, j++) { + work[j] = ofdm->rxbuf[i] * cexpf(-I * woff_est * i); + } + + ft_est = coarse_sync(ofdm, work, (en - st)); + ofdm->timing_est += (ft_est - ceilf(OFDM_FTWINDOWWIDTH / 2)); + + if (ofdm->verbose > 1) { + fprintf(stdout, " ft_est: %2d timing_est: %2d sample_point: %2d\n", ft_est, ofdm->timing_est, ofdm->sample_point); + } + + /* Black magic to keep sample_point inside cyclic prefix. Or something like that. */ + + ofdm->sample_point = max(ofdm->timing_est + (OFDM_NCP / 4), ofdm->sample_point); + ofdm->sample_point = min(ofdm->timing_est + OFDM_NCP, ofdm->sample_point); + } + + /* down-convert at current timing instant---------------------------------- */ + + complex float rx_sym[OFDM_NS + 3][OFDM_NC + 2]; + + for (i = 0; i < (OFDM_NS + 3); i++) { + for (j = 0; j < (OFDM_NC + 2); j++) { + rx_sym[i][j] = 0.0f + 0.0f * I; + } + } + + /* previous pilot */ + + st = OFDM_M + OFDM_NCP + OFDM_SAMPLESPERFRAME + (-OFDM_NS) * (OFDM_M + OFDM_NCP) + 1 + ofdm->sample_point; + en = st + OFDM_M; + + complex float work[OFDM_M]; + + for (i = 0; i < (OFDM_NC + 2); i++) { + for (j = st, k = 0; j < en; j++, k++) { + work[k] = ofdm->rxbuf[j] * cexpf(-I * woff_est * j); + } + + matrix_vector_conjugate_multiply(ofdm, acarrier, work); + + rx_sym[0][i] = vector_sum(acarrier, 0, (OFDM_NC + 2)); + } + + /* pilot - this frame - pilot */ + + for (rr = 1; rr < (OFDM_NS + 3); rr++) { + st = OFDM_M + OFDM_NCP + OFDM_SAMPLESPERFRAME + (rr - 1) * (OFDM_M + OFDM_NCP) + 1 + ofdm->sample_point; + en = st + OFDM_M; + + for (i = 0; i < (OFDM_NC + 2); i++) { + for (j = st, k = 0; j < en; j++, k++) { + work[k] = ofdm->rxbuf[j] * cexpf(-I * woff_est * j); + } + + matrix_vector_conjugate_multiply(ofdm, acarrier, work); + + rx_sym[rr][i] = vector_sum(acarrier, 0, (OFDM_NC + 2)); + } + } + + /* next pilot */ + + st = OFDM_M + OFDM_NCP + OFDM_SAMPLESPERFRAME + (2 * OFDM_NS) * (OFDM_M + OFDM_NCP) + 1 + ofdm->sample_point; + en = st + OFDM_M; + + for (i = 0; i < (OFDM_NC + 2); i++) { + for (j = st, k = 0; j < en; j++, k++) { + work[k] = ofdm->rxbuf[j] * cexpf(-I * woff_est * j); + } + + matrix_vector_conjugate_multiply(ofdm, acarrier, work); + + rx_sym[(OFDM_NS + 2)][i] = vector_sum(acarrier, 0, (OFDM_NC + 2)); + } + + /* est freq err based on all carriers ------------------------------------ */ + + if (ofdm->foff_est_en == true) { + complex float freq_err_rect = vector_conjugate_sum(rx_sym[1], 0, (OFDM_NC + 2)) * vector_sum(rx_sym[1], OFDM_NS, (OFDM_NC + 2)); + freq_err_hz = cargf(freq_err_rect) * OFDM_RS / (TAU * OFDM_NS); + + ofdm->foff_est_hz += (ofdm->foff_est_gain * freq_err_hz); + } + + /* OK - now estimate and correct phase ---------------------------------- */ + + for (i = 0; i < (OFDM_NC + 2); i++) { + aphase_est_pilot[i] = 10.0f; + aamp_est_pilot[i] = 0.0f; + } + + for (i = 0; i < (OFDM_NC + 2); i++) { + /* + * estimate phase using average of 6 pilots in a rect 2D window centered on this carrier + * + * PPP + * DDD + * DDD + * PPP + */ + + complex float symbol[3]; + + for (j = i, k = 0; j < (i + 3); j++, k++) { + symbol[k] = rx_sym[2][j] * conjf(ofdm->pilots[j]); + } + + aphase_est_pilot_rect = vector_sum(symbol, 0, 3); + + for (j = i, k = 0; j < (i + 3); j++, k++) { + symbol[k] = rx_sym[(OFDM_NS + 2)][j] * conjf(ofdm->pilots[j]); + } + + aphase_est_pilot_rect += vector_sum(symbol, 0, 3); + + /* use next step of pilots in past and future */ + + for (j = i, k = 0; j < (i + 3); j++, k++) { + symbol[k] = rx_sym[1][j] * ofdm->pilots[j]; + } + + aphase_est_pilot_rect += vector_sum(symbol, 0, 3); + + for (j = i, k = 0; j < (i + 3); j++, k++) { + symbol[k] = rx_sym[OFDM_NS + 2][j] * ofdm->pilots[j]; + } + + aphase_est_pilot_rect += vector_sum(symbol, 0, 3); + + aphase_est_pilot[i] = cargf(aphase_est_pilot_rect); + aamp_est_pilot[i] = cabsf(aphase_est_pilot_rect / 12.0f); + } + + /* + * correct phase offset using phase estimate, and demodulate + * bits, separate loop as it runs across cols (carriers) to get + * frame bit ordering correct + */ + + for (i = 0; i < (OFDM_NC + 2); i++) { + ofdm->aphase_est_pilot_log[i] = aphase_est_pilot[i]; + } + + complex float rx_corr; + int abit[2]; + int bit_index = 0; + + /* skip pilots and boundaries */ + + for (rr = 0; rr < OFDM_ROWSPERFRAME; rr++) { + for (i = 1; i < (OFDM_NC + 1); i++) { + if (ofdm->phase_est_en == true) { + rx_corr = rx_sym[rr][i] * cexpf(I * aphase_est_pilot[i]); + } else { + rx_corr = rx_sym[rr][i]; + } + + ofdm->rx_np[(rr * OFDM_ROWSPERFRAME) + (i - 1)] = rx_corr; + ofdm->rx_amp[(rr * OFDM_ROWSPERFRAME) + (i - 1)] = aamp_est_pilot[i]; + + if (OFDM_BPS == 1) { + rx_bits[bit_index++] = crealf(rx_corr) > 0.0f; + } else if (OFDM_BPS == 2) { + qpsk_demod(rx_corr, abit); + rx_bits[bit_index++] = abit[0]; + rx_bits[bit_index++] = abit[1]; + } + } + } + + /* Adjust nin to take care of sample clock offset */ + + ofdm->nin = OFDM_SAMPLESPERFRAME; + + if (ofdm->timing_en == true) { + int thresh = (OFDM_M + OFDM_NCP) / 8; + int tshift = (OFDM_M + OFDM_NCP) / 4; + + if (ofdm->timing_est > thresh) { + ofdm->nin = OFDM_SAMPLESPERFRAME + tshift; + ofdm->timing_est -= tshift; + ofdm->sample_point -= tshift; + } else if (ofdm->timing_est < -thresh) { + ofdm->nin = OFDM_SAMPLESPERFRAME - tshift; + ofdm->timing_est += tshift; + ofdm->sample_point += tshift; + } + } } diff --git a/codec2-dev/src/ofdm_internal.h b/codec2-dev/src/ofdm_internal.h index 5809951f..6a15de66 100644 --- a/codec2-dev/src/ofdm_internal.h +++ b/codec2-dev/src/ofdm_internal.h @@ -89,9 +89,9 @@ struct OFDM { /* Demodulator data */ + complex float rx_np[OFDM_ROWSPERFRAME * OFDM_NC]; + float rx_amp[OFDM_ROWSPERFRAME * OFDM_NC]; float aphase_est_pilot_log[OFDM_NC + 2]; - float rx_np[OFDM_NC]; - float rx_amp[OFDM_NC]; }; #ifdef __cplusplus -- 2.25.1