diff options
Diffstat (limited to 'src/cohpsk.c')
| -rw-r--r-- | src/cohpsk.c | 1941 |
1 files changed, 973 insertions, 968 deletions
diff --git a/src/cohpsk.c b/src/cohpsk.c index b7ce633..d5f7359 100644 --- a/src/cohpsk.c +++ b/src/cohpsk.c @@ -32,35 +32,31 @@ \*---------------------------------------------------------------------------*/ #include <assert.h> -#include <stdlib.h> +#include <math.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <math.h> #include "codec2_cohpsk.h" #include "cohpsk_defs.h" #include "cohpsk_internal.h" -#include "fdmdv_internal.h" -#include "pilots_coh.h" #include "comp_prim.h" +#include "debug_alloc.h" +#include "fdmdv_internal.h" #include "kiss_fft.h" #include "linreg.h" +#include "pilots_coh.h" #include "rn_coh.h" #include "test_bits_coh.h" -#include "debug_alloc.h" - -static COMP qpsk_mod[] = { - { 1.0, 0.0}, - { 0.0, 1.0}, - { 0.0,-1.0}, - {-1.0, 0.0} -}; +static COMP qpsk_mod[] = {{1.0, 0.0}, {0.0, 1.0}, {0.0, -1.0}, {-1.0, 0.0}}; static int sampling_points[] = {0, 1, 6, 7}; -void corr_with_pilots(float *corr_out, float *mag_out, struct COHPSK *coh, int t, float f_fine); -void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC*COHPSK_ND], COMP ch_symb[][COHPSK_NC*COHPSK_ND]); +void corr_with_pilots(float *corr_out, float *mag_out, struct COHPSK *coh, + int t, float f_fine); +void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC * COHPSK_ND], + COMP ch_symb[][COHPSK_NC * COHPSK_ND]); /*---------------------------------------------------------------------------*\ @@ -68,7 +64,6 @@ void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC*COHPSK_ND], COMP ch_symb[][ \*---------------------------------------------------------------------------*/ - /*--------------------------------------------------------------------------* \ FUNCTION....: cohpsk_create @@ -81,130 +76,133 @@ void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC*COHPSK_ND], COMP ch_symb[][ \*---------------------------------------------------------------------------*/ -struct COHPSK *cohpsk_create(void) -{ - struct COHPSK *coh; - struct FDMDV *fdmdv; - int r,c,p,i; - float freq_hz, result; - float tau = 2.0f * M_PI; - - assert(COHPSK_NC == PILOTS_NC); - assert(COHPSK_NOM_SAMPLES_PER_FRAME == (COHPSK_M*NSYMROWPILOT)); - assert(COHPSK_MAX_SAMPLES_PER_FRAME == (COHPSK_M*NSYMROWPILOT+COHPSK_M/P)); - assert(COHPSK_NSYM == NSYM); /* as we want to use the tx sym mem on fdmdv */ - assert(COHPSK_NT == NT); - - coh = (struct COHPSK*)MALLOC(sizeof(struct COHPSK)); - if (coh == NULL) - return NULL; - - /* set up buffer of tx pilot symbols for coh demod on rx */ - - for(r=0; r<2*NPILOTSFRAME; ) { - for(p=0; p<NPILOTSFRAME; r++, p++) { - for(c=0; c<COHPSK_NC; c++) { - coh->pilot2[r][c] = pilots_coh[p][c]; - } - } +struct COHPSK *cohpsk_create(void) { + struct COHPSK *coh; + struct FDMDV *fdmdv; + int r, c, p, i; + float freq_hz, result; + float tau = 2.0f * M_PI; + + assert(COHPSK_NC == PILOTS_NC); + assert(COHPSK_NOM_SAMPLES_PER_FRAME == (COHPSK_M * NSYMROWPILOT)); + assert(COHPSK_MAX_SAMPLES_PER_FRAME == + (COHPSK_M * NSYMROWPILOT + COHPSK_M / P)); + assert(COHPSK_NSYM == NSYM); /* as we want to use the tx sym mem on fdmdv */ + assert(COHPSK_NT == NT); + + coh = (struct COHPSK *)MALLOC(sizeof(struct COHPSK)); + if (coh == NULL) return NULL; + + /* set up buffer of tx pilot symbols for coh demod on rx */ + + for (r = 0; r < 2 * NPILOTSFRAME;) { + for (p = 0; p < NPILOTSFRAME; r++, p++) { + for (c = 0; c < COHPSK_NC; c++) { + coh->pilot2[r][c] = pilots_coh[p][c]; + } } + } - /* Clear symbol buffer memory */ + /* Clear symbol buffer memory */ - for (r=0; r<NCT_SYMB_BUF; r++) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - coh->ct_symb_buf[r][c].real = 0.0; - coh->ct_symb_buf[r][c].imag = 0.0; - } + for (r = 0; r < NCT_SYMB_BUF; r++) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + coh->ct_symb_buf[r][c].real = 0.0; + coh->ct_symb_buf[r][c].imag = 0.0; } + } - coh->ff_phase.real = 1.0; coh->ff_phase.imag = 0.0; - coh->sync = 0; - coh->frame = 0; - coh->ratio = 0.0; - coh->nin = COHPSK_M; + coh->ff_phase.real = 1.0; + coh->ff_phase.imag = 0.0; + coh->sync = 0; + coh->frame = 0; + coh->ratio = 0.0; + coh->nin = COHPSK_M; - /* clear sync window buffer */ + /* clear sync window buffer */ - for (i=0; i<NSW*NSYMROWPILOT*COHPSK_M; i++) { - coh->ch_fdm_frame_buf[i].real = 0.0; - coh->ch_fdm_frame_buf[i].imag = 0.0; - } + for (i = 0; i < NSW * NSYMROWPILOT * COHPSK_M; i++) { + coh->ch_fdm_frame_buf[i].real = 0.0; + coh->ch_fdm_frame_buf[i].imag = 0.0; + } - /* set up fdmdv states so we can use those modem functions */ + /* set up fdmdv states so we can use those modem functions */ - /* - * NC*ND -1 Realize that the function creates a sync carrier (+1), - * or one more carrier than asked for. We ignore any initialization - * inside of fdmdv and take care of that here, using the whole - * NC*ND number of carriers to be used in cohpsk. - */ - fdmdv = fdmdv_create((COHPSK_NC*COHPSK_ND) - 1); + /* + * NC*ND -1 Realize that the function creates a sync carrier (+1), + * or one more carrier than asked for. We ignore any initialization + * inside of fdmdv and take care of that here, using the whole + * NC*ND number of carriers to be used in cohpsk. + */ + fdmdv = fdmdv_create((COHPSK_NC * COHPSK_ND) - 1); - fdmdv->fsep = COHPSK_RS*(1.0 + COHPSK_EXCESS_BW); + fdmdv->fsep = COHPSK_RS * (1.0 + COHPSK_EXCESS_BW); - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - fdmdv->phase_tx[c].real = 1.0; - fdmdv->phase_tx[c].imag = 0.0; + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + fdmdv->phase_tx[c].real = 1.0; + fdmdv->phase_tx[c].imag = 0.0; - /* note non-linear carrier spacing to help PAPR, works v well in conjunction with CLIP */ + /* note non-linear carrier spacing to help PAPR, works v well in conjunction + * with CLIP */ - freq_hz = fdmdv->fsep*( -(COHPSK_NC*COHPSK_ND)/2 - 0.5f + powf(c + 1.0f, 0.98f) ); - result = tau * freq_hz/COHPSK_FS; + freq_hz = fdmdv->fsep * + (-(COHPSK_NC * COHPSK_ND) / 2 - 0.5f + powf(c + 1.0f, 0.98f)); + result = tau * freq_hz / COHPSK_FS; - fdmdv->freq[c].real = cosf(result); - fdmdv->freq[c].imag = sinf(result); - fdmdv->freq_pol[c] = result; + fdmdv->freq[c].real = cosf(result); + fdmdv->freq[c].imag = sinf(result); + fdmdv->freq_pol[c] = result; - //printf("c: %d %f %f\n",c,freq_hz,fdmdv->freq_pol[c]); - for(i=0; i<COHPSK_NFILTER; i++) { - coh->rx_filter_memory[c][i].real = 0.0; - coh->rx_filter_memory[c][i].imag = 0.0; - } + // printf("c: %d %f %f\n",c,freq_hz,fdmdv->freq_pol[c]); + for (i = 0; i < COHPSK_NFILTER; i++) { + coh->rx_filter_memory[c][i].real = 0.0; + coh->rx_filter_memory[c][i].imag = 0.0; + } - /* optional per-carrier amplitude weighting for testing */ + /* optional per-carrier amplitude weighting for testing */ - coh->carrier_ampl[c] = 1.0; - } - - result = tau * FDMDV_FCENTRE/COHPSK_FS; - fdmdv->fbb_rect.real = cosf(result); - fdmdv->fbb_rect.imag = sinf(result); - fdmdv->fbb_pol = result; + coh->carrier_ampl[c] = 1.0; + } - coh->fdmdv = fdmdv; + result = tau * FDMDV_FCENTRE / COHPSK_FS; + fdmdv->fbb_rect.real = cosf(result); + fdmdv->fbb_rect.imag = sinf(result); + fdmdv->fbb_pol = result; - coh->sig_rms = coh->noise_rms = 0.0; + coh->fdmdv = fdmdv; - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - for (r=0; r<NSYMROW; r++) { - coh->rx_symb[r][c].real = 0.0; - coh->rx_symb[r][c].imag = 0.0; - } + coh->sig_rms = coh->noise_rms = 0.0; + + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + for (r = 0; r < NSYMROW; r++) { + coh->rx_symb[r][c].real = 0.0; + coh->rx_symb[r][c].imag = 0.0; } + } - coh->verbose = 0; + coh->verbose = 0; - /* disable optional logging by default */ + /* disable optional logging by default */ - coh->rx_baseband_log = NULL; - coh->rx_baseband_log_col_index = 0; - coh->rx_filt_log = NULL; - coh->rx_filt_log_col_index = 0; - coh->ch_symb_log = NULL; - coh->ch_symb_log_r = 0; - coh->rx_timing_log = NULL; - coh->rx_timing_log_index = 0; + coh->rx_baseband_log = NULL; + coh->rx_baseband_log_col_index = 0; + coh->rx_filt_log = NULL; + coh->rx_filt_log_col_index = 0; + coh->ch_symb_log = NULL; + coh->ch_symb_log_r = 0; + coh->rx_timing_log = NULL; + coh->rx_timing_log_index = 0; - /* test frames */ + /* test frames */ - coh->ptest_bits_coh_tx = coh->ptest_bits_coh_rx[0] = coh->ptest_bits_coh_rx[1] = (int*)test_bits_coh; - coh->ptest_bits_coh_end = (int*)test_bits_coh + sizeof(test_bits_coh)/sizeof(int); + coh->ptest_bits_coh_tx = coh->ptest_bits_coh_rx[0] = + coh->ptest_bits_coh_rx[1] = (int *)test_bits_coh; + coh->ptest_bits_coh_end = + (int *)test_bits_coh + sizeof(test_bits_coh) / sizeof(int); - return coh; + return coh; } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_destroy @@ -215,87 +213,85 @@ struct COHPSK *cohpsk_create(void) \*---------------------------------------------------------------------------*/ -void cohpsk_destroy(struct COHPSK *coh) -{ - fdmdv_destroy(coh->fdmdv); - assert(coh != NULL); - FREE(coh); +void cohpsk_destroy(struct COHPSK *coh) { + fdmdv_destroy(coh->fdmdv); + assert(coh != NULL); + FREE(coh); } - /*---------------------------------------------------------------------------*\ FUNCTION....: bits_to_qpsk_symbols() AUTHOR......: David Rowe DATE CREATED: March 2015 - Rate Rs modulator. Maps bits to parallel DQPSK symbols and inserts pilot symbols. + Rate Rs modulator. Maps bits to parallel DQPSK symbols and inserts pilot +symbols. \*---------------------------------------------------------------------------*/ -void bits_to_qpsk_symbols(COMP tx_symb[][COHPSK_NC*COHPSK_ND], int tx_bits[], int nbits) -{ - int i, r, c, p_r, data_r, d, diversity; - short bits; +void bits_to_qpsk_symbols(COMP tx_symb[][COHPSK_NC * COHPSK_ND], int tx_bits[], + int nbits) { + int i, r, c, p_r, data_r, d, diversity; + short bits; - /* check allowed number of bits supplied matches number of QPSK - symbols in the frame */ + /* check allowed number of bits supplied matches number of QPSK + symbols in the frame */ - assert( (NSYMROW*COHPSK_NC*2 == nbits) || (NSYMROW*COHPSK_NC*2*COHPSK_ND == nbits)); + assert((NSYMROW * COHPSK_NC * 2 == nbits) || + (NSYMROW * COHPSK_NC * 2 * COHPSK_ND == nbits)); - /* if we input twice as many bits we don't do diversity */ + /* if we input twice as many bits we don't do diversity */ - if (NSYMROW*COHPSK_NC*2 == nbits) { - diversity = 1; /* diversity mode */ - } - else { - diversity = 2; /* twice as many bits, non diversity mode */ - } + if (NSYMROW * COHPSK_NC * 2 == nbits) { + diversity = 1; /* diversity mode */ + } else { + diversity = 2; /* twice as many bits, non diversity mode */ + } - /* - Insert two rows of Nc pilots at beginning of data frame. + /* + Insert two rows of Nc pilots at beginning of data frame. - Organise QPSK symbols into a NSYMBROWS rows by PILOTS_NC*ND cols matrix, - each column is a carrier, time flows down the cols...... + Organise QPSK symbols into a NSYMBROWS rows by PILOTS_NC*ND cols matrix, + each column is a carrier, time flows down the cols...... - Note: the "& 0x1" prevents and non binary tx_bits[] screwing up - our lives. Call me defensive. + Note: the "& 0x1" prevents and non binary tx_bits[] screwing up + our lives. Call me defensive. - sqrtf(ND) term ensures the same energy/symbol for different - diversity factors. - */ + sqrtf(ND) term ensures the same energy/symbol for different + diversity factors. + */ - r = 0; - for(p_r=0; p_r<2; p_r++) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - tx_symb[r][c].real = pilots_coh[p_r][c % COHPSK_NC]/sqrtf(COHPSK_ND); - tx_symb[r][c].imag = 0.0; - } - r++; + r = 0; + for (p_r = 0; p_r < 2; p_r++) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + tx_symb[r][c].real = pilots_coh[p_r][c % COHPSK_NC] / sqrtf(COHPSK_ND); + tx_symb[r][c].imag = 0.0; } - for(data_r=0; data_r<NSYMROW; data_r++, r++) { - for(c=0; c<COHPSK_NC*diversity; c++) { - i = c*NSYMROW + data_r; - bits = (tx_bits[2*i]&0x1)<<1 | (tx_bits[2*i+1]&0x1); - tx_symb[r][c] = fcmult(1.0/sqrtf(COHPSK_ND),qpsk_mod[bits]); - } + r++; + } + for (data_r = 0; data_r < NSYMROW; data_r++, r++) { + for (c = 0; c < COHPSK_NC * diversity; c++) { + i = c * NSYMROW + data_r; + bits = (tx_bits[2 * i] & 0x1) << 1 | (tx_bits[2 * i + 1] & 0x1); + tx_symb[r][c] = fcmult(1.0 / sqrtf(COHPSK_ND), qpsk_mod[bits]); } + } - assert(p_r == NPILOTSFRAME); - assert(r == NSYMROWPILOT); + assert(p_r == NPILOTSFRAME); + assert(r == NSYMROWPILOT); - /* if in diversity mode, copy symbols to upper carriers */ + /* if in diversity mode, copy symbols to upper carriers */ - for(d=1; d<1+COHPSK_ND-diversity; d++) { - for(r=0; r<NSYMROWPILOT; r++) { - for(c=0; c<COHPSK_NC; c++) { - tx_symb[r][c+COHPSK_NC*d] = tx_symb[r][c]; - } - } + for (d = 1; d < 1 + COHPSK_ND - diversity; d++) { + for (r = 0; r < NSYMROWPILOT; r++) { + for (c = 0; c < COHPSK_NC; c++) { + tx_symb[r][c + COHPSK_NC * d] = tx_symb[r][c]; + } } + } } - /*---------------------------------------------------------------------------*\ FUNCTION....: qpsk_symbols_to_bits() @@ -310,118 +306,118 @@ void bits_to_qpsk_symbols(COMP tx_symb[][COHPSK_NC*COHPSK_ND], int tx_bits[], in \*---------------------------------------------------------------------------*/ -void qpsk_symbols_to_bits(struct COHPSK *coh, float rx_bits[], COMP ct_symb_buf[][COHPSK_NC*COHPSK_ND]) -{ - int p, r, c, i, pc, d, n; - float x[NPILOTSFRAME+2], x1; - COMP y[NPILOTSFRAME+2], yfit; - COMP rx_symb_linear[NSYMROW*COHPSK_NC*COHPSK_ND]; - COMP m, b; - COMP __attribute__((unused)) corr, rot, pi_on_4, phi_rect, div_symb; - float mag, __attribute__((unused)) phi_, __attribute__((unused)) amp_; - float sum_x, sum_xx, noise_var; - float spi_4 = M_PI / 4.0f; - COMP s; - - pi_on_4.real = cosf(spi_4); pi_on_4.imag = sinf(spi_4); - - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - - /* Set up lin reg model and interpolate phase. Works better than average for channels with - quickly changing phase, like HF. */ - - for(p=0; p<NPILOTSFRAME+2; p++) { - x[p] = sampling_points[p]; - pc = c % COHPSK_NC; - y[p] = fcmult(coh->pilot2[p][pc], ct_symb_buf[sampling_points[p]][c]); - } - - linreg(&m, &b, x, y, NPILOTSFRAME+2); - for(r=0; r<NSYMROW; r++) { - x1 = (float)(r+NPILOTSFRAME); - yfit = cadd(fcmult(x1,m),b); - coh->phi_[r][c] = atan2f(yfit.imag, yfit.real); - } - - /* amplitude estimation */ +void qpsk_symbols_to_bits(struct COHPSK *coh, float rx_bits[], + COMP ct_symb_buf[][COHPSK_NC * COHPSK_ND]) { + int p, r, c, i, pc, d, n; + float x[NPILOTSFRAME + 2], x1; + COMP y[NPILOTSFRAME + 2], yfit; + COMP rx_symb_linear[NSYMROW * COHPSK_NC * COHPSK_ND]; + COMP m, b; + COMP __attribute__((unused)) corr, rot, pi_on_4, phi_rect, div_symb; + float mag, __attribute__((unused)) phi_, __attribute__((unused)) amp_; + float sum_x, sum_xx, noise_var; + float spi_4 = M_PI / 4.0f; + COMP s; + + pi_on_4.real = cosf(spi_4); + pi_on_4.imag = sinf(spi_4); + + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + /* Set up lin reg model and interpolate phase. Works better than average + for channels with quickly changing phase, like HF. */ + + for (p = 0; p < NPILOTSFRAME + 2; p++) { + x[p] = sampling_points[p]; + pc = c % COHPSK_NC; + y[p] = fcmult(coh->pilot2[p][pc], ct_symb_buf[sampling_points[p]][c]); + } - mag = 0.0f; - for(p=0; p<NPILOTSFRAME+2; p++) { - mag += cabsolute(ct_symb_buf[sampling_points[p]][c]); - } - amp_ = mag/(NPILOTSFRAME+2); - for(r=0; r<NSYMROW; r++) { - coh->amp_[r][c] = amp_; - } + linreg(&m, &b, x, y, NPILOTSFRAME + 2); + for (r = 0; r < NSYMROW; r++) { + x1 = (float)(r + NPILOTSFRAME); + yfit = cadd(fcmult(x1, m), b); + coh->phi_[r][c] = atan2f(yfit.imag, yfit.real); } - /* now correct phase of data symbols */ + /* amplitude estimation */ - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - for (r=0; r<NSYMROW; r++) { - phi_rect.real = cosf(coh->phi_[r][c]); phi_rect.imag = -sinf(coh->phi_[r][c]); - coh->rx_symb[r][c] = cmult(ct_symb_buf[NPILOTSFRAME + r][c], phi_rect); - i = c*NSYMROW + r; - rx_symb_linear[i] = coh->rx_symb[r][c]; - } + mag = 0.0f; + for (p = 0; p < NPILOTSFRAME + 2; p++) { + mag += cabsolute(ct_symb_buf[sampling_points[p]][c]); } - - /* and finally optional diversity combination, note output is soft decn a "1" is < 0 */ - - for(c=0; c<COHPSK_NC; c++) { - for(r=0; r<NSYMROW; r++) { - div_symb = coh->rx_symb[r][c]; - for (d=1; d<COHPSK_ND; d++) { - div_symb = cadd(div_symb, coh->rx_symb[r][c + COHPSK_NC*d]); - } - rot = cmult(div_symb, pi_on_4); - i = c*NSYMROW + r; - rx_bits[2*i+1] = rot.real; - rx_bits[2*i] = rot.imag; - - /* demodulate bits from upper and lower carriers separately for test purposes */ - - assert(COHPSK_ND == 2); - - i = c*NSYMROW + r; - rot = cmult(coh->rx_symb[r][c], pi_on_4); - coh->rx_bits_lower[2*i+1] = rot.real; - coh->rx_bits_lower[2*i] = rot.imag; - rot = cmult(coh->rx_symb[r][c + COHPSK_NC], pi_on_4); - coh->rx_bits_upper[2*i+1] = rot.real; - coh->rx_bits_upper[2*i] = rot.imag; - } + amp_ = mag / (NPILOTSFRAME + 2); + for (r = 0; r < NSYMROW; r++) { + coh->amp_[r][c] = amp_; } + } + /* now correct phase of data symbols */ - /* estimate RMS signal and noise */ + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + for (r = 0; r < NSYMROW; r++) { + phi_rect.real = cosf(coh->phi_[r][c]); + phi_rect.imag = -sinf(coh->phi_[r][c]); + coh->rx_symb[r][c] = cmult(ct_symb_buf[NPILOTSFRAME + r][c], phi_rect); + i = c * NSYMROW + r; + rx_symb_linear[i] = coh->rx_symb[r][c]; + } + } - mag = 0.0f; - for(i=0; i<NSYMROW*COHPSK_NC*COHPSK_ND; i++) - mag += cabsolute(rx_symb_linear[i]); - coh->sig_rms = mag/(NSYMROW*COHPSK_NC*COHPSK_ND); - - sum_x = 0.0f; - sum_xx = 0.0f; - n = 0; - for (i=0; i<NSYMROW*COHPSK_NC*COHPSK_ND; i++) { - s = rx_symb_linear[i]; - if (fabsf(s.real) > coh->sig_rms) { - sum_x += s.imag; - sum_xx += s.imag*s.imag; - n++; + /* and finally optional diversity combination, note output is soft decn a "1" + * is < 0 */ + + for (c = 0; c < COHPSK_NC; c++) { + for (r = 0; r < NSYMROW; r++) { + div_symb = coh->rx_symb[r][c]; + for (d = 1; d < COHPSK_ND; d++) { + div_symb = cadd(div_symb, coh->rx_symb[r][c + COHPSK_NC * d]); } + rot = cmult(div_symb, pi_on_4); + i = c * NSYMROW + r; + rx_bits[2 * i + 1] = rot.real; + rx_bits[2 * i] = rot.imag; + + /* demodulate bits from upper and lower carriers separately for test + * purposes */ + + assert(COHPSK_ND == 2); + + i = c * NSYMROW + r; + rot = cmult(coh->rx_symb[r][c], pi_on_4); + coh->rx_bits_lower[2 * i + 1] = rot.real; + coh->rx_bits_lower[2 * i] = rot.imag; + rot = cmult(coh->rx_symb[r][c + COHPSK_NC], pi_on_4); + coh->rx_bits_upper[2 * i + 1] = rot.real; + coh->rx_bits_upper[2 * i] = rot.imag; } - - noise_var = 0.0f; - if (n > 1) { - noise_var = (n*sum_xx - sum_x*sum_x)/(n*(n-1)); + } + + /* estimate RMS signal and noise */ + + mag = 0.0f; + for (i = 0; i < NSYMROW * COHPSK_NC * COHPSK_ND; i++) + mag += cabsolute(rx_symb_linear[i]); + coh->sig_rms = mag / (NSYMROW * COHPSK_NC * COHPSK_ND); + + sum_x = 0.0f; + sum_xx = 0.0f; + n = 0; + for (i = 0; i < NSYMROW * COHPSK_NC * COHPSK_ND; i++) { + s = rx_symb_linear[i]; + if (fabsf(s.real) > coh->sig_rms) { + sum_x += s.imag; + sum_xx += s.imag * s.imag; + n++; } - coh->noise_rms = sqrtf(noise_var); + } + noise_var = 0.0f; + if (n > 1) { + noise_var = (n * sum_xx - sum_x * sum_x) / (n * (n - 1)); + } + coh->noise_rms = sqrtf(noise_var); } - /*---------------------------------------------------------------------------*\ FUNCTION....: tx_filter_and_upconvert_coh() @@ -438,134 +434,132 @@ void qpsk_symbols_to_bits(struct COHPSK *coh, float rx_bits[], COMP ct_symb_buf[ void tx_filter_and_upconvert_coh(COMP tx_fdm[], int Nc, const COMP tx_symbols[], COMP tx_filter_memory[][COHPSK_NSYM], - COMP phase_tx[], COMP freq[], - COMP *fbb_phase, COMP fbb_rect) -{ - int c; - int i,j,k; - float acc; - COMP gain; - COMP tx_baseband; - COMP two = {2.0, 0.0}; - float mag; - - gain.real = sqrtf(2.0)/2.0; - gain.imag = 0.0; - - for(i=0; i<COHPSK_M; i++) { - tx_fdm[i].real = 0.0; - tx_fdm[i].imag = 0.0; + COMP phase_tx[], COMP freq[], COMP *fbb_phase, + COMP fbb_rect) { + int c; + int i, j, k; + float acc; + COMP gain; + COMP tx_baseband; + COMP two = {2.0, 0.0}; + float mag; + + gain.real = sqrtf(2.0) / 2.0; + gain.imag = 0.0; + + for (i = 0; i < COHPSK_M; i++) { + tx_fdm[i].real = 0.0; + tx_fdm[i].imag = 0.0; + } + + for (c = 0; c < Nc; c++) + tx_filter_memory[c][COHPSK_NSYM - 1] = cmult(tx_symbols[c], gain); + + /* + tx filter each symbol, generate M filtered output samples for + each symbol, which we then freq shift and sum with other + carriers. Efficient polyphase filter techniques used as + tx_filter_memory is sparse + */ + + for (c = 0; c < Nc; c++) { + for (i = 0; i < COHPSK_M; i++) { + /* filter real sample of symbol for carrier c */ + + acc = 0.0; + for (j = 0, k = COHPSK_M - i - 1; j < COHPSK_NSYM; j++, k += COHPSK_M) + acc += COHPSK_M * tx_filter_memory[c][j].real * gt_alpha5_root_coh[k]; + tx_baseband.real = acc; + + /* filter imag sample of symbol for carrier c */ + + acc = 0.0; + for (j = 0, k = COHPSK_M - i - 1; j < COHPSK_NSYM; j++, k += COHPSK_M) + acc += COHPSK_M * tx_filter_memory[c][j].imag * gt_alpha5_root_coh[k]; + tx_baseband.imag = acc; + // printf("%d %d %f %f\n", c, i, tx_baseband.real, tx_baseband.imag); + + /* freq shift and sum */ + + phase_tx[c] = cmult(phase_tx[c], freq[c]); + tx_fdm[i] = cadd(tx_fdm[i], cmult(tx_baseband, phase_tx[c])); + // printf("%d %d %f %f\n", c, i, phase_tx[c].real, phase_tx[c].imag); } + // exit(0); + } - for(c=0; c<Nc; c++) - tx_filter_memory[c][COHPSK_NSYM-1] = cmult(tx_symbols[c], gain); - - /* - tx filter each symbol, generate M filtered output samples for - each symbol, which we then freq shift and sum with other - carriers. Efficient polyphase filter techniques used as - tx_filter_memory is sparse - */ - - for(c=0; c<Nc; c++) { - for(i=0; i<COHPSK_M; i++) { - - /* filter real sample of symbol for carrier c */ - - acc = 0.0; - for(j=0,k=COHPSK_M-i-1; j<COHPSK_NSYM; j++,k+=COHPSK_M) - acc += COHPSK_M * tx_filter_memory[c][j].real * gt_alpha5_root_coh[k]; - tx_baseband.real = acc; - - /* filter imag sample of symbol for carrier c */ + /* shift whole thing up to carrier freq */ - acc = 0.0; - for(j=0,k=COHPSK_M-i-1; j<COHPSK_NSYM; j++,k+=COHPSK_M) - acc += COHPSK_M * tx_filter_memory[c][j].imag * gt_alpha5_root_coh[k]; - tx_baseband.imag = acc; - //printf("%d %d %f %f\n", c, i, tx_baseband.real, tx_baseband.imag); + for (i = 0; i < COHPSK_M; i++) { + *fbb_phase = cmult(*fbb_phase, fbb_rect); + tx_fdm[i] = cmult(tx_fdm[i], *fbb_phase); + } - /* freq shift and sum */ + /* + Scale such that total Carrier power C of real(tx_fdm) = Nc. This + excludes the power of the pilot tone. + We return the complex (single sided) signal to make frequency + shifting for the purpose of testing easier + */ - phase_tx[c] = cmult(phase_tx[c], freq[c]); - tx_fdm[i] = cadd(tx_fdm[i], cmult(tx_baseband, phase_tx[c])); - //printf("%d %d %f %f\n", c, i, phase_tx[c].real, phase_tx[c].imag); - } - //exit(0); - } - - /* shift whole thing up to carrier freq */ - - for (i=0; i<COHPSK_M; i++) { - *fbb_phase = cmult(*fbb_phase, fbb_rect); - tx_fdm[i] = cmult(tx_fdm[i], *fbb_phase); - } - - /* - Scale such that total Carrier power C of real(tx_fdm) = Nc. This - excludes the power of the pilot tone. - We return the complex (single sided) signal to make frequency - shifting for the purpose of testing easier - */ + for (i = 0; i < COHPSK_M; i++) tx_fdm[i] = cmult(two, tx_fdm[i]); - for (i=0; i<COHPSK_M; i++) - tx_fdm[i] = cmult(two, tx_fdm[i]); + /* normalise digital oscillators as the magnitude can drift over time */ - /* normalise digital oscillators as the magnitude can drift over time */ + for (c = 0; c < Nc; c++) { + mag = cabsolute(phase_tx[c]); + phase_tx[c].real /= mag; + phase_tx[c].imag /= mag; + } - for (c=0; c<Nc; c++) { - mag = cabsolute(phase_tx[c]); - phase_tx[c].real /= mag; - phase_tx[c].imag /= mag; - } + mag = cabsolute(*fbb_phase); + fbb_phase->real /= mag; + fbb_phase->imag /= mag; - mag = cabsolute(*fbb_phase); - fbb_phase->real /= mag; - fbb_phase->imag /= mag; + /* shift memory, inserting zeros at end */ - /* shift memory, inserting zeros at end */ + for (i = 0; i < COHPSK_NSYM - 1; i++) + for (c = 0; c < Nc; c++) + tx_filter_memory[c][i] = tx_filter_memory[c][i + 1]; - for(i=0; i<COHPSK_NSYM-1; i++) - for(c=0; c<Nc; c++) - tx_filter_memory[c][i] = tx_filter_memory[c][i+1]; - - for(c=0; c<Nc; c++) { - tx_filter_memory[c][COHPSK_NSYM-1].real = 0.0; - tx_filter_memory[c][COHPSK_NSYM-1].imag = 0.0; - } + for (c = 0; c < Nc; c++) { + tx_filter_memory[c][COHPSK_NSYM - 1].real = 0.0; + tx_filter_memory[c][COHPSK_NSYM - 1].imag = 0.0; + } } - - -void corr_with_pilots(float *corr_out, float *mag_out, struct COHPSK *coh, int t, float f_fine) -{ - COMP acorr, f_fine_rect[NPILOTSFRAME+2], f_corr; - float mag, corr, result; - float tau = 2.0f * M_PI; - int c, p, pc; - - for (p=0; p<NPILOTSFRAME+2; p++) { - result = f_fine * tau * (sampling_points[p]+1.0) / COHPSK_RS; - f_fine_rect[p].real = cosf(result); - f_fine_rect[p].imag = sinf(result); +void corr_with_pilots(float *corr_out, float *mag_out, struct COHPSK *coh, + int t, float f_fine) { + COMP acorr, f_fine_rect[NPILOTSFRAME + 2], f_corr; + float mag, corr, result; + float tau = 2.0f * M_PI; + int c, p, pc; + + for (p = 0; p < NPILOTSFRAME + 2; p++) { + result = f_fine * tau * (sampling_points[p] + 1.0) / COHPSK_RS; + f_fine_rect[p].real = cosf(result); + f_fine_rect[p].imag = sinf(result); + } + + corr = 0.0; + mag = 1E-12; + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + acorr.real = 0.0f; + acorr.imag = 0.0f; + pc = c % COHPSK_NC; + for (p = 0; p < NPILOTSFRAME + 2; p++) { + f_corr = + cmult(f_fine_rect[p], coh->ct_symb_buf[t + sampling_points[p]][c]); + acorr = cadd(acorr, fcmult(coh->pilot2[p][pc], f_corr)); + mag += cabsolute(f_corr); } + corr += cabsolute(acorr); + } - corr = 0.0; mag = 1E-12; - for (c=0; c<COHPSK_NC*COHPSK_ND; c++) { - acorr.real = 0.0f; acorr.imag = 0.0f; pc = c % COHPSK_NC; - for (p=0; p<NPILOTSFRAME+2; p++) { - f_corr = cmult(f_fine_rect[p], coh->ct_symb_buf[t+sampling_points[p]][c]); - acorr = cadd(acorr, fcmult(coh->pilot2[p][pc], f_corr)); - mag += cabsolute(f_corr); - } - corr += cabsolute(acorr); - } - - *corr_out = corr; - *mag_out = mag; + *corr_out = corr; + *mag_out = mag; } - /*---------------------------------------------------------------------------*\ FUNCTION....: frame_sync_fine_freq_est() @@ -578,105 +572,101 @@ void corr_with_pilots(float *corr_out, float *mag_out, struct COHPSK *coh, int t \*---------------------------------------------------------------------------*/ -void frame_sync_fine_freq_est(struct COHPSK *coh, COMP ch_symb[][COHPSK_NC*COHPSK_ND], int sync, int *next_sync) -{ - int t; - float f_fine, mag, max_corr, max_mag, corr, result; - float tau = 2.0f * M_PI; - - update_ct_symb_buf(coh->ct_symb_buf, ch_symb); - - /* sample pilots at start of this frame and start of next frame */ - - if (sync == 0) { - - /* sample correlation over 2D grid of time and fine freq points */ - - max_corr = 0.0; max_mag = 1E-12; - for (f_fine=-20; f_fine<=20; f_fine+=0.25) { - for (t=0; t<NSYMROWPILOT; t++) { - corr_with_pilots(&corr, &mag, coh, t, f_fine); - //printf(" f: %f t: %d corr: %f mag: %f\n", f_fine, t, corr, mag); - if (corr >= max_corr) { - max_corr = corr; - max_mag = mag; - coh->ct = t; - coh->f_fine_est = f_fine; - } - } +void frame_sync_fine_freq_est(struct COHPSK *coh, + COMP ch_symb[][COHPSK_NC * COHPSK_ND], int sync, + int *next_sync) { + int t; + float f_fine, mag, max_corr, max_mag, corr, result; + float tau = 2.0f * M_PI; + + update_ct_symb_buf(coh->ct_symb_buf, ch_symb); + + /* sample pilots at start of this frame and start of next frame */ + + if (sync == 0) { + /* sample correlation over 2D grid of time and fine freq points */ + + max_corr = 0.0; + max_mag = 1E-12; + for (f_fine = -20; f_fine <= 20; f_fine += 0.25) { + for (t = 0; t < NSYMROWPILOT; t++) { + corr_with_pilots(&corr, &mag, coh, t, f_fine); + // printf(" f: %f t: %d corr: %f mag: %f\n", f_fine, t, corr, mag); + if (corr >= max_corr) { + max_corr = corr; + max_mag = mag; + coh->ct = t; + coh->f_fine_est = f_fine; } + } + } - - result = coh->f_fine_est * tau / COHPSK_RS; - - coh->ff_rect.real = cosf(result); - coh->ff_rect.imag = -sinf(result); - if (coh->verbose) - fprintf(stderr, " [%d] fine freq f: %6.2f max_ratio: %f ct: %d\n", coh->frame, (double)coh->f_fine_est, (double)max_corr/(double)max_mag, coh->ct); - - if (max_corr/max_mag > 0.9) { - if (coh->verbose) - fprintf(stderr, " [%d] encouraging sync word!\n", coh->frame); - coh->sync_timer = 0; - *next_sync = 1; - } - else { - *next_sync = 0; - } - coh->ratio = max_corr/max_mag; + result = coh->f_fine_est * tau / COHPSK_RS; + + coh->ff_rect.real = cosf(result); + coh->ff_rect.imag = -sinf(result); + if (coh->verbose) + fprintf(stderr, " [%d] fine freq f: %6.2f max_ratio: %f ct: %d\n", + coh->frame, (double)coh->f_fine_est, + (double)max_corr / (double)max_mag, coh->ct); + + if (max_corr / max_mag > 0.9) { + if (coh->verbose) + fprintf(stderr, " [%d] encouraging sync word!\n", coh->frame); + coh->sync_timer = 0; + *next_sync = 1; + } else { + *next_sync = 0; } + coh->ratio = max_corr / max_mag; + } } +void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC * COHPSK_ND], + COMP ch_symb[][COHPSK_NC * COHPSK_ND]) { + int r, c, i; -void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC*COHPSK_ND], COMP ch_symb[][COHPSK_NC*COHPSK_ND]) -{ - int r, c, i; + /* update memory in symbol buffer */ - /* update memory in symbol buffer */ + for (r = 0; r < NCT_SYMB_BUF - NSYMROWPILOT; r++) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) + ct_symb_buf[r][c] = ct_symb_buf[r + NSYMROWPILOT][c]; + } - for(r=0; r<NCT_SYMB_BUF-NSYMROWPILOT; r++) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) - ct_symb_buf[r][c] = ct_symb_buf[r+NSYMROWPILOT][c]; - } - - for(r=NCT_SYMB_BUF-NSYMROWPILOT, i=0; r<NCT_SYMB_BUF; r++, i++) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) - ct_symb_buf[r][c] = ch_symb[i][c]; - } + for (r = NCT_SYMB_BUF - NSYMROWPILOT, i = 0; r < NCT_SYMB_BUF; r++, i++) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) + ct_symb_buf[r][c] = ch_symb[i][c]; + } } +int sync_state_machine(struct COHPSK *coh, int sync, int next_sync) { + float corr, mag; -int sync_state_machine(struct COHPSK *coh, int sync, int next_sync) -{ - float corr, mag; - - if (sync == 1) { - - /* check that sync is still good, fall out of sync on consecutive bad frames */ + if (sync == 1) { + /* check that sync is still good, fall out of sync on consecutive bad frames + */ - corr_with_pilots(&corr, &mag, coh, coh->ct, coh->f_fine_est); - coh->ratio = fabsf(corr)/mag; + corr_with_pilots(&corr, &mag, coh, coh->ct, coh->f_fine_est); + coh->ratio = fabsf(corr) / mag; - // printf("%f\n", cabsolute(corr)/mag); + // printf("%f\n", cabsolute(corr)/mag); - if (fabsf(corr)/mag < 0.8) - coh->sync_timer++; - else - coh->sync_timer = 0; + if (fabsf(corr) / mag < 0.8) + coh->sync_timer++; + else + coh->sync_timer = 0; - if (coh->sync_timer == 10) { - if (coh->verbose) - fprintf(stderr," [%d] lost sync ....\n", coh->frame); - next_sync = 0; - } + if (coh->sync_timer == 10) { + if (coh->verbose) fprintf(stderr, " [%d] lost sync ....\n", coh->frame); + next_sync = 0; } + } - sync = next_sync; + sync = next_sync; - return sync; + return sync; } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_mod() @@ -696,24 +686,24 @@ int sync_state_machine(struct COHPSK *coh, int sync, int next_sync) \*---------------------------------------------------------------------------*/ -void cohpsk_mod(struct COHPSK *coh, COMP tx_fdm[], int tx_bits[], int nbits) -{ - struct FDMDV *fdmdv = coh->fdmdv; - COMP tx_symb[NSYMROWPILOT][COHPSK_NC*COHPSK_ND]; - COMP tx_onesym[COHPSK_NC*COHPSK_ND]; - int r,c; - - bits_to_qpsk_symbols(tx_symb, tx_bits, nbits); - - for(r=0; r<NSYMROWPILOT; r++) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) - tx_onesym[c] = fcmult(coh->carrier_ampl[c], tx_symb[r][c]); - tx_filter_and_upconvert_coh(&tx_fdm[r*COHPSK_M], COHPSK_NC*COHPSK_ND , tx_onesym, fdmdv->tx_filter_memory, - fdmdv->phase_tx, fdmdv->freq, &fdmdv->fbb_phase_tx, fdmdv->fbb_rect); - } +void cohpsk_mod(struct COHPSK *coh, COMP tx_fdm[], int tx_bits[], int nbits) { + struct FDMDV *fdmdv = coh->fdmdv; + COMP tx_symb[NSYMROWPILOT][COHPSK_NC * COHPSK_ND]; + COMP tx_onesym[COHPSK_NC * COHPSK_ND]; + int r, c; + + bits_to_qpsk_symbols(tx_symb, tx_bits, nbits); + + for (r = 0; r < NSYMROWPILOT; r++) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) + tx_onesym[c] = fcmult(coh->carrier_ampl[c], tx_symb[r][c]); + tx_filter_and_upconvert_coh(&tx_fdm[r * COHPSK_M], COHPSK_NC * COHPSK_ND, + tx_onesym, fdmdv->tx_filter_memory, + fdmdv->phase_tx, fdmdv->freq, + &fdmdv->fbb_phase_tx, fdmdv->fbb_rect); + } } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_clip() @@ -724,21 +714,20 @@ void cohpsk_mod(struct COHPSK *coh, COMP tx_fdm[], int tx_bits[], int nbits) \*---------------------------------------------------------------------------*/ -void cohpsk_clip(COMP tx_fdm[], float clip_thresh, int n) -{ - COMP sam; - float mag; - int i; - - for(i=0; i<n; i++) { - sam = tx_fdm[i]; - mag = cabsolute(sam); - if (mag > clip_thresh) { - sam = fcmult(clip_thresh/mag, sam); - } - tx_fdm[i] = sam; +void cohpsk_clip(COMP tx_fdm[], float clip_thresh, int n) { + COMP sam; + float mag; + int i; + + for (i = 0; i < n; i++) { + sam = tx_fdm[i]; + mag = cabsolute(sam); + if (mag > clip_thresh) { + sam = fcmult(clip_thresh / mag, sam); } - } + tx_fdm[i] = sam; + } +} /*---------------------------------------------------------------------------*\ @@ -752,42 +741,41 @@ void cohpsk_clip(COMP tx_fdm[], float clip_thresh, int n) \*---------------------------------------------------------------------------*/ -void fdm_downconvert_coh(COMP rx_baseband[][COHPSK_M+COHPSK_M/P], int Nc, COMP rx_fdm[], COMP phase_rx[], COMP freq[], int nin) -{ - int i,c; - float mag; +void fdm_downconvert_coh(COMP rx_baseband[][COHPSK_M + COHPSK_M / P], int Nc, + COMP rx_fdm[], COMP phase_rx[], COMP freq[], int nin) { + int i, c; + float mag; - /* maximum number of input samples to demod */ + /* maximum number of input samples to demod */ - assert(nin <= (COHPSK_M+COHPSK_M/P)); + assert(nin <= (COHPSK_M + COHPSK_M / P)); - /* downconvert */ + /* downconvert */ - for (c=0; c<Nc; c++) - for (i=0; i<nin; i++) { - phase_rx[c] = cmult(phase_rx[c], freq[c]); - rx_baseband[c][i] = cmult(rx_fdm[i], cconj(phase_rx[c])); - } + for (c = 0; c < Nc; c++) + for (i = 0; i < nin; i++) { + phase_rx[c] = cmult(phase_rx[c], freq[c]); + rx_baseband[c][i] = cmult(rx_fdm[i], cconj(phase_rx[c])); + } - /* normalise digital oscilators as the magnitude can drift over time */ + /* normalise digital oscilators as the magnitude can drift over time */ - for (c=0; c<Nc; c++) { - mag = cabsolute(phase_rx[c]); - phase_rx[c].real /= mag; - phase_rx[c].imag /= mag; - } + for (c = 0; c < Nc; c++) { + mag = cabsolute(phase_rx[c]); + phase_rx[c].real /= mag; + phase_rx[c].imag /= mag; + } } /* Determine if we can use vector ops below. */ -#if __GNUC__ > 4 || \ - (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || \ - (__GNUC_MINOR__ == 6 && \ - __GNUC_PATCHLEVEL__ > 0))) +#if __GNUC__ > 4 || \ + (__GNUC__ == 4 && \ + (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0))) #define USE_VECTOR_OPS 1 #elif __clang_major__ > 3 || \ - (__clang_minor__ == 3 && (__clang_minor__ > 7 || \ - (__clang_minor__ == 7 && \ - __clang_patchlevel__ > 0))) + (__clang_minor__ == 3 && \ + (__clang_minor__ > 7 || \ + (__clang_minor__ == 7 && __clang_patchlevel__ > 0))) #define USE_VECTOR_OPS 1 #endif @@ -799,11 +787,11 @@ void fdm_downconvert_coh(COMP rx_baseband[][COHPSK_M+COHPSK_M/P], int Nc, COMP r typedef float32x4_t float4; #else /* Vector of 4 floating point numbers for use by the below function */ -typedef float float4 __attribute__ ((vector_size (16))); -#endif // __ARM_NEON +typedef float float4 __attribute__((vector_size(16))); +#endif // __ARM_NEON #endif /* USE_VECTOR_OPS */ - + /*---------------------------------------------------------------------------*\ FUNCTION....: rx_filter_coh() @@ -816,117 +804,122 @@ typedef float float4 __attribute__ ((vector_size (16))); \*---------------------------------------------------------------------------*/ -inline extern void rx_filter_coh(COMP rx_filt[COHPSK_NC*COHPSK_ND][P+1], int Nc, COMP rx_baseband[COHPSK_NC*COHPSK_ND][COHPSK_M+COHPSK_M/P], COMP rx_filter_memory[COHPSK_NC*COHPSK_ND][COHPSK_NFILTER], int nin) -{ - int c,i,j,k,l; - int n=COHPSK_M/P; - - /* rx filter each symbol, generate P filtered output samples for - each symbol. Note we keep filter memory at rate M, it's just - the filter output at rate P */ - - for(i=0, j=0; i<nin; i+=n,j++) - { - - /* latest input sample */ - - for(c=0; c<Nc; c++) - { - rx_filt[c][j].real = 0.0; - rx_filt[c][j].imag = 0.0; - - /* - This call is equivalent to the code below: - - for(k=COHPSK_NFILTER-n,l=i; k<COHPSK_NFILTER; k++,l++) - { - rx_filter_memory[c][k] = rx_baseband[c][l]; - } - */ - memcpy( - &rx_filter_memory[c][COHPSK_NFILTER-n], - &rx_baseband[c][i], - sizeof(COMP)*n); - - /* convolution (filtering) */ - +inline extern void rx_filter_coh( + COMP rx_filt[COHPSK_NC * COHPSK_ND][P + 1], int Nc, + COMP rx_baseband[COHPSK_NC * COHPSK_ND][COHPSK_M + COHPSK_M / P], + COMP rx_filter_memory[COHPSK_NC * COHPSK_ND][COHPSK_NFILTER], int nin) { + int c, i, j, k, l; + int n = COHPSK_M / P; + + /* rx filter each symbol, generate P filtered output samples for + each symbol. Note we keep filter memory at rate M, it's just + the filter output at rate P */ + + for (i = 0, j = 0; i < nin; i += n, j++) { + /* latest input sample */ + + for (c = 0; c < Nc; c++) { + rx_filt[c][j].real = 0.0; + rx_filt[c][j].imag = 0.0; + + /* + This call is equivalent to the code below: + + for(k=COHPSK_NFILTER-n,l=i; k<COHPSK_NFILTER; k++,l++) + { + rx_filter_memory[c][k] = rx_baseband[c][l]; + } + */ + memcpy(&rx_filter_memory[c][COHPSK_NFILTER - n], &rx_baseband[c][i], + sizeof(COMP) * n); + + /* convolution (filtering) */ + #if USE_VECTOR_OPS - /* assumes COHPSK_NFILTER is divisible by 2 */ + /* assumes COHPSK_NFILTER is divisible by 2 */ #ifdef __ARM_NEON - float4 resultVec = vdupq_n_f32(0); + float4 resultVec = vdupq_n_f32(0); #else - float4 resultVec = {0, 0, 0, 0}; -#endif // __ARM_NEON + float4 resultVec = {0, 0, 0, 0}; +#endif // __ARM_NEON - for(k=0, l=0; k<COHPSK_NFILTER; k += 2, l += 4) - { + for (k = 0, l = 0; k < COHPSK_NFILTER; k += 2, l += 4) { #ifdef __ARM_NEON - // Fetch gt_alpha5_root_coh and place it into a vector for later use. - // First half at index k, second half at index k + 1. - float4 alpha5Vec = vld1q_f32((const float32_t*)>_alpha5_root_coh_neon[l]); - - // Load two COMP elements (each containing two floats) into 4 element vector. - float4 filterMemVec = vld1q_f32((const float32_t *)&rx_filter_memory[c][k]); - - // Multiply each element of filterMemVec by alpha5Vec from above and add to the - // running total in resultVec. Odd indices are reals, even imag. - resultVec = vmlaq_f32(resultVec, alpha5Vec, filterMemVec); + // Fetch gt_alpha5_root_coh and place it into a vector for later use. + // First half at index k, second half at index k + 1. + float4 alpha5Vec = + vld1q_f32((const float32_t *)>_alpha5_root_coh_neon[l]); + + // Load two COMP elements (each containing two floats) into 4 element + // vector. + float4 filterMemVec = + vld1q_f32((const float32_t *)&rx_filter_memory[c][k]); + + // Multiply each element of filterMemVec by alpha5Vec from above and add + // to the running total in resultVec. Odd indices are reals, even imag. + resultVec = vmlaq_f32(resultVec, alpha5Vec, filterMemVec); #else - // Fetch gt_alpha5_root_coh and place it into a vector for later use. - // First half at index k, second half at index k + 1. - float4 alpha5Vec = { - gt_alpha5_root_coh_neon[l], gt_alpha5_root_coh_neon[l + 1], gt_alpha5_root_coh_neon[l + 2], gt_alpha5_root_coh_neon[l + 3], - }; - - // Load two COMP elements (each containing two floats) into 4 element vector. - float4 filterMemVec = { - rx_filter_memory[c][k].real, rx_filter_memory[c][k].imag, rx_filter_memory[c][k + 1].real, rx_filter_memory[c][k + 1].imag, - }; - - // Multiply each element of filterMemVec by alpha5Vec from above and add to the - // running total in resultVec. Odd indices are reals, even imag. - resultVec += alpha5Vec * filterMemVec; - -#endif // __ARM_NEON - } - - // Add total from resultVec to rx_filt. - rx_filt[c][j].real += resultVec[0] + resultVec[2]; - rx_filt[c][j].imag += resultVec[1] + resultVec[3]; + // Fetch gt_alpha5_root_coh and place it into a vector for later use. + // First half at index k, second half at index k + 1. + float4 alpha5Vec = { + gt_alpha5_root_coh_neon[l], + gt_alpha5_root_coh_neon[l + 1], + gt_alpha5_root_coh_neon[l + 2], + gt_alpha5_root_coh_neon[l + 3], + }; + + // Load two COMP elements (each containing two floats) into 4 element + // vector. + float4 filterMemVec = { + rx_filter_memory[c][k].real, + rx_filter_memory[c][k].imag, + rx_filter_memory[c][k + 1].real, + rx_filter_memory[c][k + 1].imag, + }; + + // Multiply each element of filterMemVec by alpha5Vec from above and add + // to the running total in resultVec. Odd indices are reals, even imag. + resultVec += alpha5Vec * filterMemVec; + +#endif // __ARM_NEON + } + + // Add total from resultVec to rx_filt. + rx_filt[c][j].real += resultVec[0] + resultVec[2]; + rx_filt[c][j].imag += resultVec[1] + resultVec[3]; #else - for(k=0; k<COHPSK_NFILTER; k++) - { - /* - Equivalent to this code: - - rx_filt[c][j] = cadd(rx_filt[c][j], fcmult(gt_alpha5_root_coh[k], rx_filter_memory[c][k])); - */ - rx_filt[c][j].real += gt_alpha5_root_coh[k] * rx_filter_memory[c][k].real; - rx_filt[c][j].imag += gt_alpha5_root_coh[k] * rx_filter_memory[c][k].imag; - } + for (k = 0; k < COHPSK_NFILTER; k++) { + /* + Equivalent to this code: + + rx_filt[c][j] = cadd(rx_filt[c][j], fcmult(gt_alpha5_root_coh[k], + rx_filter_memory[c][k])); + */ + rx_filt[c][j].real += + gt_alpha5_root_coh[k] * rx_filter_memory[c][k].real; + rx_filt[c][j].imag += + gt_alpha5_root_coh[k] * rx_filter_memory[c][k].imag; + } #endif /* USE_VECTOR_OPS */ - - /* - make room for next input sample. - - The below call is equivalent to this code: - - for(k=0,l=n; k<COHPSK_NFILTER-n; k++,l++) - { - rx_filter_memory[c][k] = rx_filter_memory[c][l]; - } - */ - memmove( - &rx_filter_memory[c][0], - &rx_filter_memory[c][n], - sizeof(COMP)*(COHPSK_NFILTER-n)); - } + + /* + make room for next input sample. + + The below call is equivalent to this code: + + for(k=0,l=n; k<COHPSK_NFILTER-n; k++,l++) + { + rx_filter_memory[c][k] = rx_filter_memory[c][l]; + } + */ + memmove(&rx_filter_memory[c][0], &rx_filter_memory[c][n], + sizeof(COMP) * (COHPSK_NFILTER - n)); } - - assert(j <= (P+1)); /* check for any over runs */ -} + } + assert(j <= (P + 1)); /* check for any over runs */ +} /*---------------------------------------------------------------------------*\ @@ -939,138 +932,149 @@ inline extern void rx_filter_coh(COMP rx_filt[COHPSK_NC*COHPSK_ND][P+1], int Nc, \*---------------------------------------------------------------------------*/ -void fdmdv_freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, - COMP *foff_phase_rect, int nin) -{ - COMP foff_rect; - float mag; - float tau = 2.0f * M_PI; - float result = tau * foff/Fs; - int i; - - foff_rect.real = cosf(result); - foff_rect.imag = sinf(result); - for(i=0; i<nin; i++) { - *foff_phase_rect = cmult(*foff_phase_rect, foff_rect); - rx_fdm_fcorr[i] = cmult(rx_fdm[i], *foff_phase_rect); - } - - /* normalise digital oscillator as the magnitude can drift over time */ - - mag = cabsolute(*foff_phase_rect); - foff_phase_rect->real /= mag; - foff_phase_rect->imag /= mag; +void fdmdv_freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, + float Fs, COMP *foff_phase_rect, int nin) { + COMP foff_rect; + float mag; + float tau = 2.0f * M_PI; + float result = tau * foff / Fs; + int i; + + foff_rect.real = cosf(result); + foff_rect.imag = sinf(result); + for (i = 0; i < nin; i++) { + *foff_phase_rect = cmult(*foff_phase_rect, foff_rect); + rx_fdm_fcorr[i] = cmult(rx_fdm[i], *foff_phase_rect); + } + + /* normalise digital oscillator as the magnitude can drift over time */ + + mag = cabsolute(*foff_phase_rect); + foff_phase_rect->real /= mag; + foff_phase_rect->imag /= mag; } +void rate_Fs_rx_processing(struct COHPSK *coh, + COMP ch_symb[][COHPSK_NC * COHPSK_ND], + COMP ch_fdm_frame[], float *f_est, int nsymb, + int nin, int freq_track) { + struct FDMDV *fdmdv = coh->fdmdv; + int r, c, i, ch_fdm_frame_index; + COMP rx_fdm_frame_bb[COHPSK_M + COHPSK_M / P]; + COMP rx_baseband[COHPSK_NC * COHPSK_ND][COHPSK_M + COHPSK_M / P]; + COMP rx_filt[COHPSK_NC * COHPSK_ND][P + 1]; + float env[NT * P], rx_timing; + COMP rx_onesym[COHPSK_NC * COHPSK_ND]; + float beta, g; + COMP adiff, amod_strip, mod_strip; + + ch_fdm_frame_index = 0; + rx_timing = 0; + + for (r = 0; r < nsymb; r++) { + fdmdv_freq_shift_coh(rx_fdm_frame_bb, &ch_fdm_frame[ch_fdm_frame_index], + -(*f_est), COHPSK_FS, &fdmdv->fbb_phase_rx, nin); + ch_fdm_frame_index += nin; + fdm_downconvert_coh(rx_baseband, COHPSK_NC * COHPSK_ND, rx_fdm_frame_bb, + fdmdv->phase_rx, fdmdv->freq, nin); + rx_filter_coh(rx_filt, COHPSK_NC * COHPSK_ND, rx_baseband, + coh->rx_filter_memory, nin); + rx_timing = rx_est_timing(rx_onesym, fdmdv->Nc, rx_filt, + fdmdv->rx_filter_mem_timing, env, nin, COHPSK_M); + + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + ch_symb[r][c] = rx_onesym[c]; + } -void rate_Fs_rx_processing(struct COHPSK *coh, COMP ch_symb[][COHPSK_NC*COHPSK_ND], COMP ch_fdm_frame[], float *f_est, int nsymb, int nin, int freq_track) -{ - struct FDMDV *fdmdv = coh->fdmdv; - int r, c, i, ch_fdm_frame_index; - COMP rx_fdm_frame_bb[COHPSK_M+COHPSK_M/P]; - COMP rx_baseband[COHPSK_NC*COHPSK_ND][COHPSK_M+COHPSK_M/P]; - COMP rx_filt[COHPSK_NC*COHPSK_ND][P+1]; - float env[NT*P], rx_timing; - COMP rx_onesym[COHPSK_NC*COHPSK_ND]; - float beta, g; - COMP adiff, amod_strip, mod_strip; - - ch_fdm_frame_index = 0; - rx_timing = 0; - - for (r=0; r<nsymb; r++) { - fdmdv_freq_shift_coh(rx_fdm_frame_bb, &ch_fdm_frame[ch_fdm_frame_index], -(*f_est), COHPSK_FS, &fdmdv->fbb_phase_rx, nin); - ch_fdm_frame_index += nin; - fdm_downconvert_coh(rx_baseband, COHPSK_NC*COHPSK_ND, rx_fdm_frame_bb, fdmdv->phase_rx, fdmdv->freq, nin); - rx_filter_coh(rx_filt, COHPSK_NC*COHPSK_ND, rx_baseband, coh->rx_filter_memory, nin); - rx_timing = rx_est_timing(rx_onesym, fdmdv->Nc, rx_filt, fdmdv->rx_filter_mem_timing, env, nin, COHPSK_M); - - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - ch_symb[r][c] = rx_onesym[c]; - } + /* freq tracking, see test_ftrack.m for unit test. Placed in + this function as it needs to work on a symbol by symbol + abasis rather than frame by frame. This means the control + loop operates at a sample rate of Rs = 50Hz for say 1 Hz/s + drift. */ + + if (freq_track) { + beta = 0.005; + g = 0.2; + + /* combine difference on phase from last symbol over Nc carriers */ + + mod_strip.real = 0.0; + mod_strip.imag = 0.0; + for (c = 0; c < fdmdv->Nc + 1; c++) { + // printf("rx_onesym[%d] %f %f prev_rx_symbols[%d] %f %f\n", c, + // rx_onesym[c].real, rx_onesym[c].imag, + // fdmdv->prev_rx_symbols[c].real, + // fdmdv->prev_rx_symbols[c].imag); + adiff = cmult(rx_onesym[c], cconj(fdmdv->prev_rx_symbols[c])); + fdmdv->prev_rx_symbols[c] = rx_onesym[c]; + + /* 4th power strips QPSK modulation, by multiplying phase by 4 + Using the abs value of the real coord was found to help + non-linear issues when noise power was large. */ + + amod_strip = cmult(adiff, adiff); + amod_strip = cmult(amod_strip, amod_strip); + amod_strip.real = fabsf(amod_strip.real); + mod_strip = cadd(mod_strip, amod_strip); + } + // printf("modstrip: %f %f\n", mod_strip.real, mod_strip.imag); - /* freq tracking, see test_ftrack.m for unit test. Placed in - this function as it needs to work on a symbol by symbol - abasis rather than frame by frame. This means the control - loop operates at a sample rate of Rs = 50Hz for say 1 Hz/s - drift. */ - - if (freq_track) { - beta = 0.005; - g = 0.2; - - /* combine difference on phase from last symbol over Nc carriers */ - - mod_strip.real = 0.0; mod_strip.imag = 0.0; - for(c=0; c<fdmdv->Nc+1; c++) { - //printf("rx_onesym[%d] %f %f prev_rx_symbols[%d] %f %f\n", c, rx_onesym[c].real, rx_onesym[c].imag, - // fdmdv->prev_rx_symbols[c].real, fdmdv->prev_rx_symbols[c].imag); - adiff = cmult(rx_onesym[c], cconj(fdmdv->prev_rx_symbols[c])); - fdmdv->prev_rx_symbols[c] = rx_onesym[c]; - - /* 4th power strips QPSK modulation, by multiplying phase by 4 - Using the abs value of the real coord was found to help - non-linear issues when noise power was large. */ - - amod_strip = cmult(adiff, adiff); - amod_strip = cmult(amod_strip, amod_strip); - amod_strip.real = fabsf(amod_strip.real); - mod_strip = cadd(mod_strip, amod_strip); - } - //printf("modstrip: %f %f\n", mod_strip.real, mod_strip.imag); - - /* loop filter made up of 1st order IIR plus integrator. Integerator - was found to be reqd */ - - fdmdv->foff_filt = (1.0f-beta)*fdmdv->foff_filt + beta*atan2f(mod_strip.imag, mod_strip.real); - //printf("foff_filt: %f angle: %f\n", fdmdv->foff_filt, atan2f(mod_strip.imag, mod_strip.real)); - *f_est += g*fdmdv->foff_filt; - } + /* loop filter made up of 1st order IIR plus integrator. Integerator + was found to be reqd */ - /* Optional logging used for testing against Octave version */ - - if (coh->rx_baseband_log) { - assert(nin <= (COHPSK_M+COHPSK_M/P)); - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - for(i=0; i<nin; i++) { - coh->rx_baseband_log[c*coh->rx_baseband_log_col_sz + coh->rx_baseband_log_col_index + i] = rx_baseband[c][i]; - } - } - coh->rx_baseband_log_col_index += nin; - assert(coh->rx_baseband_log_col_index <= coh->rx_baseband_log_col_sz); - } + fdmdv->foff_filt = (1.0f - beta) * fdmdv->foff_filt + + beta * atan2f(mod_strip.imag, mod_strip.real); + // printf("foff_filt: %f angle: %f\n", fdmdv->foff_filt, + // atan2f(mod_strip.imag, mod_strip.real)); + *f_est += g * fdmdv->foff_filt; + } - if (coh->rx_filt_log) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - for(i=0; i<nin/(COHPSK_M/P); i++) { - coh->rx_filt_log[c*coh->rx_filt_log_col_sz + coh->rx_filt_log_col_index + i] = rx_filt[c][i]; - } - } - coh->rx_filt_log_col_index += nin/(COHPSK_M/P); - } + /* Optional logging used for testing against Octave version */ - if (coh->ch_symb_log) { - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { - coh->ch_symb_log[coh->ch_symb_log_r*COHPSK_NC*COHPSK_ND + c] = ch_symb[r][c]; - } - coh->ch_symb_log_r++; + if (coh->rx_baseband_log) { + assert(nin <= (COHPSK_M + COHPSK_M / P)); + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + for (i = 0; i < nin; i++) { + coh->rx_baseband_log[c * coh->rx_baseband_log_col_sz + + coh->rx_baseband_log_col_index + i] = + rx_baseband[c][i]; } + } + coh->rx_baseband_log_col_index += nin; + assert(coh->rx_baseband_log_col_index <= coh->rx_baseband_log_col_sz); + } - if (coh->rx_timing_log) { - coh->rx_timing_log[coh->rx_timing_log_index] = rx_timing; - coh->rx_timing_log_index++; - //printf("rx_timing_log_index: %d\n", coh->rx_timing_log_index); + if (coh->rx_filt_log) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + for (i = 0; i < nin / (COHPSK_M / P); i++) { + coh->rx_filt_log[c * coh->rx_filt_log_col_sz + + coh->rx_filt_log_col_index + i] = rx_filt[c][i]; } + } + coh->rx_filt_log_col_index += nin / (COHPSK_M / P); + } - /* we only allow a timing shift on one symbol per frame */ + if (coh->ch_symb_log) { + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + coh->ch_symb_log[coh->ch_symb_log_r * COHPSK_NC * COHPSK_ND + c] = + ch_symb[r][c]; + } + coh->ch_symb_log_r++; + } - if (nin != COHPSK_M) - nin = COHPSK_M; + if (coh->rx_timing_log) { + coh->rx_timing_log[coh->rx_timing_log_index] = rx_timing; + coh->rx_timing_log_index++; + // printf("rx_timing_log_index: %d\n", coh->rx_timing_log_index); } - coh->rx_timing = rx_timing; -} + /* we only allow a timing shift on one symbol per frame */ + + if (nin != COHPSK_M) nin = COHPSK_M; + } + coh->rx_timing = rx_timing; +} /*---------------------------------------------------------------------------*\ @@ -1087,141 +1091,151 @@ void rate_Fs_rx_processing(struct COHPSK *coh, COMP ch_symb[][COHPSK_NC*COHPSK_N \*---------------------------------------------------------------------------*/ -void cohpsk_demod(struct COHPSK *coh, float rx_bits[], int *sync_good, COMP rx_fdm[], int *nin_frame) -{ - COMP ch_symb[NSW*NSYMROWPILOT][COHPSK_NC*COHPSK_ND]; - int i, j, sync, anext_sync, next_sync, nin, r, c; - float max_ratio, f_est; - - assert(*nin_frame <= COHPSK_MAX_SAMPLES_PER_FRAME); - - next_sync = sync = coh->sync; +void cohpsk_demod(struct COHPSK *coh, float rx_bits[], int *sync_good, + COMP rx_fdm[], int *nin_frame) { + COMP ch_symb[NSW * NSYMROWPILOT][COHPSK_NC * COHPSK_ND]; + int i, j, sync, anext_sync, next_sync, nin, r, c; + float max_ratio, f_est; - for (i=0; i<NSW*NSYMROWPILOT*COHPSK_M-*nin_frame; i++) - coh->ch_fdm_frame_buf[i] = coh->ch_fdm_frame_buf[i+*nin_frame]; - //printf("nin_frame: %d i: %d i+nin_frame: %d\n", *nin_frame, i, i+*nin_frame); - for (j=0; i<NSW*NSYMROWPILOT*COHPSK_M; i++,j++) - coh->ch_fdm_frame_buf[i] = rx_fdm[j]; - //printf("i: %d j: %d rx_fdm[0]: %f %f\n", i,j, rx_fdm[0].real, rx_fdm[0].imag); + assert(*nin_frame <= COHPSK_MAX_SAMPLES_PER_FRAME); - /* if out of sync do Initial Freq offset estimation using NSW frames to flush out filter memories */ + next_sync = sync = coh->sync; - if (sync == 0) { + for (i = 0; i < NSW * NSYMROWPILOT * COHPSK_M - *nin_frame; i++) + coh->ch_fdm_frame_buf[i] = coh->ch_fdm_frame_buf[i + *nin_frame]; + // printf("nin_frame: %d i: %d i+nin_frame: %d\n", *nin_frame, i, + // i+*nin_frame); + for (j = 0; i < NSW * NSYMROWPILOT * COHPSK_M; i++, j++) + coh->ch_fdm_frame_buf[i] = rx_fdm[j]; + // printf("i: %d j: %d rx_fdm[0]: %f %f\n", i,j, rx_fdm[0].real, + // rx_fdm[0].imag); - /* we can test +/- 20Hz, so we break this up into 3 tests to cover +/- 60Hz */ + /* if out of sync do Initial Freq offset estimation using NSW frames to flush + * out filter memories */ - max_ratio = 0.0; - f_est = 0.0; - for (coh->f_est = FDMDV_FCENTRE-40.0; coh->f_est <= FDMDV_FCENTRE+40.0; coh->f_est += 40.0) { - - if (coh->verbose) - fprintf(stderr, " [%d] acohpsk.f_est: %f +/- 20\n", coh->frame, (double)coh->f_est); - - /* we are out of sync so reset f_est and process two frames to clean out memories */ - - rate_Fs_rx_processing(coh, ch_symb, coh->ch_fdm_frame_buf, &coh->f_est, NSW*NSYMROWPILOT, COHPSK_M, 0); - for (i=0; i<NSW-1; i++) { - update_ct_symb_buf(coh->ct_symb_buf, &ch_symb[i*NSYMROWPILOT]); - } - frame_sync_fine_freq_est(coh, &ch_symb[(NSW-1)*NSYMROWPILOT], sync, &anext_sync); - - if (anext_sync == 1) { - //printf(" [%d] acohpsk.ratio: %f\n", f, coh->ratio); - if (coh->ratio > max_ratio) { - max_ratio = coh->ratio; - f_est = coh->f_est - coh->f_fine_est; - next_sync = anext_sync; - } - } - } - - if (next_sync == 1) { - - /* we've found a sync candidate! - re-process last NSW frames with adjusted f_est then check again */ - - coh->f_est = f_est; - - if (coh->verbose) - fprintf(stderr, " [%d] trying sync and f_est: %f\n", coh->frame, (double)coh->f_est); - - rate_Fs_rx_processing(coh, ch_symb, coh->ch_fdm_frame_buf, &coh->f_est, NSW*NSYMROWPILOT, COHPSK_M, 0); - for (i=0; i<NSW-1; i++) { - update_ct_symb_buf(coh->ct_symb_buf, &ch_symb[i*NSYMROWPILOT]); - } - /* - for(i=0; i<NSW*NSYMROWPILOT; i++) { - printf("%f %f\n", ch_symb[i][0].real, ch_symb[i][0].imag); - } - */ - /* - for(i=0; i<NCT_SYMB_BUF; i++) { - printf("%f %f\n", coh->ct_symb_buf[i][0].real, coh->ct_symb_buf[i][0].imag); - } - */ - frame_sync_fine_freq_est(coh, &ch_symb[(NSW-1)*NSYMROWPILOT], sync, &next_sync); - - if (fabsf(coh->f_fine_est) > 2.0) { - if (coh->verbose) - fprintf(stderr, " [%d] Hmm %f is a bit big :(\n", coh->frame, (double)coh->f_fine_est); - next_sync = 0; - } - } - - if (next_sync == 1) { - /* OK we are in sync! - demodulate first frame (demod completed below) */ + if (sync == 0) { + /* we can test +/- 20Hz, so we break this up into 3 tests to cover +/- 60Hz + */ - if (coh->verbose) - fprintf(stderr, " [%d] in sync! f_est: %f ratio: %f \n", coh->frame, (double)coh->f_est, (double)coh->ratio); - for(r=0; r<NSYMROWPILOT+2; r++) - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) - coh->ct_symb_ff_buf[r][c] = coh->ct_symb_buf[coh->ct+r][c]; + max_ratio = 0.0; + f_est = 0.0; + for (coh->f_est = FDMDV_FCENTRE - 40.0; coh->f_est <= FDMDV_FCENTRE + 40.0; + coh->f_est += 40.0) { + if (coh->verbose) + fprintf(stderr, " [%d] acohpsk.f_est: %f +/- 20\n", coh->frame, + (double)coh->f_est); + + /* we are out of sync so reset f_est and process two frames to clean out + * memories */ + + rate_Fs_rx_processing(coh, ch_symb, coh->ch_fdm_frame_buf, &coh->f_est, + NSW * NSYMROWPILOT, COHPSK_M, 0); + for (i = 0; i < NSW - 1; i++) { + update_ct_symb_buf(coh->ct_symb_buf, &ch_symb[i * NSYMROWPILOT]); + } + frame_sync_fine_freq_est(coh, &ch_symb[(NSW - 1) * NSYMROWPILOT], sync, + &anext_sync); + + if (anext_sync == 1) { + // printf(" [%d] acohpsk.ratio: %f\n", f, coh->ratio); + if (coh->ratio > max_ratio) { + max_ratio = coh->ratio; + f_est = coh->f_est - coh->f_fine_est; + next_sync = anext_sync; } + } } - /* If in sync just do sample rate processing on latest frame */ + if (next_sync == 1) { + /* we've found a sync candidate! + re-process last NSW frames with adjusted f_est then check again */ - if (sync == 1) { - rate_Fs_rx_processing(coh, ch_symb, rx_fdm, &coh->f_est, NSYMROWPILOT, coh->nin, 1); - frame_sync_fine_freq_est(coh, ch_symb, sync, &next_sync); + coh->f_est = f_est; - for(r=0; r<2; r++) - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) - coh->ct_symb_ff_buf[r][c] = coh->ct_symb_ff_buf[r+NSYMROWPILOT][c]; - for(; r<NSYMROWPILOT+2; r++) - for(c=0; c<COHPSK_NC*COHPSK_ND; c++) - coh->ct_symb_ff_buf[r][c] = coh->ct_symb_buf[coh->ct+r][c]; - } + if (coh->verbose) + fprintf(stderr, " [%d] trying sync and f_est: %f\n", coh->frame, + (double)coh->f_est); - /* if we are in sync complete demodulation with symbol rate processing */ + rate_Fs_rx_processing(coh, ch_symb, coh->ch_fdm_frame_buf, &coh->f_est, + NSW * NSYMROWPILOT, COHPSK_M, 0); + for (i = 0; i < NSW - 1; i++) { + update_ct_symb_buf(coh->ct_symb_buf, &ch_symb[i * NSYMROWPILOT]); + } + /* + for(i=0; i<NSW*NSYMROWPILOT; i++) { + printf("%f %f\n", ch_symb[i][0].real, ch_symb[i][0].imag); + } + */ + /* + for(i=0; i<NCT_SYMB_BUF; i++) { + printf("%f %f\n", coh->ct_symb_buf[i][0].real, + coh->ct_symb_buf[i][0].imag); + } + */ + frame_sync_fine_freq_est(coh, &ch_symb[(NSW - 1) * NSYMROWPILOT], sync, + &next_sync); - *sync_good = 0; - if ((next_sync == 1) || (sync == 1)) { - qpsk_symbols_to_bits(coh, rx_bits, coh->ct_symb_ff_buf); - *sync_good = 1; + if (fabsf(coh->f_fine_est) > 2.0) { + if (coh->verbose) + fprintf(stderr, " [%d] Hmm %f is a bit big :(\n", coh->frame, + (double)coh->f_fine_est); + next_sync = 0; + } } - sync = sync_state_machine(coh, sync, next_sync); - coh->sync = sync; - - /* work out how many samples we need for the next call to account - for differences in tx and rx sample clocks */ + if (next_sync == 1) { + /* OK we are in sync! + demodulate first frame (demod completed below) */ - nin = COHPSK_M; - if (sync == 1) { - if (coh->rx_timing > COHPSK_M/P) - nin = COHPSK_M + COHPSK_M/P; - if (coh->rx_timing < -COHPSK_M/P) - nin = COHPSK_M - COHPSK_M/P; + if (coh->verbose) + fprintf(stderr, " [%d] in sync! f_est: %f ratio: %f \n", coh->frame, + (double)coh->f_est, (double)coh->ratio); + for (r = 0; r < NSYMROWPILOT + 2; r++) + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) + coh->ct_symb_ff_buf[r][c] = coh->ct_symb_buf[coh->ct + r][c]; } - coh->nin = nin; - *nin_frame = (NSYMROWPILOT-1)*COHPSK_M + nin; - //if (coh->verbose) - // fprintf(stderr, "%f %d %d\n", coh->rx_timing, nin, *nin_frame); + } + + /* If in sync just do sample rate processing on latest frame */ + + if (sync == 1) { + rate_Fs_rx_processing(coh, ch_symb, rx_fdm, &coh->f_est, NSYMROWPILOT, + coh->nin, 1); + frame_sync_fine_freq_est(coh, ch_symb, sync, &next_sync); + + for (r = 0; r < 2; r++) + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) + coh->ct_symb_ff_buf[r][c] = coh->ct_symb_ff_buf[r + NSYMROWPILOT][c]; + for (; r < NSYMROWPILOT + 2; r++) + for (c = 0; c < COHPSK_NC * COHPSK_ND; c++) + coh->ct_symb_ff_buf[r][c] = coh->ct_symb_buf[coh->ct + r][c]; + } + + /* if we are in sync complete demodulation with symbol rate processing */ + + *sync_good = 0; + if ((next_sync == 1) || (sync == 1)) { + qpsk_symbols_to_bits(coh, rx_bits, coh->ct_symb_ff_buf); + *sync_good = 1; + } + + sync = sync_state_machine(coh, sync, next_sync); + coh->sync = sync; + + /* work out how many samples we need for the next call to account + for differences in tx and rx sample clocks */ + + nin = COHPSK_M; + if (sync == 1) { + if (coh->rx_timing > COHPSK_M / P) nin = COHPSK_M + COHPSK_M / P; + if (coh->rx_timing < -COHPSK_M / P) nin = COHPSK_M - COHPSK_M / P; + } + coh->nin = nin; + *nin_frame = (NSYMROWPILOT - 1) * COHPSK_M + nin; + // if (coh->verbose) + // fprintf(stderr, "%f %d %d\n", coh->rx_timing, nin, *nin_frame); } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_fs_offset() @@ -1232,31 +1246,31 @@ void cohpsk_demod(struct COHPSK *coh, float rx_bits[], int *sync_good, COMP rx_f \*---------------------------------------------------------------------------*/ -int cohpsk_fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) -{ - double f; - double tin = 0.0; - double step = 1.0 + sample_rate_ppm/1E6; - int t1, t2; - int tout = 0; - - while (tin < (double) n) { - t1 = (int) floor(tin); - t2 = (int) ceil(tin); - f = tin - (double) t1; - - out[tout].real = ((double)1.0-f)*(double)in[t1].real + f*(double)in[t2].real; - out[tout].imag = ((double)1.0-f)*(double)in[t1].imag + f*(double)in[t2].imag; - - tin += step; - tout++; - //printf("tin: %f tout: %d f: %f\n", tin, tout, f); - } - - return tout; +int cohpsk_fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) { + double f; + double tin = 0.0; + double step = 1.0 + sample_rate_ppm / 1E6; + int t1, t2; + int tout = 0; + + while (tin < (double)n) { + t1 = (int)floor(tin); + t2 = (int)ceil(tin); + f = tin - (double)t1; + + out[tout].real = + ((double)1.0 - f) * (double)in[t1].real + f * (double)in[t2].real; + out[tout].imag = + ((double)1.0 - f) * (double)in[t1].imag + f * (double)in[t2].imag; + + tin += step; + tout++; + // printf("tin: %f tout: %d f: %f\n", tin, tout, f); + } + + return tout; } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_get_demod_stats() @@ -1267,54 +1281,51 @@ int cohpsk_fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) \*---------------------------------------------------------------------------*/ -void cohpsk_get_demod_stats(struct COHPSK *coh, struct MODEM_STATS *stats) -{ - float new_snr_est; - +void cohpsk_get_demod_stats(struct COHPSK *coh, struct MODEM_STATS *stats) { + float new_snr_est; + #ifndef __EMBEDDED__ - float spi_4 = M_PI/4.0f; - COMP pi_4; - pi_4.real = cosf(spi_4); - pi_4.imag = sinf(spi_4); + float spi_4 = M_PI / 4.0f; + COMP pi_4; + pi_4.real = cosf(spi_4); + pi_4.imag = sinf(spi_4); #endif - stats->Nc = COHPSK_NC*COHPSK_ND; - assert(stats->Nc <= MODEM_STATS_NC_MAX); - new_snr_est = 20.0f * log10f((coh->sig_rms+1E-6f)/(coh->noise_rms+1E-6f)) - 10.0f*log10f(3000.0f/700.0f); - stats->snr_est = 0.9f*stats->snr_est + 0.1f*new_snr_est; + stats->Nc = COHPSK_NC * COHPSK_ND; + assert(stats->Nc <= MODEM_STATS_NC_MAX); + new_snr_est = + 20.0f * log10f((coh->sig_rms + 1E-6f) / (coh->noise_rms + 1E-6f)) - + 10.0f * log10f(3000.0f / 700.0f); + stats->snr_est = 0.9f * stats->snr_est + 0.1f * new_snr_est; - //fprintf(stderr, "sig_rms: %f noise_rms: %f snr_est: %f\n", coh->sig_rms, coh->noise_rms, stats->snr_est); - stats->sync = coh->sync; - stats->foff = coh->f_est - FDMDV_FCENTRE; - stats->rx_timing = coh->rx_timing; - stats->clock_offset = 0.0f; /* TODO - implement clock offset estimation */ + // fprintf(stderr, "sig_rms: %f noise_rms: %f snr_est: %f\n", coh->sig_rms, + // coh->noise_rms, stats->snr_est); + stats->sync = coh->sync; + stats->foff = coh->f_est - FDMDV_FCENTRE; + stats->rx_timing = coh->rx_timing; + stats->clock_offset = 0.0f; /* TODO - implement clock offset estimation */ #ifndef __EMBEDDED__ - assert(NSYMROW <= MODEM_STATS_NR_MAX); - stats->nr = NSYMROW; - for(int c=0; c<COHPSK_NC*COHPSK_ND; c++) { - for (int r=0; r<NSYMROW; r++) { - stats->rx_symbols[r][c] = cmult(coh->rx_symb[r][c], pi_4); - } + assert(NSYMROW <= MODEM_STATS_NR_MAX); + stats->nr = NSYMROW; + for (int c = 0; c < COHPSK_NC * COHPSK_ND; c++) { + for (int r = 0; r < NSYMROW; r++) { + stats->rx_symbols[r][c] = cmult(coh->rx_symb[r][c], pi_4); } + } #endif } - -void cohpsk_set_verbose(struct COHPSK *coh, int verbose) -{ - assert(coh != NULL); - coh->verbose = verbose; +void cohpsk_set_verbose(struct COHPSK *coh, int verbose) { + assert(coh != NULL); + coh->verbose = verbose; } - -void cohpsk_set_frame(struct COHPSK *coh, int frame) -{ - assert(coh != NULL); - coh->frame = frame; +void cohpsk_set_frame(struct COHPSK *coh, int frame) { + assert(coh != NULL); + coh->frame = frame; } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_get_test_bits() @@ -1325,16 +1336,14 @@ void cohpsk_set_frame(struct COHPSK *coh, int frame) \*---------------------------------------------------------------------------*/ -void cohpsk_get_test_bits(struct COHPSK *coh, int rx_bits[]) -{ - memcpy(rx_bits, coh->ptest_bits_coh_tx, sizeof(int)*COHPSK_BITS_PER_FRAME); - coh->ptest_bits_coh_tx += COHPSK_BITS_PER_FRAME; - if (coh->ptest_bits_coh_tx >=coh->ptest_bits_coh_end) { - coh->ptest_bits_coh_tx = (int*)test_bits_coh; - } +void cohpsk_get_test_bits(struct COHPSK *coh, int rx_bits[]) { + memcpy(rx_bits, coh->ptest_bits_coh_tx, sizeof(int) * COHPSK_BITS_PER_FRAME); + coh->ptest_bits_coh_tx += COHPSK_BITS_PER_FRAME; + if (coh->ptest_bits_coh_tx >= coh->ptest_bits_coh_end) { + coh->ptest_bits_coh_tx = (int *)test_bits_coh; + } } - /*---------------------------------------------------------------------------*\ FUNCTION....: cohpsk_put_test_bits() @@ -1350,84 +1359,80 @@ void cohpsk_get_test_bits(struct COHPSK *coh, int rx_bits[]) \*---------------------------------------------------------------------------*/ void cohpsk_put_test_bits(struct COHPSK *coh, int *state, short error_pattern[], - int *bit_errors, char rx_bits_char[], int channel) -{ - int i, next_state, anerror; - int rx_bits[COHPSK_BITS_PER_FRAME]; - - assert((channel == 0) || (channel == 1)); - int *ptest_bits_coh_rx = coh->ptest_bits_coh_rx[channel]; - - for(i=0; i<COHPSK_BITS_PER_FRAME; i++) { - rx_bits[i] = rx_bits_char[i]; - } - - *bit_errors = 0; - for(i=0; i<COHPSK_BITS_PER_FRAME; i++) { - anerror = (rx_bits[i] & 0x1) ^ ptest_bits_coh_rx[i]; - if ((anerror < 0) || (anerror > 1)) { - fprintf(stderr, "i: %d rx_bits: %d ptest_bits_coh_rx: %d\n", i, rx_bits[i], ptest_bits_coh_rx[i]); - } - *bit_errors += anerror; - error_pattern[i] = anerror; + int *bit_errors, char rx_bits_char[], int channel) { + int i, next_state, anerror; + int rx_bits[COHPSK_BITS_PER_FRAME]; + + assert((channel == 0) || (channel == 1)); + int *ptest_bits_coh_rx = coh->ptest_bits_coh_rx[channel]; + + for (i = 0; i < COHPSK_BITS_PER_FRAME; i++) { + rx_bits[i] = rx_bits_char[i]; + } + + *bit_errors = 0; + for (i = 0; i < COHPSK_BITS_PER_FRAME; i++) { + anerror = (rx_bits[i] & 0x1) ^ ptest_bits_coh_rx[i]; + if ((anerror < 0) || (anerror > 1)) { + fprintf(stderr, "i: %d rx_bits: %d ptest_bits_coh_rx: %d\n", i, + rx_bits[i], ptest_bits_coh_rx[i]); } + *bit_errors += anerror; + error_pattern[i] = anerror; + } - /* state logic */ + /* state logic */ - next_state = *state; + next_state = *state; - if (*state == 0) { - if (*bit_errors < 4) { - next_state = 1; - ptest_bits_coh_rx += COHPSK_BITS_PER_FRAME; - if (ptest_bits_coh_rx >= coh->ptest_bits_coh_end) { - ptest_bits_coh_rx = (int*)test_bits_coh; - } - } + if (*state == 0) { + if (*bit_errors < 4) { + next_state = 1; + ptest_bits_coh_rx += COHPSK_BITS_PER_FRAME; + if (ptest_bits_coh_rx >= coh->ptest_bits_coh_end) { + ptest_bits_coh_rx = (int *)test_bits_coh; + } } - - /* if 5 frames with large BER reset test frame sync */ - - if (*state > 0) { - if (*bit_errors > 8) { - if (*state == 6) - next_state = 0; - else - next_state = *state+1; - } - else - next_state = 1; + } + + /* if 5 frames with large BER reset test frame sync */ + + if (*state > 0) { + if (*bit_errors > 8) { + if (*state == 6) + next_state = 0; + else + next_state = *state + 1; + } else + next_state = 1; + } + + if (*state > 0) { + ptest_bits_coh_rx += COHPSK_BITS_PER_FRAME; + if (ptest_bits_coh_rx >= coh->ptest_bits_coh_end) { + ptest_bits_coh_rx = (int *)test_bits_coh; } + } - if (*state > 0) { - ptest_bits_coh_rx += COHPSK_BITS_PER_FRAME; - if (ptest_bits_coh_rx >= coh->ptest_bits_coh_end) { - ptest_bits_coh_rx = (int*)test_bits_coh; - } - } - - //fprintf(stderr, "state: %d next_state: %d bit_errors: %d\n", *state, next_state, *bit_errors); - - *state = next_state; - coh->ptest_bits_coh_rx[channel] = ptest_bits_coh_rx; -} - + // fprintf(stderr, "state: %d next_state: %d bit_errors: %d\n", *state, + // next_state, *bit_errors); -int cohpsk_error_pattern_size(void) { - return COHPSK_BITS_PER_FRAME; + *state = next_state; + coh->ptest_bits_coh_rx[channel] = ptest_bits_coh_rx; } +int cohpsk_error_pattern_size(void) { return COHPSK_BITS_PER_FRAME; } float *cohpsk_get_rx_bits_lower(struct COHPSK *coh) { - return coh->rx_bits_lower; + return coh->rx_bits_lower; } float *cohpsk_get_rx_bits_upper(struct COHPSK *coh) { - return coh->rx_bits_upper; + return coh->rx_bits_upper; } void cohpsk_set_carrier_ampl(struct COHPSK *coh, int c, float ampl) { - assert(c < COHPSK_NC*COHPSK_ND); - coh->carrier_ampl[c] = ampl; - fprintf(stderr, "cohpsk_set_carrier_ampl: %d %f\n", c, (double)ampl); + assert(c < COHPSK_NC * COHPSK_ND); + coh->carrier_ampl[c] = ampl; + fprintf(stderr, "cohpsk_set_carrier_ampl: %d %f\n", c, (double)ampl); } |
