| 
									
										
										
										
											2019-03-05 01:01:38 +01:00
										 |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FILE........: cohpsk.c | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: March 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Functions that implement a coherent PSK FDM modem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |   Copyright (C) 2015 David Rowe | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |   it under the terms of the GNU Lesser General Public License version 2.1, as | 
					
						
							|  |  |  |   published by the Free Software Foundation.  This program is | 
					
						
							|  |  |  |   distributed in the hope that it will be useful, but WITHOUT ANY | 
					
						
							|  |  |  |   WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
					
						
							|  |  |  |   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public | 
					
						
							|  |  |  |   License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  |   along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                INCLUDES | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stdio.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"
 | 
					
						
							| 
									
										
										
										
											2019-03-05 03:36:04 +01:00
										 |  |  | #include "comp_prim.h"
 | 
					
						
							| 
									
										
										
										
											2019-03-05 01:01:38 +01:00
										 |  |  | #include "kiss_fft.h"
 | 
					
						
							|  |  |  | #include "linreg.h"
 | 
					
						
							|  |  |  | #include "rn_coh.h"
 | 
					
						
							|  |  |  | #include "test_bits_coh.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FreeDV | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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_comp(float *corr_out, float *mag_out, struct COHPSK *coh, int t, COMP f_fine); | 
					
						
							|  |  |  | void update_ct_symb_buf(COMP ct_symb_buf[][COHPSK_NC*ND], COMP ch_symb[][COHPSK_NC*ND]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                FUNCTIONS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*--------------------------------------------------------------------------* \
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_create | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: Marcg 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Create and initialise an instance of the modem.  Returns a pointer | 
					
						
							|  |  |  |   to the modem states or NULL on failure.  One set of states is | 
					
						
							|  |  |  |   sufficient for a full duplex modem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct COHPSK *cohpsk_create(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct COHPSK *coh; | 
					
						
							|  |  |  |     struct FDMDV  *fdmdv; | 
					
						
							|  |  |  |     int            r,c,p,i; | 
					
						
							|  |  |  |     float          freq_hz; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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_ND == ND); | 
					
						
							|  |  |  |     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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (r=0; r<NCT_SYMB_BUF; r++) { | 
					
						
							|  |  |  |         for(c=0; c<COHPSK_NC*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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* set up fdmdv states so we can use those modem functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fdmdv = fdmdv_create(COHPSK_NC*ND - 1); | 
					
						
							|  |  |  |     fdmdv->fsep = COHPSK_RS*(1.0 + COHPSK_EXCESS_BW); | 
					
						
							|  |  |  |     for(c=0; c<COHPSK_NC*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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         freq_hz = fdmdv->fsep*( -(COHPSK_NC*ND)/2 - 0.5 + pow(c + 1.0, 0.98) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fdmdv->freq[c].real = cosf(2.0*M_PI*freq_hz/COHPSK_FS); | 
					
						
							|  |  |  |  	fdmdv->freq[c].imag = sinf(2.0*M_PI*freq_hz/COHPSK_FS); | 
					
						
							|  |  |  |  	fdmdv->freq_pol[c]  = 2.0*M_PI*freq_hz/COHPSK_FS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         coh->carrier_ampl[c] = 1.0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     fdmdv->fbb_rect.real     = cosf(2.0*PI*FDMDV_FCENTRE/COHPSK_FS); | 
					
						
							|  |  |  |     fdmdv->fbb_rect.imag     = sinf(2.0*PI*FDMDV_FCENTRE/COHPSK_FS); | 
					
						
							|  |  |  |     fdmdv->fbb_pol           = 2.0*PI*FDMDV_FCENTRE/COHPSK_FS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     coh->fdmdv = fdmdv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     coh->sig_rms = coh->noise_rms = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(c=0; c<COHPSK_NC*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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Disable 'reduce' frequency estimation mode */ | 
					
						
							|  |  |  |     coh->freq_est_mode_reduced = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return coh; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_destroy | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: March 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Destroy an instance of the modem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cohpsk_destroy(struct COHPSK *coh) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(coh != NULL); | 
					
						
							|  |  |  |     fdmdv_destroy(coh->fdmdv); | 
					
						
							|  |  |  |     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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void bits_to_qpsk_symbols(COMP tx_symb[][COHPSK_NC*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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert( (NSYMROW*COHPSK_NC*2 == nbits) || (NSYMROW*COHPSK_NC*2*ND == nbits)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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 */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |       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...... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       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. | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     r = 0; | 
					
						
							|  |  |  |     for(p_r=0; p_r<2; p_r++) { | 
					
						
							|  |  |  |         for(c=0; c<COHPSK_NC*ND; c++) { | 
					
						
							|  |  |  |             tx_symb[r][c].real = pilots_coh[p_r][c % COHPSK_NC]/sqrtf(ND); | 
					
						
							|  |  |  |             tx_symb[r][c].imag = 0.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         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(ND),qpsk_mod[bits]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(p_r == NPILOTSFRAME); | 
					
						
							|  |  |  |     assert(r == NSYMROWPILOT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* if in diversity mode, copy symbols to upper carriers */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(d=1; d<1+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() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: March 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Rate Rs demodulator. Extract pilot symbols and estimate amplitude and phase | 
					
						
							|  |  |  |   of each carrier.  Correct phase of data symbols and convert to bits. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Further improvement.  In channels with slowly changing phase we | 
					
						
							|  |  |  |   could optionally use pilots from several past and future symbols. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void qpsk_symbols_to_bits(struct COHPSK *coh, float rx_bits[], COMP ct_symb_buf[][COHPSK_NC*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*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; | 
					
						
							|  |  |  |     COMP  s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pi_on_4.real = cosf(M_PI/4); pi_on_4.imag = sinf(M_PI/4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(c=0; c<COHPSK_NC*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] = atan2(yfit.imag, yfit.real); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* amplitude estimation */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         mag = 0.0; | 
					
						
							|  |  |  |         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_; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* now correct phase of data symbols */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(c=0; c<COHPSK_NC*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]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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<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(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; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* estimate RMS signal and noise */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mag = 0.0; | 
					
						
							|  |  |  |     for(i=0; i<NSYMROW*COHPSK_NC*ND; i++) | 
					
						
							|  |  |  |         mag += cabsolute(rx_symb_linear[i]); | 
					
						
							|  |  |  |     coh->sig_rms = mag/(NSYMROW*COHPSK_NC*ND); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sum_x = 0; | 
					
						
							|  |  |  |     sum_xx = 0; | 
					
						
							|  |  |  |     n = 0; | 
					
						
							|  |  |  |     for (i=0; i<NSYMROW*COHPSK_NC*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++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     noise_var = 0; | 
					
						
							|  |  |  |     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() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Given NC symbols construct M samples (1 symbol) of NC filtered | 
					
						
							|  |  |  |   and upconverted symbols. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TODO: work out a way to merge with fdmdv version, e.g. run time define M/NSYM, | 
					
						
							|  |  |  |   and run unittests on fdmdv and cohpsk modem afterwards. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void tx_filter_and_upconvert_coh(COMP tx_fdm[], int Nc,const COMP tx_symbols[], | 
					
						
							|  |  |  |                                  COMP tx_filter_memory[COHPSK_NC*ND][COHPSK_NSYM], | 
					
						
							|  |  |  |                                  COMP phase_tx[], COMP freq[], | 
					
						
							|  |  |  |                                  COMP *fbb_phase, COMP fbb_rect) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int     c; | 
					
						
							|  |  |  |     int     i,j,k; | 
					
						
							|  |  |  |     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++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const COMP * tx_filter_memory_cn = (COMP*) &tx_filter_memory[c]; | 
					
						
							|  |  |  | 			/* filter sample of symbol for carrier c */ | 
					
						
							|  |  |  | 			tx_baseband.real = 0; | 
					
						
							|  |  |  | 			tx_baseband.imag = 0; | 
					
						
							|  |  |  | 			for(j=0,k=COHPSK_M-i-1; j<COHPSK_NSYM; j++,k+=COHPSK_M){ | 
					
						
							|  |  |  | 				tx_baseband = cadd(tx_baseband,fcmult(COHPSK_M,fcmult(gt_alpha5_root_coh[k],tx_filter_memory_cn[j]))); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* 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])); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  |         //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]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mag = cabsolute(*fbb_phase); | 
					
						
							|  |  |  |     fbb_phase->real /= mag; | 
					
						
							|  |  |  |     fbb_phase->imag /= mag; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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(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_comp(float *corr_out, float *mag_out, struct COHPSK *coh, int t, COMP dp_f_fine) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     COMP  acorr, f_fine_rect, f_corr; | 
					
						
							|  |  |  |     float mag, corr; | 
					
						
							|  |  |  |     int   c, p, pc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //1,2,7,8
 | 
					
						
							|  |  |  |     f_fine_rect.real = 1; | 
					
						
							|  |  |  |     f_fine_rect.imag = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP f_fine_rects[4]; | 
					
						
							|  |  |  |     //dp_f_fine = comp_exp_j(2*m_pi*f_fine/cohpsk_rs);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//sampling_points[0]+1 = 1
 | 
					
						
							|  |  |  |     f_fine_rects[0] = dp_f_fine; | 
					
						
							|  |  |  |     f_fine_rect = cmult(dp_f_fine,dp_f_fine);	//sampling_points[1]+1 = 2
 | 
					
						
							|  |  |  |     f_fine_rects[1] = f_fine_rect; | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//				       = 2
 | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//                     = 3
 | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//                     = 4
 | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//                     = 5
 | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//                     = 6
 | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//sampling_points[2]+1 = 7
 | 
					
						
							|  |  |  |     f_fine_rects[2] = f_fine_rect; | 
					
						
							|  |  |  |     f_fine_rect = cmult(f_fine_rect,dp_f_fine);	//sampling_points[2]+1 = 8
 | 
					
						
							|  |  |  |     f_fine_rects[3] = f_fine_rect; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     corr = 0.0; mag = 0.0; | 
					
						
							|  |  |  |     for (c=0; c<COHPSK_NC*ND; c++) { | 
					
						
							|  |  |  |         acorr.real = 0.0; acorr.imag = 0.0; | 
					
						
							|  |  |  |         for (p=0; p<NPILOTSFRAME+2; p++) { | 
					
						
							|  |  |  |             //f_fine_rect.real = cosf(f_fine*2.0*M_PI*(sampling_points[p]+1.0)/COHPSK_RS);
 | 
					
						
							|  |  |  |             //f_fine_rect.imag = sinf(f_fine*2.0*M_PI*(sampling_points[p]+1.0)/COHPSK_RS);
 | 
					
						
							|  |  |  |         	f_fine_rect = f_fine_rects[p]; | 
					
						
							|  |  |  |             f_corr = cmult(f_fine_rect, coh->ct_symb_buf[t+sampling_points[p]][c]); | 
					
						
							|  |  |  |             pc = c % COHPSK_NC; | 
					
						
							|  |  |  |             acorr = cadd(acorr, fcmult(coh->pilot2[p][pc], f_corr)); | 
					
						
							|  |  |  |             mag  += cabsolute(f_corr); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         corr += cabsolute(acorr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *corr_out = corr; | 
					
						
							|  |  |  |     *mag_out  = mag; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: frame_sync_fine_freq_est() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: April 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Returns an estimate of frame sync (coarse timing) offset and fine | 
					
						
							|  |  |  |   frequency offset, advances to next sync state if we have a reliable | 
					
						
							|  |  |  |   match for frame sync. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void frame_sync_fine_freq_est(struct COHPSK *coh, COMP ch_symb[][COHPSK_NC*ND], int sync, int *next_sync) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int   t; | 
					
						
							|  |  |  |     float f_fine, mag, max_corr, max_mag, corr, delta_f_fine, f_fine_range ; | 
					
						
							|  |  |  |     COMP f_fine_d_ph; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(coh->freq_est_mode_reduced){ | 
					
						
							|  |  |  | 		delta_f_fine = 1.3; | 
					
						
							|  |  |  | 		f_fine_range = 10; | 
					
						
							|  |  |  |     }else{ | 
					
						
							|  |  |  | 		delta_f_fine = .25; | 
					
						
							|  |  |  | 		f_fine_range = 20; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Represent f_fine scan as delta2-phase */ | 
					
						
							|  |  |  | 	const COMP f_fine_d2_ph = comp_exp_j(2*M_PI*delta_f_fine/COHPSK_RS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f_fine = -f_fine_range; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     	/* Represent f_fine as complex delta-phase instead of frequency */ | 
					
						
							|  |  |  |     	f_fine_d_ph = comp_exp_j(2*M_PI*f_fine/COHPSK_RS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* sample correlation over 2D grid of time and fine freq points */ | 
					
						
							|  |  |  |         max_corr = max_mag = 0; | 
					
						
							|  |  |  |         for (f_fine=-f_fine_range; f_fine<=f_fine_range; f_fine+=delta_f_fine) { | 
					
						
							|  |  |  |             for (t=0; t<NSYMROWPILOT; t++) { | 
					
						
							|  |  |  |                 corr_with_pilots_comp(&corr,&mag,coh,t,f_fine_d_ph); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (corr >= max_corr) { | 
					
						
							|  |  |  |                     max_corr = corr; | 
					
						
							|  |  |  |                     max_mag = mag; | 
					
						
							|  |  |  |                     coh->ct = t; | 
					
						
							|  |  |  |                     coh->f_fine_est = f_fine; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             /* Advance f_fine */ | 
					
						
							|  |  |  |             f_fine_d_ph = cmult(f_fine_d_ph,f_fine_d2_ph); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         coh->ff_rect.real = cosf(coh->f_fine_est*2.0*M_PI/COHPSK_RS); | 
					
						
							|  |  |  |         coh->ff_rect.imag = -sinf(coh->f_fine_est*2.0*M_PI/COHPSK_RS); | 
					
						
							|  |  |  |         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/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*ND], COMP ch_symb[][COHPSK_NC*ND]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int r, c, i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* update memory in symbol buffer */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(r=0; r<NCT_SYMB_BUF-NSYMROWPILOT; r++) { | 
					
						
							|  |  |  |         for(c=0; c<COHPSK_NC*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*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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sync == 1) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* check that sync is still good, fall out of sync on consecutive bad frames */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         corr_with_pilots_comp(&corr, &mag, coh, coh->ct, comp_exp_j(2*M_PI*coh->f_fine_est/COHPSK_RS)); | 
					
						
							|  |  |  |         coh->ratio = fabsf(corr)/mag; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // printf("%f\n", cabsolute(corr)/mag);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sync = next_sync; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return sync; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_mod() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 5/4/2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   COHPSK modulator, take a frame of COHPSK_BITS_PER_FRAME or | 
					
						
							|  |  |  |   2*COHPSK_BITS_PER_FRAME bits and generates a frame of | 
					
						
							|  |  |  |   COHPSK_NOM_SAMPLES_PER_FRAME modulated symbols. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if nbits == COHPSK_BITS_PER_FRAME, diveristy mode is used, if nbits | 
					
						
							|  |  |  |   == 2*COHPSK_BITS_PER_FRAME diversity mode is not used. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The output signal is complex to support single sided frequency | 
					
						
							|  |  |  |   shifting, for example when testing frequency offsets in channel | 
					
						
							|  |  |  |   simulation. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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*ND]; | 
					
						
							|  |  |  |     COMP  tx_onesym[COHPSK_NC*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*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*ND , tx_onesym, fdmdv->tx_filter_memory, | 
					
						
							|  |  |  |                                     fdmdv->phase_tx, fdmdv->freq, &fdmdv->fbb_phase_tx, fdmdv->fbb_rect); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_clip() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Hard clips a cohpsk modulator signal to improve PAPR, CLIP threshold | 
					
						
							|  |  |  |   hard coded and will need to be changed if NC*ND does. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |  } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: fdm_downconvert_coh | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Frequency shift each modem carrier down to NC baseband signals. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TODO: try to combine with fdmdv version, carefully re-test fdmdv modem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fdm_downconvert_coh(COMP rx_baseband[COHPSK_NC][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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(nin <= (COHPSK_M+COHPSK_M/P)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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])); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: rx_filter_coh() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   cohpsk version of fdmdv.c rx_filter function. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TODO: see if we can merge the two!  Will require re-testing of fdmdv modem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void rx_filter_coh(COMP rx_filt[COHPSK_NC+1][P+1], int Nc, COMP rx_baseband[COHPSK_NC+1][COHPSK_M+COHPSK_M/P], COMP rx_filter_memory[COHPSK_NC+1][+COHPSK_NFILTER], int nin) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int c, i,j,k; | 
					
						
							|  |  |  |     int n=COHPSK_M/P; | 
					
						
							|  |  |  |     COMP acc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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++){ | 
					
						
							|  |  |  | 			k=COHPSK_NFILTER-n; | 
					
						
							|  |  |  | 			memcpy(&rx_filter_memory[c][k],&rx_baseband[c][i],n*sizeof(COMP)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* convolution (filtering) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for(c=0; c<Nc; c++) { | 
					
						
							|  |  |  | 			/* Cast into const so the compiler doesn't expect aliasing */ | 
					
						
							|  |  |  | 			const COMP * rx_filt_lc = &rx_filter_memory[c][0]; | 
					
						
							|  |  |  | 			acc.real = 0.0f; | 
					
						
							|  |  |  | 			acc.imag = 0.0f; | 
					
						
							|  |  |  | 			for(k=0; k<COHPSK_NFILTER; k++){ | 
					
						
							|  |  |  | 				acc = cadd(acc, fcmult(gt_alpha5_root_coh[k], rx_filt_lc[k])); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			rx_filt[c][j] = acc; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* make room for next input sample */ | 
					
						
							|  |  |  | 		for(c=0; c<Nc; c++){ | 
					
						
							|  |  |  | 			memcpy(&rx_filter_memory[c][0],&rx_filter_memory[c][n],(COHPSK_NFILTER-n)*sizeof(COMP)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(j <= (P+1)); /* check for any over runs */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: fdmdv_freq_shift_coh() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Frequency shift modem signal.  The use of complex input and output allows | 
					
						
							|  |  |  |   single sided frequency shifting (no images). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     int   i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     foff_rect.real = cosf(2.0*PI*foff/Fs); | 
					
						
							|  |  |  |     foff_rect.imag = sinf(2.0*PI*foff/Fs); | 
					
						
							|  |  |  |     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 oscilator as the magnitude can drfift 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*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*ND][COHPSK_M+COHPSK_M/P]; | 
					
						
							|  |  |  |     COMP  rx_filt[COHPSK_NC*ND][P+1]; | 
					
						
							|  |  |  |     float env[NT*P], rx_timing; | 
					
						
							|  |  |  |     COMP  rx_onesym[COHPSK_NC*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*ND, rx_fdm_frame_bb, fdmdv->phase_rx, fdmdv->freq, nin); | 
					
						
							|  |  |  |         rx_filter_coh(rx_filt, COHPSK_NC*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*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);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* loop filter made up of 1st order IIR plus integrator.  Integerator
 | 
					
						
							|  |  |  |                was found to be reqd  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             fdmdv->foff_filt = (1.0-beta)*fdmdv->foff_filt + beta*atan2(mod_strip.imag, mod_strip.real); | 
					
						
							|  |  |  |             //printf("foff_filt: %f angle: %f\n", fdmdv->foff_filt, atan2(mod_strip.imag, mod_strip.real));
 | 
					
						
							|  |  |  |             *f_est += g*fdmdv->foff_filt; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* 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*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_filt_log) { | 
					
						
							|  |  |  |  	  for(c=0; c<COHPSK_NC*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); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (coh->ch_symb_log) { | 
					
						
							|  |  |  |             for(c=0; c<COHPSK_NC*ND; c++) { | 
					
						
							|  |  |  | 		coh->ch_symb_log[coh->ch_symb_log_r*COHPSK_NC*ND + c] = ch_symb[r][c]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             coh->ch_symb_log_r++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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);
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* we only allow a timing shift on one symbol per frame */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (nin != COHPSK_M) | 
					
						
							|  |  |  |             nin = COHPSK_M; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     coh->rx_timing = rx_timing; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_set_freq_est_mode() | 
					
						
							|  |  |  |   AUTHOR......: Brady O'Brien | 
					
						
							|  |  |  |   DATE CREATED: 12 Dec 2017 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Enables or disables a 'simple' frequency estimation mode. Simple frequency | 
					
						
							|  |  |  |   estimation uses substantially less CPU when cohpsk modem is not sunk than | 
					
						
							|  |  |  |   default mode, but may take many frames to sync. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | void cohpsk_set_freq_est_mode(struct COHPSK *coh, int use_simple_mode){ | 
					
						
							|  |  |  | 	if(use_simple_mode){ | 
					
						
							|  |  |  | 		coh->freq_est_mode_reduced = 1; | 
					
						
							|  |  |  | 	}else{ | 
					
						
							|  |  |  | 		coh->freq_est_mode_reduced = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_demod() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 5/4/2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   COHPSK demodulator, takes an array of (nominally) nin_frame = | 
					
						
							|  |  |  |   COHPSK_NOM_SAMPLES_PER_FRAME modulated samples, returns an array of | 
					
						
							|  |  |  |   COHPSK_BITS_PER_FRAME bits. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The input signal is complex to support single sided frequency shifting | 
					
						
							|  |  |  |   before the demod input (e.g. click to tune feature). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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*ND]; | 
					
						
							|  |  |  |     int   i, j, sync, anext_sync, next_sync, nin, r, c, ns_done; | 
					
						
							|  |  |  |     float max_ratio, f_est; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(*nin_frame <= COHPSK_MAX_SAMPLES_PER_FRAME); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     next_sync = sync = coh->sync; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* if out of sync do Initial Freq offset estimation using NSW frames to flush out filter memories */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sync == 0) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         max_ratio = 0.0; | 
					
						
							|  |  |  |         f_est = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         coh->f_est -= 20; | 
					
						
							|  |  |  |         if(coh->f_est < FDMDV_FCENTRE - 60.0){ | 
					
						
							|  |  |  |         	coh->f_est = FDMDV_FCENTRE + 60; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if(!coh->freq_est_mode_reduced){ | 
					
						
							|  |  |  |         	coh->f_est = FDMDV_FCENTRE-40.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ns_done = 0; | 
					
						
							|  |  |  |         //for (coh->f_est = FDMDV_FCENTRE-40.0; coh->f_est <= FDMDV_FCENTRE+40.0; coh->f_est += 40.0)
 | 
					
						
							|  |  |  |         while(!ns_done){ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         	/* Use slower freq estimator; only do one chunk of freq range */ | 
					
						
							|  |  |  |         	if(coh->freq_est_mode_reduced){ | 
					
						
							|  |  |  |         		coh->f_est -= 20; | 
					
						
							|  |  |  | 				if(coh->f_est < FDMDV_FCENTRE - 60.0){ | 
					
						
							|  |  |  | 					coh->f_est = FDMDV_FCENTRE + 60; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				ns_done = 1; | 
					
						
							|  |  |  |         	}else{ | 
					
						
							|  |  |  |                 /* we can test +/- 20Hz, so we break this up into 3 tests to cover +/- 60Hz */ | 
					
						
							|  |  |  |         		if(coh->f_est > FDMDV_FCENTRE+40.0) ns_done = 1; | 
					
						
							|  |  |  |         	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             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(!coh->freq_est_mode_reduced){ | 
					
						
							|  |  |  |         		coh->f_est += 40; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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 (fabs(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 (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*ND; c++) | 
					
						
							|  |  |  |                     coh->ct_symb_ff_buf[r][c] = coh->ct_symb_buf[coh->ct+r][c]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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*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*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() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Simulates small Fs offset between mod and demod. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int cohpsk_fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     double tin, f; | 
					
						
							|  |  |  |     int   tout, t1, t2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tin = 0.0; tout = 0; | 
					
						
							|  |  |  |     while (tin < n) { | 
					
						
							|  |  |  |       t1 = floor(tin); | 
					
						
							|  |  |  |       t2 = ceil(tin); | 
					
						
							|  |  |  |       f = tin - t1; | 
					
						
							|  |  |  |       out[tout].real = (1.0-f)*in[t1].real + f*in[t2].real; | 
					
						
							|  |  |  |       out[tout].imag = (1.0-f)*in[t1].imag + f*in[t2].imag; | 
					
						
							|  |  |  |       tout += 1; | 
					
						
							|  |  |  |       tin  += 1.0 + sample_rate_ppm/1E6; | 
					
						
							|  |  |  |       //printf("tin: %f tout: %d f: %f\n", tin, tout, f);
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return tout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_get_demod_stats() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 14 June 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Fills stats structure with a bunch of demod information. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cohpsk_get_demod_stats(struct COHPSK *coh, struct MODEM_STATS *stats) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int   c,r; | 
					
						
							|  |  |  |     COMP  pi_4; | 
					
						
							|  |  |  |     float new_snr_est; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pi_4.real = cosf(M_PI/4.0); | 
					
						
							|  |  |  |     pi_4.imag = sinf(M_PI/4.0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stats->Nc = COHPSK_NC*ND; | 
					
						
							|  |  |  |     assert(stats->Nc <= MODEM_STATS_NC_MAX); | 
					
						
							|  |  |  |     new_snr_est = 20*log10((coh->sig_rms+1E-6)/(coh->noise_rms+1E-6)) - 10*log10(3000.0/700.0); | 
					
						
							|  |  |  |     stats->snr_est = 0.9*stats->snr_est + 0.1*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.0; /* TODO - implement clock offset estimation */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(NSYMROW <= MODEM_STATS_NR_MAX); | 
					
						
							|  |  |  |     stats->nr = NSYMROW; | 
					
						
							|  |  |  |     for(c=0; c<COHPSK_NC*ND; c++) { | 
					
						
							|  |  |  |         for (r=0; r<NSYMROW; r++) { | 
					
						
							|  |  |  |             stats->rx_symbols[r][c] = cmult(coh->rx_symb[r][c], pi_4); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: cohpsk_get_test_bits() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: June 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Returns a frame of known test bits. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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() | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: June 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Accepts bits from demod and attempts to sync with the known | 
					
						
							|  |  |  |   test_bits sequence.  When synced measures bit errors. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Has states to track two separate received test sequences based on | 
					
						
							|  |  |  |   channel 0 or 1. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* state logic */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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 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; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float *cohpsk_get_rx_bits_upper(struct COHPSK *coh) { | 
					
						
							|  |  |  |     return coh->rx_bits_upper; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cohpsk_set_carrier_ampl(struct COHPSK *coh, int c, float ampl) { | 
					
						
							|  |  |  |     assert(c < COHPSK_NC*ND); | 
					
						
							|  |  |  |     coh->carrier_ampl[c] = ampl; | 
					
						
							|  |  |  |     fprintf(stderr, "cohpsk_set_carrier_ampl: %d %f\n", c, (double)ampl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // FreeDV
 |