| 
									
										
										
										
											2019-03-05 01:01:38 +01:00
										 |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FILE........: fsk.c | 
					
						
							|  |  |  |   AUTHOR......: Brady O'Brien | 
					
						
							|  |  |  |   DATE CREATED: 7 January 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   C Implementation of 2/4FSK modulator/demodulator, based on octave/fsk_horus.m | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |   Copyright (C) 2016 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/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                DEFINES | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* P oversampling rate constant -- should probably be init-time configurable */ | 
					
						
							|  |  |  | #define horus_P 8
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Define this to enable EbNodB estimate */ | 
					
						
							|  |  |  | /* This needs square roots, may take more cpu time than it's worth */ | 
					
						
							|  |  |  | #define EST_EBNO
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This is a flag for the freq. estimator to use a precomputed/rt computed hann window table
 | 
					
						
							|  |  |  |    On platforms with slow cosf, this will produce a substantial speedup at the cost of a small | 
					
						
							|  |  |  |     amount of memory | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | #define USE_HANN_TABLE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This flag turns on run-time hann table generation. If USE_HANN_TABLE is unset,
 | 
					
						
							|  |  |  |     this flag has no effect. If USE_HANN_TABLE is set and this flag is set, the | 
					
						
							|  |  |  |     hann table will be allocated and generated when fsk_init or fsk_init_hbr is | 
					
						
							|  |  |  |     called. If this flag is not set, a hann function table of size fsk->Ndft MUST | 
					
						
							|  |  |  |     be provided. On small platforms, this can be used with a precomputed table to | 
					
						
							|  |  |  |     save memory at the cost of flash space. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | #define GENERATE_HANN_TABLE_RUNTIME
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Turn off table generation if on cortex M4 to save memory */ | 
					
						
							|  |  |  | #ifdef CORTEX_M4
 | 
					
						
							|  |  |  | #undef USE_HANN_TABLE
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                INCLUDES | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "fsk.h"
 | 
					
						
							| 
									
										
										
										
											2019-03-05 03:36:04 +01:00
										 |  |  | #include "comp_prim.h"
 | 
					
						
							| 
									
										
										
										
											2019-03-05 01:01:38 +01:00
										 |  |  | #include "kiss_fftr.h"
 | 
					
						
							|  |  |  | #include "modem_probe.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FreeDV | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                FUNCTIONS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stats_init(struct FSK *fsk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef USE_HANN_TABLE
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |    This is used by fsk_create and fsk_create_hbr to generate a hann function | 
					
						
							|  |  |  |    table | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | static void fsk_generate_hann_table(struct FSK* fsk){ | 
					
						
							|  |  |  |     int Ndft = fsk->Ndft; | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up complex oscilator to calculate hann function */ | 
					
						
							|  |  |  |     COMP dphi = comp_exp_j((2*M_PI)/((float)Ndft-1)); | 
					
						
							|  |  |  |     COMP rphi = {.5,0}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rphi = cmult(cconj(dphi),rphi); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < Ndft; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         rphi = cmult(dphi,rphi); | 
					
						
							|  |  |  |         float hannc = .5-rphi.real; | 
					
						
							|  |  |  |         //float hann = .5-(.5*cosf((2*M_PI*(float)(i))/((float)Ndft-1)));
 | 
					
						
							|  |  |  |         fsk->hann_table[i] = hannc; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: fsk_create_hbr | 
					
						
							|  |  |  |   AUTHOR......: Brady O'Brien | 
					
						
							|  |  |  |   DATE CREATED: 11 February 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Create and initialize an instance of the FSK modem. Returns a pointer | 
					
						
							|  |  |  |   to the modem state/config struct. One modem config struct may be used | 
					
						
							|  |  |  |   for both mod and demod. returns NULL on failure. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct FSK * fsk_create_hbr(int Fs, int Rs,int P,int M, int tx_f1, int tx_fs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct FSK *fsk; | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     int memold; | 
					
						
							|  |  |  |     int Ndft = 0; | 
					
						
							|  |  |  |     /* Number of symbols in a processing frame */ | 
					
						
							|  |  |  |     int nsyms = 48; | 
					
						
							|  |  |  |     /* Check configuration validity */ | 
					
						
							|  |  |  |     assert(Fs > 0 ); | 
					
						
							|  |  |  |     assert(Rs > 0 ); | 
					
						
							|  |  |  |     assert(tx_f1 > 0); | 
					
						
							|  |  |  |     assert(tx_fs > 0); | 
					
						
							|  |  |  |     assert(P > 0); | 
					
						
							|  |  |  |     /* Ts (Fs/Rs) must be an integer */ | 
					
						
							|  |  |  |     assert( (Fs%Rs) == 0 ); | 
					
						
							|  |  |  |     /* Ts/P (Fs/Rs/P) must be an integer */ | 
					
						
							|  |  |  |     assert( ((Fs/Rs)%P) == 0 ); | 
					
						
							|  |  |  |     assert( M==2 || M==4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk = (struct FSK*) malloc(sizeof(struct FSK)); | 
					
						
							|  |  |  |     if(fsk == NULL) return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set constant config parameters */ | 
					
						
							|  |  |  |     fsk->Fs = Fs; | 
					
						
							|  |  |  |     fsk->Rs = Rs; | 
					
						
							|  |  |  |     fsk->Ts = Fs/Rs; | 
					
						
							|  |  |  |     fsk->burst_mode = 0; | 
					
						
							|  |  |  |     fsk->N = fsk->Ts*nsyms; | 
					
						
							|  |  |  |     fsk->P = P; | 
					
						
							|  |  |  |     fsk->Nsym = nsyms; | 
					
						
							|  |  |  |     fsk->Nmem = fsk->N+(2*fsk->Ts); | 
					
						
							|  |  |  |     fsk->f1_tx = tx_f1; | 
					
						
							|  |  |  |     fsk->fs_tx = tx_fs; | 
					
						
							|  |  |  |     fsk->nin = fsk->N; | 
					
						
							|  |  |  |     fsk->mode = M==2 ? MODE_2FSK : MODE_4FSK; | 
					
						
							|  |  |  |     fsk->Nbits = M==2 ? fsk->Nsym : fsk->Nsym*2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Find smallest 2^N value that fits Fs for efficient FFT */ | 
					
						
							|  |  |  |     /* It would probably be better to use KISS-FFt's routine here */ | 
					
						
							|  |  |  |     for(i=1; i; i<<=1) | 
					
						
							|  |  |  |         if((fsk->N)&i) | 
					
						
							|  |  |  |             Ndft = i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->Ndft = Ndft; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->est_min = Rs/4; | 
					
						
							|  |  |  |     if(fsk->est_min<0) fsk->est_min = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->est_max = (Fs/2)-Rs/4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->est_space = Rs-(Rs/5); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up rx state */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for( i=0; i<M; i++) | 
					
						
							|  |  |  |         fsk->phi_c[i] = comp_exp_j(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memold = (4*fsk->Ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->nstash = memold; | 
					
						
							|  |  |  |     fsk->samp_old = (COMP*) malloc(sizeof(COMP)*memold); | 
					
						
							|  |  |  |     if(fsk->samp_old == NULL){ | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0;i<memold;i++) { | 
					
						
							|  |  |  |         fsk->samp_old[i].real = 0; | 
					
						
							|  |  |  |         fsk->samp_old[i].imag = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->fft_cfg = kiss_fft_alloc(fsk->Ndft,0,NULL,NULL); | 
					
						
							|  |  |  |     if(fsk->fft_cfg == NULL){ | 
					
						
							|  |  |  |         free(fsk->samp_old); | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->fft_est = (float*)malloc(sizeof(float)*fsk->Ndft/2); | 
					
						
							|  |  |  |     if(fsk->fft_est == NULL){ | 
					
						
							|  |  |  |         free(fsk->samp_old); | 
					
						
							|  |  |  |         free(fsk->fft_cfg); | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef USE_HANN_TABLE
 | 
					
						
							|  |  |  |         #ifdef GENERATE_HANN_TABLE_RUNTIME
 | 
					
						
							|  |  |  |             fsk->hann_table = (float*)malloc(sizeof(float)*fsk->Ndft); | 
					
						
							|  |  |  |             if(fsk->hann_table == NULL){ | 
					
						
							|  |  |  |                 free(fsk->fft_est); | 
					
						
							|  |  |  |                 free(fsk->samp_old); | 
					
						
							|  |  |  |                 free(fsk->fft_cfg); | 
					
						
							|  |  |  |                 free(fsk); | 
					
						
							|  |  |  |                 return NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             fsk_generate_hann_table(fsk); | 
					
						
							|  |  |  |         #else
 | 
					
						
							|  |  |  |             fsk->hann_table = NULL; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0;i<fsk->Ndft/2;i++)fsk->fft_est[i] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->norm_rx_timing = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up tx state */ | 
					
						
							|  |  |  |     fsk->tx_phase_c = comp_exp_j(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up demod stats */ | 
					
						
							|  |  |  |     fsk->EbNodB = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for( i=0; i<M; i++) | 
					
						
							|  |  |  |         fsk->f_est[i] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->ppm = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats = (struct MODEM_STATS*)malloc(sizeof(struct MODEM_STATS)); | 
					
						
							|  |  |  |     if(fsk->stats == NULL){ | 
					
						
							|  |  |  |         free(fsk->fft_est); | 
					
						
							|  |  |  |         free(fsk->samp_old); | 
					
						
							|  |  |  |         free(fsk->fft_cfg); | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     stats_init(fsk); | 
					
						
							|  |  |  |     fsk->normalise_eye = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fsk; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define HORUS_MIN 800
 | 
					
						
							|  |  |  | #define HORUS_MAX 2500
 | 
					
						
							|  |  |  | #define HORUS_MIN_SPACING 100
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: fsk_create | 
					
						
							|  |  |  |   AUTHOR......: Brady O'Brien | 
					
						
							|  |  |  |   DATE CREATED: 7 January 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Create and initialize an instance of the FSK modem. Returns a pointer | 
					
						
							|  |  |  |   to the modem state/config struct. One modem config struct may be used | 
					
						
							|  |  |  |   for both mod and demod. returns NULL on failure. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct FSK * fsk_create(int Fs, int Rs,int M, int tx_f1, int tx_fs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct FSK *fsk; | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     int Ndft = 0; | 
					
						
							|  |  |  |     int memold; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Check configuration validity */ | 
					
						
							|  |  |  |     assert(Fs > 0 ); | 
					
						
							|  |  |  |     assert(Rs > 0 ); | 
					
						
							|  |  |  |     assert(tx_f1 > 0); | 
					
						
							|  |  |  |     assert(tx_fs > 0); | 
					
						
							|  |  |  |     assert(horus_P > 0); | 
					
						
							|  |  |  |     /* Ts (Fs/Rs) must be an integer */ | 
					
						
							|  |  |  |     assert( (Fs%Rs) == 0 ); | 
					
						
							|  |  |  |     /* Ts/P (Fs/Rs/P) must be an integer */ | 
					
						
							|  |  |  |     assert( ((Fs/Rs)%horus_P) == 0 ); | 
					
						
							|  |  |  |     assert( M==2 || M==4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk = (struct FSK*) malloc(sizeof(struct FSK)); | 
					
						
							|  |  |  |     if(fsk == NULL) return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Ndft = 1024; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set constant config parameters */ | 
					
						
							|  |  |  |     fsk->Fs = Fs; | 
					
						
							|  |  |  |     fsk->Rs = Rs; | 
					
						
							|  |  |  |     fsk->Ts = Fs/Rs; | 
					
						
							|  |  |  |     fsk->N = Fs; | 
					
						
							|  |  |  |     fsk->burst_mode = 0; | 
					
						
							|  |  |  |     fsk->P = horus_P; | 
					
						
							|  |  |  |     fsk->Nsym = fsk->N/fsk->Ts; | 
					
						
							|  |  |  |     fsk->Ndft = Ndft; | 
					
						
							|  |  |  |     fsk->Nmem = fsk->N+(2*fsk->Ts); | 
					
						
							|  |  |  |     fsk->f1_tx = tx_f1; | 
					
						
							|  |  |  |     fsk->fs_tx = tx_fs; | 
					
						
							|  |  |  |     fsk->nin = fsk->N; | 
					
						
							|  |  |  |     fsk->mode = M==2 ? MODE_2FSK : MODE_4FSK; | 
					
						
							|  |  |  |     fsk->Nbits = M==2 ? fsk->Nsym : fsk->Nsym*2; | 
					
						
							|  |  |  |     fsk->est_min = HORUS_MIN; | 
					
						
							|  |  |  |     fsk->est_max = HORUS_MAX; | 
					
						
							|  |  |  |     fsk->est_space = HORUS_MIN_SPACING; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up rx state */ | 
					
						
							|  |  |  |     for( i=0; i<M; i++) | 
					
						
							|  |  |  |         fsk->phi_c[i] = comp_exp_j(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memold = (4*fsk->Ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->nstash = memold; | 
					
						
							|  |  |  |     fsk->samp_old = (COMP*) malloc(sizeof(COMP)*memold); | 
					
						
							|  |  |  |     if(fsk->samp_old == NULL){ | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0;i<memold;i++) { | 
					
						
							|  |  |  |         fsk->samp_old[i].real = 0.0; | 
					
						
							|  |  |  |         fsk->samp_old[i].imag = 0.0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->fft_cfg = kiss_fft_alloc(Ndft,0,NULL,NULL); | 
					
						
							|  |  |  |     if(fsk->fft_cfg == NULL){ | 
					
						
							|  |  |  |         free(fsk->samp_old); | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->fft_est = (float*)malloc(sizeof(float)*fsk->Ndft/2); | 
					
						
							|  |  |  |     if(fsk->fft_est == NULL){ | 
					
						
							|  |  |  |         free(fsk->samp_old); | 
					
						
							|  |  |  |         free(fsk->fft_cfg); | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef USE_HANN_TABLE
 | 
					
						
							|  |  |  |         #ifdef GENERATE_HANN_TABLE_RUNTIME
 | 
					
						
							|  |  |  |             fsk->hann_table = (float*)malloc(sizeof(float)*fsk->Ndft); | 
					
						
							|  |  |  |             if(fsk->hann_table == NULL){ | 
					
						
							|  |  |  |                 free(fsk->fft_est); | 
					
						
							|  |  |  |                 free(fsk->samp_old); | 
					
						
							|  |  |  |                 free(fsk->fft_cfg); | 
					
						
							|  |  |  |                 free(fsk); | 
					
						
							|  |  |  |                 return NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             fsk_generate_hann_table(fsk); | 
					
						
							|  |  |  |         #else
 | 
					
						
							|  |  |  |             fsk->hann_table = NULL; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0;i<Ndft/2;i++)fsk->fft_est[i] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->norm_rx_timing = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up tx state */ | 
					
						
							|  |  |  |     fsk->tx_phase_c = comp_exp_j(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up demod stats */ | 
					
						
							|  |  |  |     fsk->EbNodB = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for( i=0; i<M; i++) | 
					
						
							|  |  |  |         fsk->f_est[i] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->ppm = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats = (struct MODEM_STATS*)malloc(sizeof(struct MODEM_STATS)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if(fsk->stats == NULL){ | 
					
						
							|  |  |  |         free(fsk->fft_est); | 
					
						
							|  |  |  |         free(fsk->samp_old); | 
					
						
							|  |  |  |         free(fsk->fft_cfg); | 
					
						
							|  |  |  |         free(fsk); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     stats_init(fsk); | 
					
						
							|  |  |  |     fsk->normalise_eye = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fsk; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* make sure stats have known values in case monitoring process reads stats before they are set */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stats_init(struct FSK *fsk) { | 
					
						
							|  |  |  |     /* Take a sample for the eye diagrams */ | 
					
						
							|  |  |  |     int i,j,m; | 
					
						
							|  |  |  |     int P = fsk->P; | 
					
						
							|  |  |  |     int M = fsk->mode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* due to oversample rate P, we have too many samples for eye
 | 
					
						
							|  |  |  |        trace.  So lets output a decimated version */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* asserts below as we found some problems over-running eye matrix */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* TODO: refactor eye tracing code here and in fsk_demod */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int neyesamp_dec = ceil(((float)P*2)/MODEM_STATS_EYE_IND_MAX); | 
					
						
							|  |  |  |     int neyesamp = (P*2)/neyesamp_dec; | 
					
						
							|  |  |  |     assert(neyesamp <= MODEM_STATS_EYE_IND_MAX); | 
					
						
							|  |  |  |     fsk->stats->neyesamp = neyesamp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int eye_traces = MODEM_STATS_ET_MAX/M; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats->neyetr = fsk->mode*eye_traces; | 
					
						
							|  |  |  |     for(i=0; i<eye_traces; i++) { | 
					
						
							|  |  |  |         for (m=0; m<M; m++){ | 
					
						
							|  |  |  |             for(j=0; j<neyesamp; j++) { | 
					
						
							|  |  |  |                 assert((i*M+m) < MODEM_STATS_ET_MAX); | 
					
						
							|  |  |  |                 fsk->stats->rx_eye[i*M+m][j] = 0; | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats->rx_timing = fsk->stats->snr_est = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_set_nsym(struct FSK *fsk,int nsyms){ | 
					
						
							|  |  |  |     assert(nsyms>0); | 
					
						
							|  |  |  |     int Ndft,i; | 
					
						
							|  |  |  |     Ndft = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set constant config parameters */ | 
					
						
							|  |  |  |     fsk->N = fsk->Ts*nsyms; | 
					
						
							|  |  |  |     fsk->Nsym = nsyms; | 
					
						
							|  |  |  |     fsk->Nmem = fsk->N+(2*fsk->Ts); | 
					
						
							|  |  |  |     fsk->nin = fsk->N; | 
					
						
							|  |  |  |     fsk->Nbits = fsk->mode==2 ? fsk->Nsym : fsk->Nsym*2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Find smallest 2^N value that fits Fs for efficient FFT */ | 
					
						
							|  |  |  |     /* It would probably be better to use KISS-FFt's routine here */ | 
					
						
							|  |  |  |     for(i=1; i; i<<=1) | 
					
						
							|  |  |  |         if((fsk->N)&i) | 
					
						
							|  |  |  |             Ndft = i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->Ndft = Ndft; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     free(fsk->fft_cfg); | 
					
						
							|  |  |  |     free(fsk->fft_est); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->fft_cfg = kiss_fft_alloc(Ndft,0,NULL,NULL); | 
					
						
							|  |  |  |     fsk->fft_est = (float*)malloc(sizeof(float)*fsk->Ndft/2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0;i<Ndft/2;i++)fsk->fft_est[i] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Set the FSK modem into burst demod mode */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_enable_burst_mode(struct FSK *fsk,int nsyms){ | 
					
						
							|  |  |  |     fsk_set_nsym(fsk,nsyms); | 
					
						
							|  |  |  |     fsk->nin = fsk->N; | 
					
						
							|  |  |  |     fsk->burst_mode = 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_clear_estimators(struct FSK *fsk){ | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     /* Clear freq estimator state */ | 
					
						
							|  |  |  |     for(i=0; i < (fsk->Ndft/2); i++){ | 
					
						
							|  |  |  |         fsk->fft_est[i] = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* Reset timing diff correction */ | 
					
						
							|  |  |  |     fsk->nin = fsk->N; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint32_t fsk_nin(struct FSK *fsk){ | 
					
						
							|  |  |  |     return (uint32_t)fsk->nin; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_destroy(struct FSK *fsk){ | 
					
						
							|  |  |  |     free(fsk->fft_cfg); | 
					
						
							|  |  |  |     free(fsk->samp_old); | 
					
						
							|  |  |  |     free(fsk->stats); | 
					
						
							|  |  |  |     free(fsk); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats){ | 
					
						
							|  |  |  |     /* copy from internal stats, note we can't overwrite stats completely
 | 
					
						
							|  |  |  |        as it has other states rqd by caller, also we want a consistent | 
					
						
							|  |  |  |        interface across modem types for the freedv_api. | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stats->clock_offset = fsk->stats->clock_offset; | 
					
						
							|  |  |  |     stats->snr_est = fsk->stats->snr_est;           // TODO: make this SNR not Eb/No
 | 
					
						
							|  |  |  |     stats->rx_timing = fsk->stats->rx_timing; | 
					
						
							|  |  |  |     stats->foff = fsk->stats->foff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stats->neyesamp = fsk->stats->neyesamp; | 
					
						
							|  |  |  |     stats->neyetr = fsk->stats->neyetr; | 
					
						
							|  |  |  |     memcpy(stats->rx_eye, fsk->stats->rx_eye, sizeof(stats->rx_eye)); | 
					
						
							|  |  |  |     memcpy(stats->f_est, fsk->stats->f_est, fsk->mode*sizeof(float)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* these fields not used for FSK so set to something sensible */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stats->sync = 0; | 
					
						
							|  |  |  |     stats->nr = fsk->stats->nr; | 
					
						
							|  |  |  |     stats->Nc = fsk->stats->Nc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Set the minimum and maximum frequencies at which the freq. estimator can find tones | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void fsk_set_est_limits(struct FSK *fsk,int est_min, int est_max){ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->est_min = est_min; | 
					
						
							|  |  |  |     if(fsk->est_min<0) fsk->est_min = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->est_max = est_max; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Internal function to estimate the frequencies of the two tones within a block of samples. | 
					
						
							|  |  |  |  * This is split off because it is fairly complicated, needs a bunch of memory, and probably | 
					
						
							|  |  |  |  * takes more cycles than the rest of the demod. | 
					
						
							|  |  |  |  * Parameters: | 
					
						
							|  |  |  |  * fsk - FSK struct from demod containing FSK config | 
					
						
							|  |  |  |  * fsk_in - block of samples in this demod cycles, must be nin long | 
					
						
							|  |  |  |  * freqs - Array for the estimated frequencies | 
					
						
							|  |  |  |  * M - number of frequency peaks to find | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void fsk_demod_freq_est(struct FSK *fsk, COMP fsk_in[],float *freqs,int M){ | 
					
						
							|  |  |  |     int Ndft = fsk->Ndft; | 
					
						
							|  |  |  |     int Fs = fsk->Fs; | 
					
						
							|  |  |  |     int nin = fsk->nin; | 
					
						
							|  |  |  |     int i,j; | 
					
						
							|  |  |  |     float hann; | 
					
						
							|  |  |  |     float max; | 
					
						
							|  |  |  |     float tc; | 
					
						
							|  |  |  |     int imax; | 
					
						
							|  |  |  |     kiss_fft_cfg fft_cfg = fsk->fft_cfg; | 
					
						
							|  |  |  |     int *freqi = new int[M]; | 
					
						
							|  |  |  |     int f_min,f_max,f_zero; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Array to do complex FFT from using kiss_fft */ | 
					
						
							|  |  |  |     kiss_fft_cpx *fftin  = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx)*Ndft); | 
					
						
							|  |  |  |     kiss_fft_cpx *fftout = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx)*Ndft); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifndef USE_HANN_TABLE
 | 
					
						
							|  |  |  |     COMP dphi = comp_exp_j((2*M_PI)/((float)Ndft-1)); | 
					
						
							|  |  |  |     COMP rphi = {.5,0}; | 
					
						
							|  |  |  |     rphi = cmult(cconj(dphi),rphi); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f_min  = (fsk->est_min*Ndft)/Fs; | 
					
						
							|  |  |  |     f_max  = (fsk->est_max*Ndft)/Fs; | 
					
						
							|  |  |  |     f_zero = (fsk->est_space*Ndft)/Fs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* scale averaging time constant based on number of samples */ | 
					
						
							|  |  |  |     tc = 0.95*Ndft/Fs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int samps; | 
					
						
							|  |  |  |     int fft_samps; | 
					
						
							|  |  |  |     int fft_loops = nin / Ndft; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (j = 0; j < fft_loops; j++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         /* 48000 sample rate (for example) will have a spare */ | 
					
						
							|  |  |  |         /* 896 samples besides the 46 "Ndft" samples, so adjust */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         samps = (nin - ((j + 1) * Ndft)); | 
					
						
							|  |  |  |         fft_samps = (samps >= Ndft) ? Ndft : samps; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Copy FSK buffer into reals of FFT buffer and apply a hann window */ | 
					
						
							|  |  |  |         for(i=0; i<fft_samps; i++){ | 
					
						
							|  |  |  |             #ifdef USE_HANN_TABLE
 | 
					
						
							|  |  |  |             hann = fsk->hann_table[i]; | 
					
						
							|  |  |  |             #else
 | 
					
						
							|  |  |  |             //hann = 1-cosf((2*M_PI*(float)(i))/((float)fft_samps-1));
 | 
					
						
							|  |  |  |             rphi = cmult(dphi,rphi); | 
					
						
							|  |  |  |             hann = .5-rphi.real; | 
					
						
							|  |  |  |             #endif
 | 
					
						
							|  |  |  |             fftin[i].r = hann*fsk_in[i+Ndft*j].real; | 
					
						
							|  |  |  |             fftin[i].i = hann*fsk_in[i+Ndft*j].imag; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Zero out the remaining slots on spare samples */ | 
					
						
							|  |  |  |         for(; i<Ndft;i++){ | 
					
						
							|  |  |  |             fftin[i].r = 0; | 
					
						
							|  |  |  |             fftin[i].i = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Do the FFT */ | 
					
						
							|  |  |  |         kiss_fft(fft_cfg,fftin,fftout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Find the magnitude^2 of each freq slot and stash away in the real
 | 
					
						
							|  |  |  |         * value, so this only has to be done once. Since we're only comparing | 
					
						
							|  |  |  |         * these values and only need the mag of 2 points, we don't need to do | 
					
						
							|  |  |  |         * a sqrt to each value */ | 
					
						
							|  |  |  |         for(i=0; i<Ndft/2; i++){ | 
					
						
							|  |  |  |             fftout[i].r = (fftout[i].r*fftout[i].r) + (fftout[i].i*fftout[i].i) ; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Zero out the minimum and maximum ends */ | 
					
						
							|  |  |  |         for(i=0; i<f_min; i++){ | 
					
						
							|  |  |  |             fftout[i].r = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for(i=f_max-1; i<Ndft/2; i++){ | 
					
						
							|  |  |  |             fftout[i].r = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Mix back in with the previous fft block */ | 
					
						
							|  |  |  |         /* Copy new fft est into imag of fftout for frequency divination below */ | 
					
						
							|  |  |  |         for(i=0; i<Ndft/2; i++){ | 
					
						
							|  |  |  |             fsk->fft_est[i] = (fsk->fft_est[i]*(1-tc)) + (sqrtf(fftout[i].r)*tc); | 
					
						
							|  |  |  |             fftout[i].i = fsk->fft_est[i]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     modem_probe_samp_f("t_fft_est", fsk->fft_est, Ndft/2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     max = 0; | 
					
						
							|  |  |  |     /* Find the M frequency peaks here */ | 
					
						
							|  |  |  |     for(i=0; i<M; i++){ | 
					
						
							|  |  |  |         imax = 0; | 
					
						
							|  |  |  |         max = 0; | 
					
						
							|  |  |  |         for(j=0;j<Ndft/2;j++){ | 
					
						
							|  |  |  |             if(fftout[j].i > max){ | 
					
						
							|  |  |  |                 max = fftout[j].i; | 
					
						
							|  |  |  |                 imax = j; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Blank out FMax +/-Fspace/2 */ | 
					
						
							|  |  |  |         f_min = imax - f_zero; | 
					
						
							|  |  |  |         f_min = f_min < 0 ? 0 : f_min; | 
					
						
							|  |  |  |         f_max = imax + f_zero; | 
					
						
							|  |  |  |         f_max = f_max > Ndft ? Ndft : f_max; | 
					
						
							|  |  |  |         for(j=f_min; j<f_max; j++) | 
					
						
							|  |  |  |             fftout[j].i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Stick the freq index on the list */ | 
					
						
							|  |  |  |         freqi[i] = imax; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Gnome sort the freq list */ | 
					
						
							|  |  |  |     /* My favorite sort of sort*/ | 
					
						
							|  |  |  |     i = 1; | 
					
						
							|  |  |  |     while(i<M){ | 
					
						
							|  |  |  |         if(freqi[i] >= freqi[i-1]) i++; | 
					
						
							|  |  |  |         else{ | 
					
						
							|  |  |  |             j = freqi[i]; | 
					
						
							|  |  |  |             freqi[i] = freqi[i-1]; | 
					
						
							|  |  |  |             freqi[i-1] = j; | 
					
						
							|  |  |  |             if(i>1) i--; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Convert freqs from indices to frequencies */ | 
					
						
							|  |  |  |     for(i=0; i<M; i++){ | 
					
						
							|  |  |  |         freqs[i] = (float)(freqi[i])*((float)Fs/(float)Ndft); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] freqi; | 
					
						
							|  |  |  |     free(fftin); | 
					
						
							|  |  |  |     free(fftout); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[]){ | 
					
						
							|  |  |  |     int N = fsk->N; | 
					
						
							|  |  |  |     int Ts = fsk->Ts; | 
					
						
							|  |  |  |     int Rs = fsk->Rs; | 
					
						
							|  |  |  |     int Fs = fsk->Fs; | 
					
						
							|  |  |  |     int nsym = fsk->Nsym; | 
					
						
							|  |  |  |     int nin = fsk->nin; | 
					
						
							|  |  |  |     int P = fsk->P; | 
					
						
							|  |  |  |     int Nmem = fsk->Nmem; | 
					
						
							|  |  |  |     int M = fsk->mode; | 
					
						
							|  |  |  |     int i, j, m, dc_i, cbuf_i; | 
					
						
							|  |  |  |     float ft1; | 
					
						
							|  |  |  |     int nstash = fsk->nstash; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP* *f_int = new COMP*[M];    /* Filtered and downsampled symbol tones */ | 
					
						
							|  |  |  |     COMP *t = new COMP[M];          /* complex number temps */ | 
					
						
							|  |  |  |     COMP t_c;           /* another complex temp */ | 
					
						
							|  |  |  |     COMP *phi_c = new COMP[M]; | 
					
						
							|  |  |  |     COMP phi_ft; | 
					
						
							|  |  |  |     int nold = Nmem-nin; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP *dphi = new COMP[M]; | 
					
						
							|  |  |  |     COMP dphift; | 
					
						
							|  |  |  |     float rx_timing,norm_rx_timing,old_norm_rx_timing,d_norm_rx_timing,appm; | 
					
						
							|  |  |  |     int using_old_samps; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP* sample_src; | 
					
						
							|  |  |  |     COMP* f_intbuf_m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float *f_est = new float[M]; | 
					
						
							|  |  |  |     float fc_avg, fc_tx; | 
					
						
							|  |  |  |     float meanebno,stdebno,eye_max; | 
					
						
							|  |  |  |     int neyesamp,neyeoffset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef MODEMPROBE_ENABLE
 | 
					
						
							|  |  |  |     char mp_name_tmp[20]; /* Temporary string for modem probe trace names */ | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //for(size_t jj = 0; jj<nin; jj++){
 | 
					
						
							|  |  |  |     //    fprintf(stderr,"%f,j%f,",fsk_in[jj].real,fsk_in[jj].imag);
 | 
					
						
							|  |  |  |     //}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Load up demod phases from struct */ | 
					
						
							|  |  |  |     for(m = 0; m < M; m++) { | 
					
						
							|  |  |  |         phi_c[m] = fsk->phi_c[m]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Estimate tone frequencies */ | 
					
						
							|  |  |  |     fsk_demod_freq_est(fsk,fsk_in,f_est,M); | 
					
						
							|  |  |  |     modem_probe_samp_f("t_f_est",f_est,M); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Allocate circular buffer for integration */ | 
					
						
							|  |  |  |     f_intbuf_m = (COMP*) malloc(sizeof(COMP)*Ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* allocate memory for the integrated samples */ | 
					
						
							|  |  |  |     for( m=0; m<M; m++){ | 
					
						
							|  |  |  |         /* allocate memory for the integrated samples */ | 
					
						
							|  |  |  |         /* Note: This must be kept after fsk_demod_freq_est for memory usage reasons */ | 
					
						
							|  |  |  |         f_int[m] = (COMP*) malloc(sizeof(COMP)*(nsym+1)*P); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* If this is the first run, we won't have any valid f_est */ | 
					
						
							|  |  |  |     /* TODO: add first_run flag to FSK to make negative freqs possible */ | 
					
						
							|  |  |  |     if(fsk->f_est[0]<1){ | 
					
						
							|  |  |  |         for( m=0; m<M; m++) | 
					
						
							|  |  |  |             fsk->f_est[m] = f_est[m]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Initalize downmixers for each symbol tone */ | 
					
						
							|  |  |  |     for( m=0; m<M; m++){ | 
					
						
							|  |  |  |         /* Back the stored phase off to account for re-integraton of old samples */ | 
					
						
							|  |  |  |         dphi[m] = comp_exp_j(-2*(Nmem-nin-(Ts/P))*M_PI*((fsk->f_est[m])/(float)(Fs))); | 
					
						
							|  |  |  |         phi_c[m] = cmult(dphi[m],phi_c[m]); | 
					
						
							|  |  |  |         //fprintf(stderr,"F%d = %f",m,fsk->f_est[m]);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Figure out how much to nudge each sample downmixer for every sample */ | 
					
						
							|  |  |  |         dphi[m] = comp_exp_j(2*M_PI*((fsk->f_est[m])/(float)(Fs))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Integrate and downsample for symbol tones */ | 
					
						
							|  |  |  |     for(m=0; m<M; m++){ | 
					
						
							|  |  |  |         /* Copy buffer pointers in to avoid second buffer indirection */ | 
					
						
							|  |  |  |         float f_est_m = f_est[m]; | 
					
						
							|  |  |  |         COMP* f_int_m = &(f_int[m][0]); | 
					
						
							|  |  |  |         COMP dphi_m = dphi[m]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dc_i = 0; | 
					
						
							|  |  |  |         cbuf_i = 0; | 
					
						
							|  |  |  |         sample_src = &(fsk->samp_old[nstash-nold]); | 
					
						
							|  |  |  |         using_old_samps = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Pre-fill integration buffer */ | 
					
						
							|  |  |  |         for(dc_i=0; dc_i<Ts-(Ts/P); dc_i++){ | 
					
						
							|  |  |  |             /* Switch sample source to new samples when we run out of old ones */ | 
					
						
							|  |  |  |             if(dc_i>=nold && using_old_samps){ | 
					
						
							|  |  |  |                 sample_src = &fsk_in[0]; | 
					
						
							|  |  |  |                 dc_i = 0; | 
					
						
							|  |  |  |                 using_old_samps = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 /* Recalculate delta-phi after switching to new sample source */ | 
					
						
							|  |  |  |                 phi_c[m] = comp_normalize(phi_c[m]); | 
					
						
							|  |  |  |                 dphi_m = comp_exp_j(2*M_PI*((f_est_m)/(float)(Fs))); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             /* Downconvert and place into integration buffer */ | 
					
						
							|  |  |  |             f_intbuf_m[dc_i]=cmult(sample_src[dc_i],cconj(phi_c[m])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             #ifdef MODEMPROBE_ENABLE
 | 
					
						
							|  |  |  |             snprintf(mp_name_tmp,19,"t_f%zd_dc",m+1); | 
					
						
							|  |  |  |             modem_probe_samp_c(mp_name_tmp,&f_intbuf_m[dc_i],1); | 
					
						
							|  |  |  |             #endif
 | 
					
						
							|  |  |  |             /* Spin downconversion phases */ | 
					
						
							|  |  |  |             phi_c[m] = cmult(phi_c[m],dphi_m); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         cbuf_i = dc_i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Integrate over Ts at offsets of Ts/P */ | 
					
						
							|  |  |  |         for(i=0; i<(nsym+1)*P; i++){ | 
					
						
							|  |  |  |             /* Downconvert and Place Ts/P samples in the integration buffers */ | 
					
						
							|  |  |  |             for(j=0; j<(Ts/P); j++,dc_i++){ | 
					
						
							|  |  |  |                 /* Switch sample source to new samples when we run out of old ones */ | 
					
						
							|  |  |  |                 if(dc_i>=nold && using_old_samps){ | 
					
						
							|  |  |  |                     sample_src = &fsk_in[0]; | 
					
						
							|  |  |  |                     dc_i = 0; | 
					
						
							|  |  |  |                     using_old_samps = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     /* Recalculate delta-phi after switching to new sample source */ | 
					
						
							|  |  |  |                     phi_c[m] = comp_normalize(phi_c[m]); | 
					
						
							|  |  |  |                     dphi_m = comp_exp_j(2*M_PI*((f_est_m)/(float)(Fs))); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 /* Downconvert and place into integration buffer */ | 
					
						
							|  |  |  |                 f_intbuf_m[cbuf_i+j]=cmult(sample_src[dc_i],cconj(phi_c[m])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 #ifdef MODEMPROBE_ENABLE
 | 
					
						
							|  |  |  |                 snprintf(mp_name_tmp,19,"t_f%zd_dc",m+1); | 
					
						
							|  |  |  |                 modem_probe_samp_c(mp_name_tmp,&f_intbuf_m[cbuf_i+j],1); | 
					
						
							|  |  |  |                 #endif
 | 
					
						
							|  |  |  |                 /* Spin downconversion phases */ | 
					
						
							|  |  |  |                 phi_c[m] = cmult(phi_c[m],dphi_m); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* Dump internal samples */ | 
					
						
							|  |  |  |             cbuf_i += Ts/P; | 
					
						
							|  |  |  |             if(cbuf_i>=Ts) cbuf_i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* Integrate over the integration buffers, save samples */ | 
					
						
							|  |  |  |             float it_r = 0; | 
					
						
							|  |  |  |             float it_i = 0; | 
					
						
							|  |  |  |             for(j=0; j<Ts; j++){ | 
					
						
							|  |  |  |                 it_r += f_intbuf_m[j].real; | 
					
						
							|  |  |  |                 it_i += f_intbuf_m[j].imag; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             f_int_m[i].real = it_r; | 
					
						
							|  |  |  |             f_int_m[i].imag = it_i; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Save phases back into FSK struct */ | 
					
						
							|  |  |  |     for(m=0; m<M; m++){ | 
					
						
							|  |  |  |         fsk->phi_c[m] = phi_c[m]; | 
					
						
							|  |  |  |         fsk->f_est[m] = f_est[m]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Stash samples away in the old sample buffer for the next round of bit getting */ | 
					
						
							|  |  |  |     memcpy((void*)&(fsk->samp_old[0]),(void*)&(fsk_in[nin-nstash]),sizeof(COMP)*nstash); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Fine Timing Estimation */ | 
					
						
							|  |  |  |     /* Apply magic nonlinearity to f1_int and f2_int, shift down to 0,
 | 
					
						
							|  |  |  |      * extract angle */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Figure out how much to spin the oscillator to extract magic spectral line */ | 
					
						
							|  |  |  |     dphift = comp_exp_j(2*M_PI*((float)(Rs)/(float)(P*Rs))); | 
					
						
							|  |  |  |     phi_ft.real = 1; | 
					
						
							|  |  |  |     phi_ft.imag = 0; | 
					
						
							|  |  |  |     t_c=comp0(); | 
					
						
							|  |  |  |     for(i=0; i<(nsym+1)*P; i++){ | 
					
						
							|  |  |  |         /* Get abs^2 of fx_int[i], and add 'em */ | 
					
						
							|  |  |  |         ft1 = 0; | 
					
						
							|  |  |  |         for( m=0; m<M; m++){ | 
					
						
							|  |  |  |             ft1 += (f_int[m][i].real*f_int[m][i].real) + (f_int[m][i].imag*f_int[m][i].imag); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Down shift and accumulate magic line */ | 
					
						
							|  |  |  |         t_c = cadd(t_c,fcmult(ft1,phi_ft)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Spin the oscillator for the magic line shift */ | 
					
						
							|  |  |  |         phi_ft = cmult(phi_ft,dphift); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Check for NaNs in the fine timing estimate, return if found */ | 
					
						
							|  |  |  |     /* otherwise segfaults happen */ | 
					
						
							|  |  |  |     if( isnan(t_c.real) || isnan(t_c.imag)){ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Get the magic angle */ | 
					
						
							|  |  |  |     norm_rx_timing =  atan2f(t_c.imag,t_c.real)/(2*M_PI); | 
					
						
							|  |  |  |     rx_timing = norm_rx_timing*(float)P; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     old_norm_rx_timing = fsk->norm_rx_timing; | 
					
						
							|  |  |  |     fsk->norm_rx_timing = norm_rx_timing; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Estimate sample clock offset */ | 
					
						
							|  |  |  |     d_norm_rx_timing = norm_rx_timing - old_norm_rx_timing; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Filter out big jumps in due to nin change */ | 
					
						
							|  |  |  |     if(fabsf(d_norm_rx_timing) < .2){ | 
					
						
							|  |  |  |         appm = 1e6*d_norm_rx_timing/(float)nsym; | 
					
						
							|  |  |  |         fsk->ppm = .9*fsk->ppm + .1*appm; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Figure out how many samples are needed the next modem cycle */ | 
					
						
							|  |  |  |     /* Unless we're in burst mode */ | 
					
						
							|  |  |  |     if(!fsk->burst_mode){ | 
					
						
							|  |  |  |         if(norm_rx_timing > 0.25) | 
					
						
							|  |  |  |             fsk->nin = N+Ts/2; | 
					
						
							|  |  |  |         else if(norm_rx_timing < -0.25) | 
					
						
							|  |  |  |             fsk->nin = N-Ts/2; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             fsk->nin = N; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     modem_probe_samp_f("t_norm_rx_timing",&(norm_rx_timing),1); | 
					
						
							|  |  |  |     modem_probe_samp_i("t_nin",&(fsk->nin),1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Re-sample the integrators with linear interpolation magic */ | 
					
						
							|  |  |  |     int low_sample = (int)floorf(rx_timing); | 
					
						
							|  |  |  |     float fract = rx_timing - (float)low_sample; | 
					
						
							|  |  |  |     int high_sample = (int)ceilf(rx_timing); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Vars for finding the max-of-4 for each bit */ | 
					
						
							|  |  |  |     float *tmax = new float[M]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef EST_EBNO
 | 
					
						
							|  |  |  |     meanebno = 0; | 
					
						
							|  |  |  |     stdebno = 0; | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* FINALLY, THE BITS */ | 
					
						
							|  |  |  |     /* also, resample fx_int */ | 
					
						
							|  |  |  |     for(i = 0; i < nsym; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int st = (i+1)*P; | 
					
						
							|  |  |  |         for( m=0; m<M; m++){ | 
					
						
							|  |  |  |             t[m] =           fcmult(1-fract,f_int[m][st+ low_sample]); | 
					
						
							|  |  |  |             t[m] = cadd(t[m],fcmult(  fract,f_int[m][st+high_sample])); | 
					
						
							|  |  |  |             /* Figure mag^2 of each resampled fx_int */ | 
					
						
							|  |  |  |             tmax[m] = (t[m].real*t[m].real) + (t[m].imag*t[m].imag); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         float max = tmax[0]; /* Maximum for figuring correct symbol */ | 
					
						
							|  |  |  |         float min = tmax[0]; | 
					
						
							|  |  |  |         int sym = 0; /* Index of maximum */ | 
					
						
							|  |  |  |         for( m=0; m<M; m++){ | 
					
						
							|  |  |  |             if(tmax[m]>max){ | 
					
						
							|  |  |  |                 max = tmax[m]; | 
					
						
							|  |  |  |                 sym = m; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if(tmax[m]<min){ | 
					
						
							|  |  |  |                 min = tmax[m]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Get the actual bit */ | 
					
						
							|  |  |  |         if(rx_bits != NULL){ | 
					
						
							|  |  |  |             /* Get bits for 2FSK and 4FSK */ | 
					
						
							|  |  |  |             /* TODO: Replace this with something more generic maybe */ | 
					
						
							|  |  |  |             if(M==2){ | 
					
						
							|  |  |  |                 rx_bits[i] = sym==1;                /* 2FSK. 1 bit per symbol */ | 
					
						
							|  |  |  |             }else if(M==4){ | 
					
						
							|  |  |  |                 rx_bits[(i*2)+1] = (sym&0x1);       /* 4FSK. 2 bits per symbol */ | 
					
						
							|  |  |  |                 rx_bits[(i*2)  ] = (sym&0x2)>>1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Produce soft decision symbols */ | 
					
						
							|  |  |  |         if(rx_sd != NULL){ | 
					
						
							|  |  |  |             /* Convert symbols from max^2 into max */ | 
					
						
							|  |  |  |             for( m=0; m<M; m++) | 
					
						
							|  |  |  |                 tmax[m] = sqrtf(tmax[m]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if(M==2){ | 
					
						
							|  |  |  |                 rx_sd[i] = tmax[0] - tmax[1]; | 
					
						
							|  |  |  |             }else if(M==4){ | 
					
						
							|  |  |  |                 /* TODO: Find a soft-decision mode that works for 4FSK */ | 
					
						
							|  |  |  |                 min = sqrtf(min); | 
					
						
							|  |  |  |                 rx_sd[(i*2)+1] = - tmax[0] ;  /* Bits=00 */ | 
					
						
							|  |  |  |                 rx_sd[(i*2)  ] = - tmax[0] ; | 
					
						
							|  |  |  |                 rx_sd[(i*2)+1]+=   tmax[1] ;  /* Bits=01 */ | 
					
						
							|  |  |  |                 rx_sd[(i*2)  ]+= - tmax[1] ; | 
					
						
							|  |  |  |                 rx_sd[(i*2)+1]+= - tmax[2] ;  /* Bits=10 */ | 
					
						
							|  |  |  |                 rx_sd[(i*2)  ]+=   tmax[2] ; | 
					
						
							|  |  |  |                 rx_sd[(i*2)+1]+=   tmax[3] ;  /* Bits=11 */ | 
					
						
							|  |  |  |                 rx_sd[(i*2)  ]+=   tmax[3] ; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Accumulate resampled int magnitude for EbNodB estimation */ | 
					
						
							|  |  |  |         /* Standard deviation is calculated by algorithm devised by crafty soviets */ | 
					
						
							|  |  |  |         #ifdef EST_EBNO
 | 
					
						
							|  |  |  |         /* Accumulate the square of the sampled value */ | 
					
						
							|  |  |  |         ft1 = max; | 
					
						
							|  |  |  |         stdebno += ft1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Figure the abs value of the max tone */ | 
					
						
							|  |  |  |         meanebno += sqrtf(ft1); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         /* Soft output goes here */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] tmax; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef EST_EBNO
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Calculate mean for EbNodB estimation */ | 
					
						
							|  |  |  |     meanebno = meanebno/(float)nsym; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Calculate the std. dev for EbNodB estimate */ | 
					
						
							|  |  |  |     stdebno = (stdebno/(float)nsym) - (meanebno*meanebno); | 
					
						
							|  |  |  |     /* trap any negative numbers to avoid NANs flowing through */ | 
					
						
							|  |  |  |     if (stdebno > 0.0) { | 
					
						
							|  |  |  |         stdebno = sqrt(stdebno); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         stdebno = 0.0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->EbNodB = -6+(20*log10f((1e-6+meanebno)/(1e-6+stdebno))); | 
					
						
							|  |  |  |     #else
 | 
					
						
							|  |  |  |     fsk->EbNodB = 1; | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Write some statistics to the stats struct */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Save clock offset in ppm */ | 
					
						
							|  |  |  |     fsk->stats->clock_offset = fsk->ppm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Calculate and save SNR from EbNodB estimate */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats->snr_est = .5*fsk->stats->snr_est + .5*fsk->EbNodB;//+ 10*log10f(((float)Rs)/((float)Rs*M));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Save rx timing */ | 
					
						
							|  |  |  |     fsk->stats->rx_timing = (float)rx_timing; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Estimate and save frequency offset */ | 
					
						
							|  |  |  |     fc_avg = (f_est[0]+f_est[1])/2; | 
					
						
							|  |  |  |     fc_tx = (fsk->f1_tx+fsk->f1_tx+fsk->fs_tx)/2; | 
					
						
							|  |  |  |     fsk->stats->foff = fc_tx-fc_avg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Take a sample for the eye diagrams ---------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* due to oversample rate P, we have too many samples for eye
 | 
					
						
							|  |  |  |        trace.  So lets output a decimated version.  We use 2P | 
					
						
							|  |  |  |        as we want two symbols worth of samples in trace  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int neyesamp_dec = ceil(((float)P*2)/MODEM_STATS_EYE_IND_MAX); | 
					
						
							|  |  |  |     neyesamp = (P*2)/neyesamp_dec; | 
					
						
							|  |  |  |     assert(neyesamp <= MODEM_STATS_EYE_IND_MAX); | 
					
						
							|  |  |  |     fsk->stats->neyesamp = neyesamp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef I_DONT_UNDERSTAND
 | 
					
						
							|  |  |  |     neyeoffset = high_sample+1+(P*28); /* WTF this line? Where does "28" come from ?                           */ | 
					
						
							|  |  |  |     #endif                             /* ifdef-ed out as I am afraid it will index out of memory as P changes */
 | 
					
						
							|  |  |  |     neyeoffset = high_sample+1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int eye_traces = MODEM_STATS_ET_MAX/M; | 
					
						
							|  |  |  |     int ind; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats->neyetr = fsk->mode*eye_traces; | 
					
						
							|  |  |  |     for( i=0; i<eye_traces; i++){ | 
					
						
							|  |  |  |         for ( m=0; m<M; m++){ | 
					
						
							|  |  |  |             for(j=0; j<neyesamp; j++) { | 
					
						
							|  |  |  |                /*
 | 
					
						
							|  |  |  |                   2*P*i...........: advance two symbols for next trace | 
					
						
							|  |  |  |                   neyeoffset......: centre trace on ideal timing offset, peak eye opening | 
					
						
							|  |  |  |                   j*neweyesamp_dec: For 2*P>MODEM_STATS_EYE_IND_MAX advance through integrated | 
					
						
							|  |  |  |                                     samples newamp_dec at a time so we dont overflow rx_eye[][] | 
					
						
							|  |  |  |                */ | 
					
						
							|  |  |  |                ind = 2*P*i + neyeoffset + j*neyesamp_dec; | 
					
						
							|  |  |  |                assert((i*M+m) < MODEM_STATS_ET_MAX); | 
					
						
							|  |  |  |                assert(ind < (nsym+1)*P); | 
					
						
							|  |  |  |                fsk->stats->rx_eye[i*M+m][j] = cabsolute(f_int[m][ind]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (fsk->normalise_eye) { | 
					
						
							|  |  |  |         eye_max = 0; | 
					
						
							|  |  |  |         /* Normalize eye to +/- 1 */ | 
					
						
							|  |  |  |         for(i=0; i<M*eye_traces; i++) | 
					
						
							|  |  |  |             for(j=0; j<neyesamp; j++) | 
					
						
							|  |  |  |                 if(fabsf(fsk->stats->rx_eye[i][j])>eye_max) | 
					
						
							|  |  |  |                     eye_max = fabsf(fsk->stats->rx_eye[i][j]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(i=0; i<M*eye_traces; i++) | 
					
						
							|  |  |  |             for(j=0; j<neyesamp; j++) | 
					
						
							|  |  |  |                 fsk->stats->rx_eye[i][j] = fsk->stats->rx_eye[i][j]/eye_max; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fsk->stats->nr = 0; | 
					
						
							|  |  |  |     fsk->stats->Nc = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0; i<M; i++) { | 
					
						
							|  |  |  |         fsk->stats->f_est[i] = f_est[i]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Dump some internal samples */ | 
					
						
							|  |  |  |     modem_probe_samp_f("t_EbNodB",&(fsk->EbNodB),1); | 
					
						
							|  |  |  |     modem_probe_samp_f("t_ppm",&(fsk->ppm),1); | 
					
						
							|  |  |  |     modem_probe_samp_f("t_rx_timing",&(rx_timing),1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef MODEMPROBE_ENABLE
 | 
					
						
							|  |  |  |     for( m=0; m<M; m++){ | 
					
						
							|  |  |  |         snprintf(mp_name_tmp,19,"t_f%zd_int",m+1); | 
					
						
							|  |  |  |         modem_probe_samp_c(mp_name_tmp,f_int[m],(nsym+1)*P); | 
					
						
							|  |  |  |         snprintf(mp_name_tmp,19,"t_f%zd",m+1); | 
					
						
							|  |  |  |         modem_probe_samp_f(mp_name_tmp,&f_est[m],1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for( m=0; m<M; m++){ | 
					
						
							|  |  |  |         free(f_int[m]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     free(f_intbuf_m); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] f_est; | 
					
						
							|  |  |  |     delete[] dphi; | 
					
						
							|  |  |  |     delete[] phi_c; | 
					
						
							|  |  |  |     delete[] t; | 
					
						
							|  |  |  |     delete[] f_int; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_demod(struct FSK *fsk, uint8_t rx_bits[], COMP fsk_in[]){ | 
					
						
							|  |  |  |     fsk2_demod(fsk,rx_bits,NULL,fsk_in); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_demod_sd(struct FSK *fsk, float rx_sd[], COMP fsk_in[]){ | 
					
						
							|  |  |  |     fsk2_demod(fsk,NULL,rx_sd,fsk_in); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_mod(struct FSK *fsk,float fsk_out[],uint8_t tx_bits[]){ | 
					
						
							|  |  |  |     COMP tx_phase_c = fsk->tx_phase_c; /* Current complex TX phase */ | 
					
						
							|  |  |  |     int f1_tx = fsk->f1_tx;         /* '0' frequency */ | 
					
						
							|  |  |  |     int fs_tx = fsk->fs_tx;         /* space between frequencies */ | 
					
						
							|  |  |  |     int Ts = fsk->Ts;               /* samples-per-symbol */ | 
					
						
							|  |  |  |     int Fs = fsk->Fs;               /* sample freq */ | 
					
						
							|  |  |  |     int M = fsk->mode; | 
					
						
							|  |  |  |     COMP *dosc_f = new COMP[M];     /* phase shift per sample */ | 
					
						
							|  |  |  |     COMP dph;                       /* phase shift of current bit */ | 
					
						
							|  |  |  |     int i, j, m, bit_i, sym; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Init the per sample phase shift complex numbers */ | 
					
						
							|  |  |  |     for( m=0; m<M; m++){ | 
					
						
							|  |  |  |         dosc_f[m] = comp_exp_j(2*M_PI*((float)(f1_tx+(fs_tx*m))/(float)(Fs))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bit_i = 0; | 
					
						
							|  |  |  |     for( i=0; i<fsk->Nsym; i++){ | 
					
						
							|  |  |  |         sym = 0; | 
					
						
							|  |  |  |         /* Pack the symbol number from the bit stream */ | 
					
						
							|  |  |  |         for( m=M; m>>=1; ){ | 
					
						
							|  |  |  |             uint8_t bit = tx_bits[bit_i]; | 
					
						
							|  |  |  |             bit = (bit==1)?1:0; | 
					
						
							|  |  |  |             sym = (sym<<1)|bit; | 
					
						
							|  |  |  |             bit_i++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Look up symbol phase shift */ | 
					
						
							|  |  |  |         dph = dosc_f[sym]; | 
					
						
							|  |  |  |         /* Spin the oscillator for a symbol period */ | 
					
						
							|  |  |  |         for(j=0; j<Ts; j++){ | 
					
						
							|  |  |  |             tx_phase_c = cmult(tx_phase_c,dph); | 
					
						
							|  |  |  |             fsk_out[i*Ts+j] = 2*tx_phase_c.real; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Normalize TX phase to prevent drift */ | 
					
						
							|  |  |  |     tx_phase_c = comp_normalize(tx_phase_c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* save TX phase */ | 
					
						
							|  |  |  |     fsk->tx_phase_c = tx_phase_c; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] dosc_f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_mod_c(struct FSK *fsk,COMP fsk_out[],uint8_t tx_bits[]){ | 
					
						
							|  |  |  |     COMP tx_phase_c = fsk->tx_phase_c; /* Current complex TX phase */ | 
					
						
							|  |  |  |     int f1_tx = fsk->f1_tx;         /* '0' frequency */ | 
					
						
							|  |  |  |     int fs_tx = fsk->fs_tx;         /* space between frequencies */ | 
					
						
							|  |  |  |     int Ts = fsk->Ts;               /* samples-per-symbol */ | 
					
						
							|  |  |  |     int Fs = fsk->Fs;               /* sample freq */ | 
					
						
							|  |  |  |     int M = fsk->mode; | 
					
						
							|  |  |  |     COMP *dosc_f = new COMP[M];     /* phase shift per sample */ | 
					
						
							|  |  |  |     COMP dph;                       /* phase shift of current bit */ | 
					
						
							|  |  |  |     int i, j, bit_i, sym; | 
					
						
							|  |  |  |     int m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Init the per sample phase shift complex numbers */ | 
					
						
							|  |  |  |     for( m=0; m<M; m++){ | 
					
						
							|  |  |  |         dosc_f[m] = comp_exp_j(2*M_PI*((float)(f1_tx+(fs_tx*m))/(float)(Fs))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bit_i = 0; | 
					
						
							|  |  |  |     for( i=0; i<fsk->Nsym; i++){ | 
					
						
							|  |  |  |         sym = 0; | 
					
						
							|  |  |  |         /* Pack the symbol number from the bit stream */ | 
					
						
							|  |  |  |         for( m=M; m>>=1; ){ | 
					
						
							|  |  |  |             uint8_t bit = tx_bits[bit_i]; | 
					
						
							|  |  |  |             bit = (bit==1)?1:0; | 
					
						
							|  |  |  |             sym = (sym<<1)|bit; | 
					
						
							|  |  |  |             bit_i++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Look up symbol phase shift */ | 
					
						
							|  |  |  |         dph = dosc_f[sym]; | 
					
						
							|  |  |  |         /* Spin the oscillator for a symbol period */ | 
					
						
							|  |  |  |         for(j=0; j<Ts; j++){ | 
					
						
							|  |  |  |             tx_phase_c = cmult(tx_phase_c,dph); | 
					
						
							|  |  |  |             fsk_out[i*Ts+j] = fcmult(2,tx_phase_c); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Normalize TX phase to prevent drift */ | 
					
						
							|  |  |  |     tx_phase_c = comp_normalize(tx_phase_c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* save TX phase */ | 
					
						
							|  |  |  |     fsk->tx_phase_c = tx_phase_c; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] dosc_f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Modulator that assume an external VCO.  The output is a voltage
 | 
					
						
							|  |  |  |    that changes for each symbol */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_mod_ext_vco(struct FSK *fsk, float vco_out[], uint8_t tx_bits[]) { | 
					
						
							|  |  |  |     int f1_tx = fsk->f1_tx;         /* '0' frequency */ | 
					
						
							|  |  |  |     int fs_tx = fsk->fs_tx;         /* space between frequencies */ | 
					
						
							|  |  |  |     int Ts = fsk->Ts;               /* samples-per-symbol */ | 
					
						
							|  |  |  |     int M = fsk->mode; | 
					
						
							|  |  |  |     int i, j, m, sym, bit_i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bit_i = 0; | 
					
						
							|  |  |  |     for(i=0; i<fsk->Nsym; i++) { | 
					
						
							|  |  |  |         /* generate the symbol number from the bit stream,
 | 
					
						
							|  |  |  |            e.g. 0,1 for 2FSK, 0,1,2,3 for 4FSK */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sym = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* unpack the symbol number from the bit stream */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for( m=M; m>>=1; ){ | 
					
						
							|  |  |  |             uint8_t bit = tx_bits[bit_i]; | 
					
						
							|  |  |  |             bit = (bit==1)?1:0; | 
					
						
							|  |  |  |             sym = (sym<<1)|bit; | 
					
						
							|  |  |  |             bit_i++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /*
 | 
					
						
							|  |  |  |            Map 'sym' to VCO frequency | 
					
						
							|  |  |  |            Note: drive is inverted, a higher tone drives VCO voltage lower | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //fprintf(stderr, "i: %d sym: %d freq: %f\n", i, sym, f1_tx + fs_tx*(float)sym);
 | 
					
						
							|  |  |  |         for(j=0; j<Ts; j++) { | 
					
						
							|  |  |  |             vco_out[i*Ts+j] = f1_tx + fs_tx*(float)sym; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void fsk_stats_normalise_eye(struct FSK *fsk, int normalise_enable) { | 
					
						
							|  |  |  |     fsk->normalise_eye = normalise_enable; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // freeDV
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |