From 2bdae577743a93be05eeb82c22d81fd4e84fd0c1 Mon Sep 17 00:00:00 2001 From: Mark Jessop Date: Sun, 7 Jun 2020 20:37:20 +0930 Subject: [PATCH] Rebase fsk_demod. --- auto_rx/test/generate_lowsnr.py | 4 +- auto_rx/test/test_demod.py | 87 ++- utils/codec2_fdmdv.h | 6 +- utils/comp_prim.h | 2 +- utils/fsk.c | 1145 +++++++++++++------------------ utils/fsk.h | 47 +- utils/fsk_demod.c | 220 +++--- utils/modem_probe.h | 1 + utils/modem_stats.c | 7 +- utils/modem_stats.h | 27 +- utils/test.c | 80 +++ 11 files changed, 795 insertions(+), 831 deletions(-) create mode 100644 utils/test.c diff --git a/auto_rx/test/generate_lowsnr.py b/auto_rx/test/generate_lowsnr.py index ca16afd..bc83f78 100644 --- a/auto_rx/test/generate_lowsnr.py +++ b/auto_rx/test/generate_lowsnr.py @@ -38,12 +38,12 @@ NORMALISE = True SAMPLES = [ ['rs41_96k_float.bin', 4800, -20.0, 96000], - ['rs92_96k_float.bin', 2400, -100, 96000], # No threshold set, as signal is continuous. + ['rs92_96k_float.bin', 4800, -100, 96000], # No threshold set, as signal is continuous. ['dfm09_96k_float.bin', 2500, -100, 96000], # Weird baud rate. No threshold set, as signal is continuous. ['m10_96k_float.bin', 9616, -10.0, 96000], # Really weird baud rate. ['imet4_96k_float.bin', 1200, -10.0, 96000], # 1200 baud, but AFSK, so we expect 7-8 dB worse performance than the other sondes. ['rsngp_96k_float.bin', 2400, -100.0, 96000], # RS92-NGP - wider bandwidth. - ['lms6-400_96k_float.bin', 4800, -100, 96000] # LMS6, 400 MHz variant. Continuous signal. + ['lms6-400_96k_float.bin', 9600, -100, 96000] # LMS6, 400 MHz variant. Continuous signal. ] diff --git a/auto_rx/test/test_demod.py b/auto_rx/test/test_demod.py index d6cfa41..582c187 100644 --- a/auto_rx/test/test_demod.py +++ b/auto_rx/test/test_demod.py @@ -121,7 +121,7 @@ processing_type = { # # RS41 Decoding 'rs41_fsk_demod': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 0.500 | ../fsk_demod --cs16 -b 1 -u 24000 --stats=100 2 48000 4800 - - 2>stats.txt |", + 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 0.500 | ../fsk_demod --cs16 -b 1 -u 24000 --stats=5 2 48000 4800 - - 2>stats.txt |", # Decode using rs41ecc 'decode': "../rs41mod --ecc --ptu --crc --bin --json 2>/dev/null", @@ -132,7 +132,7 @@ processing_type = { # # RS92 Decoding 'rs92_fsk_demod': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=100 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", # Decode using rs41ecc 'decode': "../rs92mod -vx -v --crc --ecc --vel 2>/dev/null", @@ -142,7 +142,7 @@ processing_type = { }, 'm10_fsk_demod': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 1.0016666 -c | ../fsk_demod --cs16 -b 1 -u 45000 --stats=100 2 96160 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ", + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 1.0016666 -c | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96160 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ", 'decode': "tee test.wav | ../m10mod --json -vvv 2>/dev/null", # Count the number of telemetry lines. "post_process" : "| grep aprsid | wc -l", @@ -154,7 +154,7 @@ processing_type = { #python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| #../dfm09ecc -vv --json --dist --auto - 'demod': '| csdr shift_addition_cc 0.125000 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.5208| ../fsk_demod --cs16 -b 1250 -u 23750 --stats=100 2 50000 2500 - - 2>stats.txt |',#' python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| ', + 'demod': '| csdr shift_addition_cc 0.125000 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.5208| ../fsk_demod --cs16 -b 1250 -u 23750 --stats=5 2 50000 2500 - - 2>stats.txt |',#' python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| ', 'decode': '../dfm09mod -vv --json --dist --auto --bin 2>/dev/null', "post_process" : " | grep frame | wc -l", # ECC #"post_process" : "| grep -o '\[OK\]' | wc -l", # No ECC @@ -164,7 +164,7 @@ processing_type = { # LMS6-400 Decoding 'lms6-400_fsk_demod': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=100 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", # Decode using rs41ecc 'decode': "../lms6Xmod --json 2>/dev/null", @@ -179,7 +179,78 @@ processing_type = { # The recording bandwidth needs to be correspondingly huge, with ~480 kHz sample rate required to capture the signal. # We need to resample up to a multiple of 9616 Hz to be able to get fsk_demod to decode. # fsk_demod does not decode these types reliably at the moment. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 1.00166666 | ../fsk_demod --cs16 -b 5000 -u 230000 --stats=100 2 480800 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null|", + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 1.00166666 | ../fsk_demod --cs16 -b 5000 -u 230000 --stats=5 2 480800 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null|", + + # Decode using rs41ecc + 'decode': "../mk2a_lms1680 -i --json 2>/dev/null", + # Count the number of telemetry lines. + "post_process" : " | grep frame | wc -l", + # No low-SNR samples for this sonde available yet. + 'files' : "./generated/lms6-1680*" + }, + + ## Tests for the updated fsk_demod (2020-06) + 'rs41_fsk_demod_new': { + # Shift up to ~24 khz, and then pass into fsk_demod. + 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 0.500 | ../fsk_demod --cs16 -b 1 -u 24000 --stats=5 2 48000 4800 - - 2>stats.txt |", + + # Decode using rs41ecc + 'decode': "../rs41mod --ecc --ptu --crc --bin --json 2>/dev/null", + # Count the number of telemetry lines. + "post_process" : " | grep frame | wc -l", + 'files' : "./generated/rs41*" + }, + # # RS92 Decoding + 'rs92_fsk_demod_new': { + # Shift up to ~24 khz, and then pass into fsk_demod. + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", + + # Decode using rs41ecc + 'decode': "../rs92mod -vx -v --crc --ecc --vel 2>/dev/null", + # Count the number of telemetry lines. + "post_process" : " | grep M2513116 | wc -l", + 'files' : "./generated/rs92*" + }, + 'm10_fsk_demod_new': { + # Shift up to ~24 khz, and then pass into fsk_demod. + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 1.0016666 -c | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96160 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ", + 'decode': "tee test.wav | ../m10mod --json -vvv 2>/dev/null", + # Count the number of telemetry lines. + "post_process" : "| grep aprsid | wc -l", + 'files' : "./generated/m10*" + }, + 'dfm_fsk_demod_new': { + # cat ./generated/dfm09_96k_float_15.0dB.bin | csdr shift_addition_cc 0.25000 2>/dev/null | csdr convert_f_s16 | + #./tsrc - - 1.041666 | ../fsk_demod --cs16 -b 1 -u 45000 2 100000 2500 - - 2>/dev/null | + #python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| + #../dfm09ecc -vv --json --dist --auto + + 'demod': '| csdr shift_addition_cc 0.125000 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.5208| ../fsk_demod --cs16 -b 1250 -u 23750 --stats=5 2 50000 2500 - - 2>stats.txt |',#' python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| ', + 'decode': '../dfm09mod -vv --json --dist --auto --bin 2>/dev/null', + "post_process" : " | grep frame | wc -l", # ECC + #"post_process" : "| grep -o '\[OK\]' | wc -l", # No ECC + 'files' : "./generated/dfm*.bin" + }, + + # LMS6-400 Decoding + 'lms6-400_fsk_demod_new': { + # Shift up to ~24 khz, and then pass into fsk_demod. + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", + + # Decode using rs41ecc + 'decode': "../lms6Xmod --json 2>/dev/null", + # Count the number of telemetry lines. + "post_process" : "| grep frame | wc -l", + 'files' : "./generated/lms6-400*", + }, + + 'lms6-1680_fsk_demod_new': { + # This is a weird one. + # The baud rate is ~9616 Baud, but the deviation is *huge* (~170 kHz occupied bandwidth). + # The recording bandwidth needs to be correspondingly huge, with ~480 kHz sample rate required to capture the signal. + # We need to resample up to a multiple of 9616 Hz to be able to get fsk_demod to decode. + # fsk_demod does not decode these types reliably at the moment. + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 1.00166666 | ../fsk_demod --cs16 -b 5000 -u 230000 --stats=5 2 480800 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null|", # Decode using rs41ecc 'decode': "../mk2a_lms1680 -i --json 2>/dev/null", @@ -616,7 +687,7 @@ def run_analysis(mode, file_mask=None, shift=0.0, verbose=False, log_output = No _runtime = time.time() - _start - _result = "%s, %s" % (os.path.basename(_file), _output.strip()) + _result = "%s, %s, %.1f" % (os.path.basename(_file), _output.strip(), _runtime) print(_result) if log_output is not None: @@ -633,7 +704,7 @@ def run_analysis(mode, file_mask=None, shift=0.0, verbose=False, log_output = No if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument("-m", "--mode", type=str, default="rs41_csdr", help="Operation mode.") + parser.add_argument("-m", "--mode", type=str, default="rs41_fsk_demod", help="Operation mode.") parser.add_argument("-f", "--files", type=str, default=None, help="Glob-path to files to run over.") parser.add_argument("-v", "--verbose", action='store_true', default=False, help="Show additional debug info.") parser.add_argument("-d", "--dry-run", action='store_true', default=False, help="Show additional debug info.") diff --git a/utils/codec2_fdmdv.h b/utils/codec2_fdmdv.h index 4298fab..2b809d5 100644 --- a/utils/codec2_fdmdv.h +++ b/utils/codec2_fdmdv.h @@ -38,6 +38,9 @@ #ifndef __FDMDV__ #define __FDMDV__ +#include "comp.h" +#include "modem_stats.h" + #ifdef __cplusplus extern "C" { #endif @@ -55,9 +58,6 @@ extern "C" { #define CODEC2_WIN32SUPPORT #endif -#include "comp.h" -#include "modem_stats.h" - #define FDMDV_NC 14 /* default number of data carriers */ #define FDMDV_NC_MAX 20 /* maximum number of data carriers */ #define FDMDV_BITS_PER_FRAME 28 /* 20ms frames, for nominal 1400 bit/s */ diff --git a/utils/comp_prim.h b/utils/comp_prim.h index 4d130bc..d0f070a 100644 --- a/utils/comp_prim.h +++ b/utils/comp_prim.h @@ -86,7 +86,7 @@ inline static COMP cadd(COMP a, COMP b) inline static float cabsolute(COMP a) { - return sqrtf(powf(a.real, 2.0) + powf(a.imag, 2.0)); + return sqrtf((a.real * a.real) + (a.imag * a.imag) ); } /* diff --git a/utils/fsk.c b/utils/fsk.c index 65e8f29..5b405fa 100644 --- a/utils/fsk.c +++ b/utils/fsk.c @@ -1,15 +1,15 @@ /*---------------------------------------------------------------------------*\ FILE........: fsk.c - AUTHOR......: Brady O'Brien + AUTHOR......: Brady O'Brien & David Rowe DATE CREATED: 7 January 2016 - C Implementation of 2/4FSK modulator/demodulator, based on octave/fsk_horus.m + C Implementation of 2/4FSK modulator/demodulator, based on octave/fsk_lib.m \*---------------------------------------------------------------------------*/ /* - Copyright (C) 2016 David Rowe + Copyright (C) 2016-2020 David Rowe All rights reserved. @@ -31,9 +31,6 @@ \*---------------------------------------------------------------------------*/ -/* 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 @@ -95,279 +92,93 @@ static void fsk_generate_hann_table(struct FSK* fsk){ int Ndft = fsk->Ndft; size_t 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; ihann_table[i] = hannc; + fsk->hann_table[i] = 0.5 - 0.5 * cosf(2.0 * M_PI * (float)i / (float) (Ndft-1)); } } #endif - - /*---------------------------------------------------------------------------*\ - FUNCTION....: fsk_create_hbr + FUNCTION....: fsk_create_core AUTHOR......: Brady O'Brien - DATE CREATED: 11 February 2016 + 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. + In this version of the demod the stdanard/hbr modes have been + largely combined at they shared so much common code. The + fsk_create/fsk_create_hbr function interface has been retained to + maximise compatability with existing applications. \*---------------------------------------------------------------------------*/ -struct FSK * fsk_create_hbr(int Fs, int Rs,int P,int M, int tx_f1, int tx_fs) +struct FSK * fsk_create_core(int Fs, int Rs, int M, int P, int Nsym, 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); + assert(Nsym > 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; + fsk = (struct FSK*) malloc(sizeof(struct FSK)); assert(fsk != NULL); + // Need enough bins to with 10% of tone centre + float bin_width_Hz = 0.1*Rs; + float Ndft = (float)Fs/bin_width_Hz; + Ndft = pow(2.0, ceil(log2(Ndft))); /* 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->Nsym = Nsym; + fsk->N = fsk->Ts*fsk->Nsym; + fsk->Ndft = Ndft; + fsk->tc = 0.95*Ndft/Fs; fsk->Nmem = fsk->N+(2*fsk->Ts); fsk->f1_tx = tx_f1; fsk->fs_tx = tx_fs; fsk->nin = fsk->N; + fsk->lock_nin = 0; fsk->mode = M==2 ? MODE_2FSK : MODE_4FSK; fsk->Nbits = M==2 ? fsk->Nsym : fsk->Nsym*2; + fsk->est_min = 0; + fsk->est_max = Fs; + fsk->est_space = 0.75*Rs; - /* 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); - + //printf("C.....: M: %d Fs: %d Rs: %d Ts: %d nsym: %d nbit: %d N: %d Ndft: %d fmin: %d fmax: %d\n", + // M, fsk->Fs, fsk->Rs, fsk->Ts, fsk->Nsym, fsk->Nbits, fsk->N, fsk->Ndft, fsk->est_min, fsk->est_max); /* Set up rx state */ - - for( i=0; iphi_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;isamp_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; - } + fsk->f_dc = (COMP*)malloc(M*fsk->Nmem*sizeof(COMP)); assert(fsk->f_dc != NULL); + for(i=0; iNmem; i++) + fsk->f_dc[i] = comp0(); + + fsk->fft_cfg = kiss_fft_alloc(Ndft,0,NULL,NULL); assert(fsk->fft_cfg != NULL); + fsk->Sf = (float*)malloc(sizeof(float)*fsk->Ndft); assert(fsk->Sf != 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->hann_table = (float*)malloc(sizeof(float)*fsk->Ndft); assert(fsk->hann_table != NULL); fsk_generate_hann_table(fsk); #else fsk->hann_table = NULL; #endif #endif - for(i=0;iNdft/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; if_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; iphi_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;isamp_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;ifft_est[i] = 0; + for(i=0;iSf[i] = 0; fsk->norm_rx_timing = 0; @@ -382,153 +193,240 @@ struct FSK * fsk_create(int Fs, int Rs,int M, int tx_f1, int tx_fs) 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; - } + fsk->stats = (struct MODEM_STATS*)malloc(sizeof(struct MODEM_STATS)); assert(fsk->stats != 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; + 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. + +\*---------------------------------------------------------------------------*/ + +struct FSK * fsk_create(int Fs, int Rs, int M, int tx_f1, int tx_fs) { + return fsk_create_core(Fs, Rs, M, FSK_DEFAULT_P, FSK_DEFAULT_NSYM, tx_f1, tx_fs); +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: fsk_create_hbr + AUTHOR......: Brady O'Brien + DATE CREATED: 11 February 2016 + + Alternate version of create allows user defined decimation P and + Nsym. In the current version of the demod it's simply an alias for + the default core function. + + P is the decimation rate, so the intermal demod processing happens + at Fs/P Hz. Nsym is the number of symbols we average demod + parameters like symbol timing over. + +\*---------------------------------------------------------------------------*/ + +struct FSK * fsk_create_hbr(int Fs, int Rs, int M, int P, int Nsym, int tx_f1, int tx_fs) { + return fsk_create_core(Fs, Rs, M, P, Nsym, tx_f1, tx_fs); +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: fsk_destroy + AUTHOR......: Brady O'Brien + DATE CREATED: 11 February 2016 + + Call this to free all memory and shut down the modem. + +\*---------------------------------------------------------------------------*/ + +void fsk_destroy(struct FSK *fsk){ + free(fsk->f_dc); + free(fsk->fft_cfg); + free(fsk->stats); + free(fsk->hann_table); + free(fsk); +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: fsk_mod + AUTHOR......: Brady O'Brien + DATE CREATED: 11 February 2016 + + FSK modulator function, real valued output samples. + +\*---------------------------------------------------------------------------*/ + +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[M]; /* phase shift per sample */ + COMP dph; /* phase shift of current bit */ + size_t i,j,m,bit_i,sym; + + /* Init the per sample phase shift complex numbers */ + for( m=0; mNsym; 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; jtx_phase_c = tx_phase_c; + +} - /* 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 */ + FUNCTION....: fsk_mod_c + AUTHOR......: Brady O'Brien + DATE CREATED: 11 February 2016 + + FSK modulator function, complex valued output samples. + +\*---------------------------------------------------------------------------*/ + +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[M]; /* phase shift per sample */ + COMP dph; /* phase shift of current bit */ + size_t i,j,bit_i,sym; + int m; - /* TODO: refactor eye tracing code here and in fsk_demod */ + /* Init the per sample phase shift complex numbers */ + for( m=0; mstats->neyesamp = neyesamp; - - int eye_traces = MODEM_STATS_ET_MAX/M; - - fsk->stats->neyetr = fsk->mode*eye_traces; - for(i=0; istats->rx_eye[i*M+m][j] = 0; - } + bit_i = 0; + for( i=0; iNsym; 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; jstats->rx_timing = fsk->stats->snr_est = 0; + + /* Normalize TX phase to prevent drift */ + tx_phase_c = comp_normalize(tx_phase_c); + + /* save TX phase */ + fsk->tx_phase_c = tx_phase_c; } -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;ifft_est[i] = 0; - -} +/*---------------------------------------------------------------------------*\ -/* Set the FSK modem into burst demod mode */ + FUNCTION....: fsk_mod_ext_vco + AUTHOR......: David Rowe + DATE CREATED: February 2018 + + Modulator that assume an external VCO. The output is a voltage + that changes for each symbol. -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; +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; iNsym; 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; jnin = fsk->N; } +/*---------------------------------------------------------------------------*\ + + FUNCTION....: fsk_nin + AUTHOR......: Brady O'Brien + DATE CREATED: 11 February 2016 + + Call me before each call to fsk_demod() to determine how many new + samples you should pass in. the number of samples will vary due to + timing variations. + +\*---------------------------------------------------------------------------*/ + 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. + * Internal function to estimate the frequencies of the FSK tones. * 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: @@ -537,18 +435,17 @@ void fsk_set_est_limits(struct FSK *fsk,int est_min, int est_max){ * 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){ +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; size_t i,j; float hann; float max; - float tc; int imax; kiss_fft_cfg fft_cfg = fsk->fft_cfg; int freqi[M]; - int f_min,f_max,f_zero; + int st,en,f_zero; /* Array to do complex FFT from using kiss_fft */ #ifdef DEMOD_ALLOC_STACK @@ -559,89 +456,67 @@ void fsk_demod_freq_est(struct FSK *fsk, COMP fsk_in[],float *freqs,int M){ kiss_fft_cpx *fftout = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx)*Ndft); #endif - #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 + st = (fsk->est_min*Ndft)/Fs + Ndft/2; if (st < 0) st = 0; + en = (fsk->est_max*Ndft)/Fs + Ndft/2; if (en > Ndft) en = Ndft; + //fprintf(stderr, "min: %d max: %d st: %d en: %d\n", fsk->est_min, fsk->est_max, st, en); - 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= Ndft) ? Ndft : samps; + int numffts = floor((float)nin/(Ndft/2)) - 1; + for(j=0; jhann_table[i]; #else - //hann = 1-cosf((2*M_PI*(float)(i))/((float)fft_samps-1)); - rphi = cmult(dphi,rphi); - hann = .5-rphi.real; + hann = 0.5 - 0.5 * cosf(2.0 * M_PI * (float)i / (float) (fft_samps-1)); #endif - fftin[i].r = hann*fsk_in[i+Ndft*j].real; - fftin[i].i = hann*fsk_in[i+Ndft*j].imag; + fftin[i].r = hann*fsk_in[i+a].real; + fftin[i].i = hann*fsk_in[i+a].imag; } - /* Zero out the remaining slots on spare samples */ - for(; ifft_est[i] = (fsk->fft_est[i]*(1-tc)) + (sqrtf(fftout[i].r)*tc); - fftout[i].i = fsk->fft_est[i]; + float tc = fsk->tc; + for(i=0; iSf[i] = (fsk->Sf[i]*(1-tc)) + (sqrtf(fftout[i].r)*tc); + fftout[i].i = fsk->Sf[i]; } } - modem_probe_samp_f("t_fft_est",fsk->fft_est,Ndft/2); + modem_probe_samp_f("t_Sf",fsk->Sf,Ndft); max = 0; /* Find the M frequency peaks here */ for(i=0; i max){ max = fftout[j].i; imax = j; } } /* Blank out FMax +/-Fspace/2 */ + int f_min, f_max; f_min = imax - f_zero; f_min = f_min < 0 ? 0 : f_min; f_max = imax + f_zero; @@ -650,7 +525,7 @@ void fsk_demod_freq_est(struct FSK *fsk, COMP fsk_in[],float *freqs,int M){ fftout[j].i = 0; /* Stick the freq index on the list */ - freqi[i] = imax; + freqi[i] = imax - Ndft/2; } /* Gnome sort the freq list */ @@ -670,13 +545,52 @@ void fsk_demod_freq_est(struct FSK *fsk, COMP fsk_in[],float *freqs,int M){ for(i=0; ifs_tx*Ndft/Fs)-1; + for(i=bin; i<=bin+2; i++) mask[i] = 1.0; + } + int len_mask = bin+2+1; + + #ifdef MODEMPROBE_ENABLE + modem_probe_samp_f("t_mask",mask,len_mask); + #endif + + /* drag mask over Sf, looking for peak in correlation */ + int b_max = st; float corr_max = 0.0; + float *Sf = fsk->Sf; + for (int b=st; b corr_max) { + corr_max = corr; + b_max = b; + } + } + float foff = (b_max-Ndft/2)*Fs/Ndft; + //fprintf(stderr, "fsk->fs_tx: %d\n",fsk->fs_tx); + for (int m=0; mf2_est[m] = foff + m*fsk->fs_tx; + #ifdef MODEMPROBE_ENABLE + modem_probe_samp_f("t_f2_est",fsk->f2_est,M); + #endif + #ifndef DEMOD_ALLOC_STACK free(fftin); free(fftout); #endif } -void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[]){ +/* core demodulator function */ +void fsk_demod_core(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; @@ -686,170 +600,80 @@ void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[] int P = fsk->P; int Nmem = fsk->Nmem; int M = fsk->mode; - size_t i,j,m,dc_i,cbuf_i; + size_t i,j,m; float ft1; - int nstash = fsk->nstash; - COMP* f_int[M]; /* Filtered and downsampled symbol tones */ COMP t[M]; /* complex number temps */ COMP t_c; /* another complex temp */ - COMP phi_c[M]; + COMP *phi_c = fsk->phi_c; + COMP *f_dc = fsk->f_dc; COMP phi_ft; int nold = Nmem-nin; - COMP dphi[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[M],fc_avg,fc_tx; + 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 */ + #define NMP_NAME 26 + char mp_name_tmp[NMP_NAME+1]; /* Temporary string for modem probe trace names */ #endif - //for(size_t jj = 0; jjphi_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 */ - #ifdef DEMOD_ALLOC_STACK - f_intbuf_m = (COMP*) alloca(sizeof(COMP)*Ts); - #else - f_intbuf_m = (COMP*) malloc(sizeof(COMP)*Ts); + fsk_demod_freq_est(fsk,fsk_in,fsk->f_est,M); + #ifdef MODEMPROBE_ENABLE + modem_probe_samp_f("t_f_est",fsk->f_est,M); #endif - - /* allocate memory for the integrated samples */ - for( m=0; mfreq_est_type) + f_est = fsk->f2_est; + else + f_est = fsk->f_est; + + /* update filter (integrator) memory by shifting in nin samples */ + for(m=0; mf_est[0]<1){ - for( m=0; mf_est[m] = f_est[m]; - } - - /* Initalize downmixers for each symbol tone */ - for( m=0; mf_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; msamp_old[nstash-nold]); - using_old_samps = 1; - - /* Pre-fill integration buffer */ - for(dc_i=0; 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; jphi_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); - + #ifdef MODEMPROBE_ENABLE + for(m=0; mburst_mode){ + /* Unless we're in burst mode or nin locked */ + if(!fsk->burst_mode && !fsk->lock_nin) { if(norm_rx_timing > 0.25) fsk->nin = N+Ts/2; else if(norm_rx_timing < -0.25) @@ -905,7 +729,7 @@ void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[] 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); @@ -1024,8 +848,11 @@ void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[] 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; + fc_avg = fc_tx = 0.0; + for(int m=0; mf1_tx + m*fsk->fs_tx)/M; + } fsk->stats->foff = fc_tx-fc_avg; /* Take a sample for the eye diagrams ---------------------------------- */ @@ -1039,9 +866,6 @@ void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[] 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; @@ -1081,171 +905,134 @@ void fsk2_demod(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[] fsk->stats->nr = 0; fsk->stats->Nc = 0; - for(i=0; istats->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; mtx_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 */ +/* 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; - COMP dosc_f[M]; /* phase shift per sample */ - COMP dph; /* phase shift of current bit */ - size_t i,j,m,bit_i,sym; - - /* Init the per sample phase shift complex numbers */ - for( m=0; mNsym; 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; jtx_phase_c = tx_phase_c; - -} -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[M]; /* phase shift per sample */ - COMP dph; /* phase shift of current bit */ - size_t i,j,bit_i,sym; - int m; + /* 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 */ - /* Init the per sample phase shift complex numbers */ - for( m=0; mNsym; 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; jstats->neyesamp = neyesamp; + + int eye_traces = MODEM_STATS_ET_MAX/M; + + fsk->stats->neyetr = fsk->mode*eye_traces; + for(i=0; istats->rx_eye[i*M+m][j] = 0; + } } } - - /* Normalize TX phase to prevent drift */ - tx_phase_c = comp_normalize(tx_phase_c); - - /* save TX phase */ - fsk->tx_phase_c = tx_phase_c; + + fsk->stats->rx_timing = fsk->stats->snr_est = 0; } -/* Modulator that assume an external VCO. The output is a voltage - that changes for each symbol */ +/* Set the FSK modem into burst demod mode */ -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; iNsym; i++) { - /* generate the symbol number from the bit stream, - e.g. 0,1 for 2FSK, 0,1,2,3 for 4FSK */ +void fsk_enable_burst_mode(struct FSK *fsk){ + fsk->nin = fsk->N; + fsk->burst_mode = 1; +} - 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; jNdft); i++){ + fsk->Sf[i] = 0; } + /* Reset timing diff correction */ + fsk->nin = fsk->N; +} + +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_freq_est_limits(struct FSK *fsk, int est_min, int est_max){ + assert(fsk != NULL); + assert(est_min >= -fsk->Fs/2); + assert(est_max <= fsk->Fs/2); + assert(est_max > est_min); + fsk->est_min = est_min; + fsk->est_max = est_max; } void fsk_stats_normalise_eye(struct FSK *fsk, int normalise_enable) { + assert(fsk != NULL); fsk->normalise_eye = normalise_enable; } +void fsk_set_freq_est_alg(struct FSK *fsk, int est_type) { + assert(fsk != NULL); + fsk->freq_est_type = est_type; +} + diff --git a/utils/fsk.h b/utils/fsk.h index ddce55a..c21ec33 100644 --- a/utils/fsk.h +++ b/utils/fsk.h @@ -28,6 +28,7 @@ #ifndef __C2FSK_H #define __C2FSK_H + #include #include "comp.h" #include "kiss_fftr.h" @@ -40,9 +41,13 @@ #define FSK_SCALE 16383 +/* default internal parameters */ +#define FSK_DEFAULT_P 8 +#define FSK_DEFAULT_NSYM 50 + struct FSK { /* Static parameters set up by fsk_init */ - int Ndft; /* buffer size for freq offset est fft */ + int Ndft; /* freq offset est fft */ int Fs; /* sample freq */ int N; /* processing buffer size */ int Rs; /* symbol rate */ @@ -54,35 +59,35 @@ struct FSK { int f1_tx; /* f1 for modulator */ int fs_tx; /* Space between TX freqs for modulatosr */ int mode; /* 2FSK or 4FSK */ + float tc; /* time constant for smoothing FFTs */ int est_min; /* Minimum frequency for freq. estimator */ int est_max; /* Maximum frequency for freq. estimaotr */ int est_space; /* Minimum frequency spacing for freq. estimator */ - float* hann_table; /* Precomputed or runtime computed hann window table */ + float* hann_table; /* Precomputed or runtime computed hann window table */ /* Parameters used by demod */ - COMP phi_c[MODE_M_MAX]; + float* Sf; /* Average of magnitude spectrum */ + COMP phi_c[MODE_M_MAX]; /* phase of each demod local oscillator */ + COMP *f_dc; /* down converted samples */ kiss_fft_cfg fft_cfg; /* Config for KISS FFT, used in freq est */ float norm_rx_timing; /* Normalized RX timing */ - - COMP* samp_old; /* Tail end of last batch of samples */ - int nstash; /* How many elements are in there */ - - float* fft_est; /* Freq est FFT magnitude */ - - /* Memory used by demod but not important between demod frames */ + /* Parameters used by mod */ COMP tx_phase_c; /* TX phase, but complex */ /* Statistics generated by demod */ - float EbNodB; /* Estimated EbNo in dB */ - float f_est[MODE_M_MAX];/* Estimated frequencies */ - float ppm; /* Estimated PPM clock offset */ + float EbNodB; /* Estimated EbNo in dB */ + float f_est[MODE_M_MAX]; /* Estimated frequencies (peak method) */ + float f2_est[MODE_M_MAX];/* Estimated frequencies (mask method) */ + int freq_est_type; /* which estimator to use */ + float ppm; /* Estimated PPM clock offset */ /* Parameters used by mod/demod and driving code */ int nin; /* Number of samples to feed the next demod cycle */ int burst_mode; /* enables/disables 'burst' mode */ + int lock_nin; /* locks nin during testing */ /* modem statistic struct */ struct MODEM_STATS *stats; @@ -107,17 +112,12 @@ struct FSK * fsk_create(int Fs, int Rs, int M, int tx_f1, int tx_fs); * int tx_f1 - '0' frequency * int tx_fs - frequency spacing */ -struct FSK * fsk_create_hbr(int Fs, int Rs, int P, int M, int tx_f1, int tx_fs); - -/* - * Set a new number of symbols per processing frame - */ -void fsk_set_nsym(struct FSK *fsk,int nsym); +struct FSK * fsk_create_hbr(int Fs, int Rs, int M, int P, int Nsym, int tx_f1, int tx_fs); /* * Set the minimum and maximum frequencies at which the freq. estimator can find tones */ -void fsk_set_est_limits(struct FSK *fsk,int fmin, int fmax); +void fsk_set_freq_est_limits(struct FSK *fsk,int fmin, int fmax); /* * Clear the estimator states @@ -179,7 +179,7 @@ uint32_t fsk_nin(struct FSK *fsk); * * struct FSK *fsk - FSK config/state struct, set up by fsk_create * uint8_t rx_bits[] - Buffer for Nbits unpacked bits to be written - * float fsk_in[] - nin samples of modualted FSK + * float fsk_in[] - nin samples of modulated FSK */ void fsk_demod(struct FSK *fsk, uint8_t rx_bits[],COMP fsk_in[]); @@ -199,6 +199,9 @@ void fsk_stats_normalise_eye(struct FSK *fsk, int normalise_enable); /* Set the FSK modem into burst demod mode */ -void fsk_enable_burst_mode(struct FSK *fsk,int nsyms); +void fsk_enable_burst_mode(struct FSK *fsk); + +/* Set freq est algorithm 0: peak 1:mask */ +void fsk_set_freq_est_alg(struct FSK *fsk, int est_type); #endif diff --git a/utils/fsk_demod.c b/utils/fsk_demod.c index 1702165..9c96b13 100644 --- a/utils/fsk_demod.c +++ b/utils/fsk_demod.c @@ -37,7 +37,6 @@ #include #include - #include "fsk.h" #include "codec2_fdmdv.h" #include "modem_stats.h" @@ -57,7 +56,6 @@ int main(int argc,char *argv[]){ int Fs,Rs,M,P,stats_ctr,stats_loop; float loop_time; int enable_stats = 0; - int hbr = 1; FILE *fin,*fout; uint8_t *bitbuf = NULL; int16_t *rawbuf; @@ -69,86 +67,88 @@ int main(int argc,char *argv[]){ int complex_input = 1, bytes_per_sample = 2; int stats_rate = 8; int testframe_mode = 0; - P = 0; + P = 10; /* default */ M = 0; - int fsk_lower = -1; - int fsk_upper = -1; + int fsk_lower = 0; + int fsk_upper = 0; + int user_fsk_lower = 0; + int user_fsk_upper = 0; + int nsym = FSK_DEFAULT_NSYM; + int mask = 0; + int tx_tone_separation = 100; - /* -m 2/4 --mode 2/4 - FSK mode - * -l --lbr - Low bit rate mode - * -p n --conv n - Downconverted symbol size - * If p is unspecified, it will default to Ts - * -c --cs16 - Complex (signed 16-bit) - * -d --cu8 - Complex (unsigned 8-bit) - * If neither -c or -d are specified, input will be real valued signed 16-bit - * -t --stats - dump demod statistics to stderr - * -s --soft-dec - ouput soft decision (float 32-bit) - */ - /* usage: [-l] [-p P] [-s] [(-c|-d)] [-t] (2|4) SampleRate SymbolRatez InputModemRawFile OutputOneBitPerCharFile */ - - int o = 0; - int opt_idx = 0; - while( o != -1 ){ + int o = 0; + int opt_idx = 0; + while( o != -1 ){ static struct option long_opts[] = { {"help", no_argument, 0, 'h'}, - {"lbr", no_argument, 0, 'l'}, {"conv", required_argument, 0, 'p'}, {"cs16", no_argument, 0, 'c'}, {"cu8", no_argument, 0, 'd'}, - {"fsk_lower", optional_argument, 0, 'b'}, - {"fsk_upper", optional_argument, 0, 'u'}, + {"fsk_lower", required_argument, 0, 'b'}, + {"fsk_upper", required_argument, 0, 'u'}, {"stats", optional_argument, 0, 't'}, {"soft-dec", no_argument, 0, 's'}, {"testframes",no_argument, 0, 'f'}, + {"nsym", required_argument, 0, 'n'}, + {"mask", required_argument, 0, 'm'}, {0, 0, 0, 0} }; - o = getopt_long(argc,argv,"fhlp:cdt::sb:u:",long_opts,&opt_idx); + o = getopt_long(argc,argv,"fhlp:cdt::sb:u:m",long_opts,&opt_idx); switch(o){ - case 'l': - hbr = 0; - break; - case 'c': - complex_input = 2; - bytes_per_sample = 2; - break; - case 'd': - complex_input = 2; - bytes_per_sample = 1; - break; - case 'f': - testframe_mode = 1; - break; - case 't': - enable_stats = 1; - if(optarg != NULL){ - stats_rate = atoi(optarg); - if(stats_rate == 0){ - stats_rate = 8; - } + case 'c': + complex_input = 2; + bytes_per_sample = 2; + break; + case 'd': + complex_input = 2; + bytes_per_sample = 1; + break; + case 'f': + testframe_mode = 1; + break; + case 't': + enable_stats = 1; + if(optarg != NULL){ + stats_rate = atoi(optarg); + if(stats_rate == 0){ + stats_rate = 8; } - break; - case 's': - soft_dec_mode = 1; - break; - case 'p': - P = atoi(optarg); - break; - case 'b': - if (optarg != NULL){ - fsk_lower = atoi(optarg); - } - break; - case 'u': - if (optarg != NULL){ - fsk_upper = atoi(optarg); - } - break; - case 'h': - case '?': - goto helpmsg; - break; + } + break; + case 's': + soft_dec_mode = 1; + break; + case 'p': + P = atoi(optarg); + break; + case 'b': + if (optarg != NULL) { + fsk_lower = atoi(optarg); + user_fsk_lower = 1; + } + break; + case 'u': + if (optarg != NULL){ + fsk_upper = atoi(optarg); + user_fsk_upper = 1; + } + break; + case 'n': + if (optarg != NULL) { + nsym = atoi(optarg); + } + break; + case 'm': + mask = 1; + tx_tone_separation = atoi(optarg); + break; + case 'h': + case '?': + goto helpmsg; + break; } } int dx = optind; @@ -158,23 +158,26 @@ int main(int argc,char *argv[]){ goto helpmsg; } - if( (argc - dx) > 5){ + if( (argc - dx) > 5) { fprintf(stderr, "Too many arguments\n"); - helpmsg: - fprintf(stderr,"usage: %s [-l] [-p P] [-s] [(-c|-d)] [-t [r]] [-f] (2|4) SampleRate SymbolRate InputModemRawFile OutputFile\n",argv[0]); - fprintf(stderr," -lP --conv=P - P specifies the rate at which symbols are down-converted before further processing\n"); - fprintf(stderr," P must be divisible by the symbol size. Smaller P values will result in faster\n"); - fprintf(stderr," processing but lower demodulation preformance. If no P value is specified,\n"); - fprintf(stderr," P will default to it's highes possible value\n"); - fprintf(stderr," -c --cs16 - The raw input file will be in complex signed 16 bit format.\n"); - fprintf(stderr," -d --cu8 - The raw input file will be in complex unsigned 8 bit format.\n"); - fprintf(stderr," If neither -c nor -d are used, the input should be in signed 16 bit format.\n"); - fprintf(stderr," -f --testframes - Testframe mode, prints stats to stderr when a testframe is detected, if -t (JSON) \n"); - fprintf(stderr," is enabled stats will be in JSON format\n"); - fprintf(stderr," -t[r] --stats=[r] - Print out modem statistics to stderr in JSON.\n"); - fprintf(stderr," r, if provided, sets the number of modem frames between statistic printouts.\n"); - fprintf(stderr," -s --soft-dec - The output file will be in a soft-decision format, with one 32-bit float per bit.\n"); - fprintf(stderr," If -s is not used, the output will be in a 1 byte-per-bit format.\n"); + helpmsg: + fprintf(stderr,"usage: %s [options] (2|4) SampleRate SymbolRate InputModemRawFile OutputFile\n",argv[0]); + fprintf(stderr," -c --cs16 The raw input file will be in complex signed 16 bit format.\n"); + fprintf(stderr," -d --cu8 The raw input file will be in complex unsigned 8 bit format.\n"); + fprintf(stderr," If neither -c nor -d are used, the input should be in signed 16 bit format.\n"); + fprintf(stderr," -f --testframes Testframe mode, prints stats to stderr when a testframe is detected, if -t (JSON) \n"); + fprintf(stderr," is enabled stats will be in JSON format\n"); + fprintf(stderr," -t[r] --stats=[r] Print out modem statistics to stderr in JSON.\n"); + fprintf(stderr," r, if provided, sets the number of modem frames between statistic printouts.\n"); + fprintf(stderr," -s --soft-dec The output file will be in a soft-decision format, with one 32-bit float per bit.\n"); + fprintf(stderr," If -s is not used, the output will be in a 1 byte-per-bit format.\n"); + fprintf(stderr," -p P The demod internals operate at a rate of Fs/P, default %d\n", FSK_DEFAULT_P); + fprintf(stderr," P must be divisible by the symbol rate. Smaller P values will result in faster\n"); + fprintf(stderr," processing but lower demodulation performance. Default %d\n", FSK_DEFAULT_P); + fprintf(stderr," --fsk_lower freq lower limit of freq estimator (default 0 for real input, -Fs/2 for complex input)\n"); + fprintf(stderr," --fsk_upper freq upper limit of freq estimator (default Fs/2)\n"); + fprintf(stderr," --nsym Nsym number of symbols used for estimators. Default %d\n", FSK_DEFAULT_NSYM); + fprintf(stderr," --mask TxFreqSpace Use \"mask\" freq estimator (default is \"peak\" estimator)\n"); exit(1); } @@ -183,10 +186,6 @@ int main(int argc,char *argv[]){ Fs = atoi(argv[dx + 1]); Rs = atoi(argv[dx + 2]); - if( P == 0 ){ - P = Fs/Rs; - } - if( (M!=2) && (M!=4) ){ fprintf(stderr,"Mode %d is not valid. Mode must be 2 or 4.\n",M); goto helpmsg; @@ -204,20 +203,26 @@ int main(int argc,char *argv[]){ }else{ fout = fopen(argv[dx + 4],"w"); } - - - /* set up FSK */ - if(!hbr) { - fsk = fsk_create(Fs,Rs,M,1200,400); - } - else { - fsk = fsk_create_hbr(Fs,Rs,P,M,1200,400); - if(fsk_lower> 0 && fsk_upper > fsk_lower){ - fsk_set_est_limits(fsk,fsk_lower,fsk_upper); - fprintf(stderr,"Setting estimator limits to %d to %d Hz.\n",fsk_lower, fsk_upper); - } - } + /* set up FSK */ + #define UNUSED 1000 + fsk = fsk_create_hbr(Fs,Rs,M,P,nsym,UNUSED,tx_tone_separation); + + /* set freq estimator limits */ + if (!user_fsk_lower) { + if (complex_input == 1) + fsk_lower = 0; + else + fsk_lower = -Fs/2; + } + if (!user_fsk_upper) { + fsk_upper = Fs/2; + } + fprintf(stderr,"Setting estimator limits to %d to %d Hz.\n", fsk_lower, fsk_upper); + fsk_set_freq_est_limits(fsk,fsk_lower,fsk_upper); + + fsk_set_freq_est_alg(fsk, mask); + if(fin==NULL || fout==NULL || fsk==NULL){ fprintf(stderr,"Couldn't open files\n"); exit(1); @@ -270,7 +275,7 @@ int main(int argc,char *argv[]){ while( fread(rawbuf,bytes_per_sample*complex_input,fsk_nin(fsk),fin) == fsk_nin(fsk) ){ /* convert input to a buffer of floats. Note scaling isn't really necessary for FSK */ - if (complex_input == 1) { + if (complex_input == 1) { /* S16 real input */ for(i=0;iNbits; j++) { @@ -336,7 +341,7 @@ int main(int argc,char *argv[]){ biterr += errs; if (enable_stats == 0) { fprintf(stderr,"errs: %d FSK BER %f, bits tested %d, bit errors %d\n", - errs, ((float)biterr/(float)bitcnt),bitcnt,biterr); + errs, ((float)biterr/(float)bitcnt),bitcnt,biterr); } } } @@ -352,12 +357,17 @@ int main(int argc,char *argv[]){ time_t seconds = time(NULL); fprintf(stderr,"\"secs\": %ld, \"EbNodB\": %5.1f, \"ppm\": %4d,",seconds, stats.snr_est, (int)fsk->ppm); - fprintf(stderr," \"f1_est\":%.1f, \"f2_est\":%.1f",fsk->f_est[0],fsk->f_est[1]); + float *f_est; + if (fsk->freq_est_type) + f_est = fsk->f2_est; + else + f_est = fsk->f_est; + fprintf(stderr," \"f1_est\":%.1f, \"f2_est\":%.1f",f_est[0],f_est[1]); /* Print 4FSK stats if in 4FSK mode */ if(fsk->mode == 4){ - fprintf(stderr,", \"f3_est\":%.1f, \"f4_est\":%.1f",fsk->f_est[2],fsk->f_est[3]); + fprintf(stderr,", \"f3_est\":%.1f, \"f4_est\":%.1f",f_est[2],f_est[3]); } if (testframe_mode == 0) { @@ -379,7 +389,7 @@ int main(int argc,char *argv[]){ fprintf(stderr,"\"samp_fft\":["); Ndft = fsk->Ndft/2; for(i=0; ifft_est)[i]); + fprintf(stderr,"%f ",(fsk->Sf)[i]); if(i #include #include diff --git a/utils/modem_stats.c b/utils/modem_stats.c index 875fcf1..d212e2b 100644 --- a/utils/modem_stats.c +++ b/utils/modem_stats.c @@ -40,16 +40,19 @@ void modem_stats_open(struct MODEM_STATS *f) /* init the FFT */ +#ifndef __EMBEDDED__ for(i=0; i<2*MODEM_STATS_NSPEC; i++) f->fft_buf[i] = 0.0; f->fft_cfg = kiss_fft_alloc (2*MODEM_STATS_NSPEC, 0, NULL, NULL); assert(f->fft_cfg != NULL); - +#endif } void modem_stats_close(struct MODEM_STATS *f) { +#ifndef __EMBEDDED__ KISS_FFT_FREE(f->fft_cfg); +#endif } /*---------------------------------------------------------------------------*\ @@ -81,6 +84,7 @@ void modem_stats_close(struct MODEM_STATS *f) \*---------------------------------------------------------------------------*/ +#ifndef __EMBEDDED__ void modem_stats_get_rx_spectrum(struct MODEM_STATS *f, float mag_spec_dB[], COMP rx_fdm[], int nin) { int i,j; @@ -116,3 +120,4 @@ void modem_stats_get_rx_spectrum(struct MODEM_STATS *f, float mag_spec_dB[], COM mag_spec_dB[i] -= full_scale_dB; } } +#endif diff --git a/utils/modem_stats.h b/utils/modem_stats.h index 12cf1db..3a8f4a2 100644 --- a/utils/modem_stats.h +++ b/utils/modem_stats.h @@ -25,17 +25,17 @@ along with this program; if not, see . */ -#ifdef __cplusplus - extern "C" { -#endif - #ifndef __MODEM_STATS__ #define __MODEM_STATS__ #include "comp.h" #include "kiss_fft.h" -#define MODEM_STATS_NC_MAX 20 +#ifdef __cplusplus + extern "C" { +#endif + +#define MODEM_STATS_NC_MAX 50 #define MODEM_STATS_NR_MAX 8 #define MODEM_STATS_ET_MAX 8 #define MODEM_STATS_EYE_IND_MAX 160 @@ -46,9 +46,11 @@ struct MODEM_STATS { int Nc; float snr_est; /* estimated SNR of rx signal in dB (3 kHz noise BW) */ +#ifndef __EMBEDDED__ COMP rx_symbols[MODEM_STATS_NR_MAX][MODEM_STATS_NC_MAX+1]; /* latest received symbols, for scatter plot */ - int nr; /* number of rows in rx_symbols */ +#endif + int nr; /* number of rows in rx_symbols */ int sync; /* demod sync state */ float foff; /* estimated freq offset in Hz */ float rx_timing; /* estimated optimum timing offset in samples */ @@ -57,6 +59,7 @@ struct MODEM_STATS { /* eye diagram traces */ /* Eye diagram plot -- first dim is trace number, second is the trace idx */ +#ifndef __EMBEDDED__ float rx_eye[MODEM_STATS_ET_MAX][MODEM_STATS_EYE_IND_MAX]; int neyetr; /* How many eye traces are plotted */ int neyesamp; /* How many samples in the eye diagram */ @@ -64,19 +67,23 @@ struct MODEM_STATS { /* optional for FSK modems - est tone freqs */ float f_est[MODEM_STATS_MAX_F_EST]; +#endif /* Buf for FFT/waterfall */ - float fft_buf[2*MODEM_STATS_NSPEC]; - kiss_fft_cfg fft_cfg; +#ifndef __EMBEDDED__ + float fft_buf[2*MODEM_STATS_NSPEC]; + kiss_fft_cfg fft_cfg; +#endif }; void modem_stats_open(struct MODEM_STATS *f); void modem_stats_close(struct MODEM_STATS *f); void modem_stats_get_rx_spectrum(struct MODEM_STATS *f, float mag_spec_dB[], COMP rx_fdm[], int nin); -#endif - #ifdef __cplusplus } #endif + +#endif + diff --git a/utils/test.c b/utils/test.c new file mode 100644 index 0000000..56c064d --- /dev/null +++ b/utils/test.c @@ -0,0 +1,80 @@ +/* + tsrc.c + David Rowe + Sat Nov 3 2012 + + Unit test for libresample code. + + build: gcc tsrc.c -o tsrc -lm -lsamplerate + + */ + +#include +#include +#include +#include +#include +#include + +#define N 10000 /* processing buffer size */ + +void display_help(void) { + fprintf(stderr, "\nusage: tsrc inputRawFile OutputRawFile OutSampleRatio [-l] [-c]\n"); + fprintf(stderr, "\nUse - for stdin/stdout\n\n"); + fprintf(stderr, "-l fast linear resampler\n"); + fprintf(stderr, "-c complex (two channel) resampling\n\n"); +} + +int main(int argc, char *argv[]) { + FILE *fin, *fout; + short in_short[N], out_short[N]; + float in[N], out[N]; + SRC_DATA data; + int error, nin, nremaining, i, output_rate, symbol_rate, samples_per_symbol; + + if (argc < 3) { + display_help(); + exit(1); + } + + if (strcmp(argv[1], "-") == 0) + fin = stdin; + else + fin = fopen(argv[1], "rb"); + assert(fin != NULL); + + if (strcmp(argv[2], "-") == 0) + fout = stdout; + else + fout = fopen(argv[2], "wb"); + assert(fout != NULL); + + output_rate = atof(argv[3]); + symbol_rate = atof(argv[4]); + samples_per_symbol = output_rate / symbol_rate; + + char in_char; + + while(fread(&in_char, 1, 1, fin) == 1) { + + if(in_char == 0){ + for (i=0; i