From: drowe67 Date: Wed, 2 May 2018 21:23:18 +0000 (+0000) Subject: during FreeDV 700D integration, I discovered FreeDV 1600 was losing sync when there... X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=76ec26a1ce71f12c10b7a4966b3e85b8748cbc80;p=freetel-svn-tracking.git during FreeDV 700D integration, I discovered FreeDV 1600 was losing sync when there we took more/less samples to adjust for sample clock differences. Ressurected the fdmdv modem unitttest used for 1600, tracked down and fixed the bug In Octave and then ported fix to C, tfdmdv passes, and bug is fixed in fdmdv_tx/rx C unit tests for samples rates of 7990/8010 (+/- 1000ppm). git-svn-id: https://svn.code.sf.net/p/freetel/code@3556 01035d8c-6547-0410-b346-abe4f91aad63 --- diff --git a/codec2-dev/README_fdmdv.txt b/codec2-dev/README_fdmdv.txt index eb0c0dff..e70aded1 100644 --- a/codec2-dev/README_fdmdv.txt +++ b/codec2-dev/README_fdmdv.txt @@ -37,7 +37,7 @@ Built as part of codec2-dev, see README for build instructions. $ cd ../octave $ octave - octave:1> fdmdv_demod_c("../src/demod_dump.txt",1400) + octave:1> fdmdv_demod_c("../src/demod_dump.txt",14000) 4. Run Octave simulation of entire modem and AWGN channel: diff --git a/codec2-dev/octave/fdmdv.m b/codec2-dev/octave/fdmdv.m index a398f12c..20290478 100644 --- a/codec2-dev/octave/fdmdv.m +++ b/codec2-dev/octave/fdmdv.m @@ -11,6 +11,7 @@ % [X] refactor with states % [X] remove commented out globals % [X] tfdmdv works +% [X] fdmdv_demod works % [ ] fdmdv_ut works % reqd to make sure we get same random bits at mod and demod @@ -50,7 +51,8 @@ function f = fdmdv_init(Nc=14) f.tx_filter_memory = zeros(Nc+1, Nfilter); f.rx_filter_memory = zeros(Nc+1, Nfilter); - f.rx_fdm_mem = zeros(1,Nfilter+M); + f.Nrx_fdm_mem = Nfilter+M+M/P; + f.rx_fdm_mem = zeros(1,f.Nrx_fdm_mem); f.snr_coeff = 0.9; % SNR est averaging filter coeff @@ -98,7 +100,10 @@ function f = fdmdv_init(Nc=14) 0.0096718 0.00986375 0.00490937 0.000163906 -0.0019897 -0.00204605 ... -0.00125472]; - f.rxdec_lpf_mem = zeros(1,f.Nrxdec-1+M); + % we need room for Nrdec + the max nin, as we may need to filter max_min samples + + f.Nrxdecmem = f.Nrxdec+M+M/P; + f.rxdec_lpf_mem = zeros(1,f.Nrxdecmem); f.Q=M/4; % freq offset estimation @@ -330,6 +335,7 @@ function [rx_filt fdmdv] = rx_filter(fdmdv, rx_baseband, nin) % rx filter each symbol, generate P filtered output samples for each symbol. % Note we keep memory at rate M, it's just the filter output at rate P + assert(mod(M,P)==0); N=M/P; j=1; for i=1:N:nin @@ -343,20 +349,32 @@ function [rx_filt fdmdv] = rx_filter(fdmdv, rx_baseband, nin) endfunction -% LP filter +/- 1000 Hz, allows us to perfrom rx filtering at a lower rate saving CPU +% LP filter +/- 1000 Hz, allows us to perform rx filtering at a lower rate saving CPU function [rx_fdm_filter fdmdv] = rxdec_filter(fdmdv, rx_fdm, nin) M = fdmdv.M; + Nrxdecmem = fdmdv.Nrxdecmem; Nrxdec = fdmdv.Nrxdec; rxdec_coeff = fdmdv.rxdec_coeff; rxdec_lpf_mem = fdmdv.rxdec_lpf_mem; - - rxdec_lpf_mem(1:Nrxdec-1+M-nin) = rxdec_lpf_mem(nin+1:Nrxdec-1+M); - rxdec_lpf_mem(Nrxdec-1+M-nin+1:Nrxdec-1+M) = rx_fdm(1:nin); + % place latest nin samples at end of buffer + + rxdec_lpf_mem(1:Nrxdecmem-nin) = rxdec_lpf_mem(nin+1:Nrxdecmem); + rxdec_lpf_mem(Nrxdecmem-nin+1:Nrxdecmem) = rx_fdm(1:nin); + + % init room for nin output samples + rx_fdm_filter = zeros(1,nin); + + % Move starting point for filter dot product to filter newest samples + % in buffer. This stuff makes my head hurt. + + st = Nrxdecmem - nin - Nrxdec + 1; for i=1:nin - rx_fdm_filter(i) = rxdec_lpf_mem(i:Nrxdec-1+i) * rxdec_coeff'; + a = st+1; b = st+i+Nrxdec-1; + %printf("nin: %d i: %d a: %d b: %d\n", nin, i, a, b); + rx_fdm_filter(i) = rxdec_lpf_mem(st+i:st+i+Nrxdec-1) * rxdec_coeff'; end fdmdv.rxdec_lpf_mem = rxdec_lpf_mem; @@ -372,6 +390,7 @@ function [rx_filt fdmdv] = down_convert_and_rx_filter(fdmdv, rx_fdm, nin, dec_ra M = fdmdv.M; P = fdmdv.P; rx_fdm_mem = fdmdv.rx_fdm_mem; + Nrx_fdm_mem = fdmdv.Nrx_fdm_mem; phase_rx = fdmdv.phase_rx; freq = fdmdv.freq; freq_pol = fdmdv.freq_pol; @@ -379,42 +398,55 @@ function [rx_filt fdmdv] = down_convert_and_rx_filter(fdmdv, rx_fdm, nin, dec_ra gt_alpha5_root = fdmdv.gt_alpha5_root; Q = fdmdv.Q; - % update memory of rx_fdm + % update memory of rx_fdm_mem, newest nin sample ast end of buffer - rx_fdm_mem(1:Nfilter+M-nin) = rx_fdm_mem(nin+1:Nfilter+M); - rx_fdm_mem(Nfilter+M-nin+1:Nfilter+M) = rx_fdm(1:nin); + rx_fdm_mem(1:Nrx_fdm_mem-nin) = rx_fdm_mem(nin+1:Nrx_fdm_mem); + rx_fdm_mem(Nrx_fdm_mem-nin+1:Nrx_fdm_mem) = rx_fdm(1:nin); for c=1:Nc+1 - % now downconvert using current freq offset to get Nfilter+nin - % baseband samples. - % - % Nfilter nin - % |--------------------------|---------| - % | - % phase_rx(c) - % - % This means winding phase(c) back from this point - % to ensure phase continuity - - wind_back_phase = -freq_pol(c)*Nfilter; + #{ + So we have rx_fdm_mem, a baseband array of samples at + rate Fs Hz, including the last nin samples at the end. To + filter each symbol we require the baseband samples for all Nsym + symbols that we filter over. So we need to downconvert the + entire rx_fdm_mem array. To downconvert these we need the LO + phase referenced to the start of the rx_fdm_mem array. + + + <--------------- Nrx_filt_mem -------> + nin + |--------------------------|---------| + 1 | + phase_rx(c) + + This means winding phase(c) back from this point + to ensure phase continuity. + #} + + wind_back_phase = -freq_pol(c)*(Nfilter); phase_rx(c) = phase_rx(c)*exp(j*wind_back_phase); % down convert all samples in buffer - rx_baseband = zeros(1,Nfilter+M); - st = Nfilter+M; % end of buffer + rx_baseband = zeros(1,Nrx_fdm_mem); + + st = Nrx_fdm_mem; % end of buffer st -= nin-1; % first new sample st -= Nfilter; % first sample used in filtering - + + %printf("Nfilter: %d Nrx_fdm_mem: %d dec_rate: %d nin: %d st: %d\n", + % Nfilter, Nrx_fdm_mem, dec_rate, nin, st); + f_rect = freq(c) .^ dec_rate; - for i=st:dec_rate:Nfilter+M + for i=st:dec_rate:Nrx_fdm_mem phase_rx(c) = phase_rx(c) * f_rect; rx_baseband(i) = rx_fdm_mem(i)*phase_rx(c)'; end - % now we can filter this carrier's P symbols. Due to filtering of rx_fdm we can filter at rate at rate M/Q + % now we can filter this carrier's P (+/-1) symbols. Due to + % filtering of rx_fdm we can filter at rate at rate M/Q N=M/P; k = 1; for i=1:N:nin @@ -576,6 +608,9 @@ function [rx_symbols rx_timing_M env fdmdv] = rx_est_timing(fdmdv, rx_filt, nin) rx_symbols = rx_filter_mem_timing(:,low_sample)*(1-fract) + rx_filter_mem_timing(:,high_sample)*fract; % rx_symbols = rx_filter_mem_timing(:,high_sample+1); + % This value will be +/- half a symbol so will wrap around at +/- + % M/2 or +/- 80 samples with M=160 + rx_timing_M = norm_rx_timing*M; fdmdv.rx_filter_mem_timing = rx_filter_mem_timing; diff --git a/codec2-dev/octave/fdmdv_demod.m b/codec2-dev/octave/fdmdv_demod.m index 51d398a7..7f4e4cf6 100644 --- a/codec2-dev/octave/fdmdv_demod.m +++ b/codec2-dev/octave/fdmdv_demod.m @@ -73,6 +73,8 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s dual_rx_bits = zeros(1,2*Nc*Nb); end + atimer = 0; + % Main loop ---------------------------------------------------- for fr=1:frames @@ -108,7 +110,7 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s [foff_coarse S1 S2 f] = rx_est_freq_offset(f, rx_fdm, pilot, prev_pilot, nin, !sync ); if sync == 0 - foff = foff_coarse; + foff = foff_coarse; end foff_coarse_log = [foff_coarse_log foff_coarse]; @@ -121,19 +123,30 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s % baseband processing - [rx_fdm_filter f] = rxdec_filter(f, rx_fdm_fcorr, nin); - [rx_filt f] = down_convert_and_rx_filter(f, rx_fdm_filter, nin, M/Q); + if 0 + % easier to understand, but more memory and CPU hungry filtering and down conversion + + [rx_baseband f] = fdm_downconvert(f, rx_fdm_fcorr, nin); + [rx_filt f] = rx_filter(f, rx_baseband, nin); + else + % more efficient filtering and down conversion + + [rx_fdm_filter f] = rxdec_filter(f, rx_fdm_fcorr, nin); + [rx_filt f] = down_convert_and_rx_filter(f, rx_fdm_filter, nin, M/Q); + end + [rx_symbols rx_timing env f] = rx_est_timing(f, rx_filt, nin); rx_timing_log = [rx_timing_log rx_timing]; - nin = M; - if rx_timing > 2*M/P - nin += M/P; + nin = M; + if rx_timing > M/P + nin += M/P; end - if rx_timing < 0; - nin -= M/P; + if rx_timing < -M/P; + nin -= M/P; end - + %printf("fr: %d rx_timing: %d nin = %d\n", fr, rx_timing, nin); + rx_symbols_log = [rx_symbols_log rx_symbols.*conj(prev_rx_symbols./abs(prev_rx_symbols))*exp(j*pi/4)]; [rx_bits sync_bit f_err pd] = psk_to_bits(f, prev_rx_symbols, rx_symbols, modulation); @@ -183,6 +196,9 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s [test_frame_sync bit_errors error_pattern f] = put_test_bits(f, test_bits, rx_bits); if (test_frame_sync == 1) + if (bit_errors) + printf("fr: %d bit_errors: %d\n", fr, bit_errors); + end total_bit_errors = total_bit_errors + bit_errors; total_bits = total_bits + f.Ntest_bits; bit_errors_log = [bit_errors_log bit_errors/f.Ntest_bits]; @@ -244,32 +260,28 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s xt = (1:frames)/Rs; secs = frames/Rs; - figure(1) - clf; + figure(1); clf; [n m] = size(rx_symbols_log); plot(real(rx_symbols_log(1:Nc+1,15:m)),imag(rx_symbols_log(1:Nc+1,15:m)),'+') axis([-2 2 -2 2]); title('Scatter Diagram'); - figure(2) - clf; - subplot(211) + figure(2); clf; plot(xt, rx_timing_log) title('timing offset (samples)'); - subplot(212) + + figure(3); plot(xt, foff_log, '-;freq offset;') - hold on; - plot(xt, sync_log*75, 'r;course-fine;'); - hold off; + %hold on; + %plot(xt, sync_log*75, 'r;course-fine;'); + %hold off; title('Freq offset (Hz)'); - grid + grid; - figure(3) - clf; + figure(4); clf; plot_specgram(rx_fdm_log, Fs); - figure(4) - clf; + figure(5); clf; subplot(311) stem(xt, sync_log) axis([0 secs 0 1.5]); @@ -282,8 +294,7 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s axis([0 secs 0 1.5]); title('Test Frame Sync') - figure(5) - clf; + figure(6); clf; subplot(211); plot(xt, snr_est_log); title('SNR Estimates') @@ -292,8 +303,7 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s bar(snrdB_pc(1:Nc) - mean(snrdB_pc(1:Nc))) axis([0 Nc+1 -3 3]); - figure(6) - clf; + figure(7); clf; hold on; lep = length(error_pattern_log); if lep != 0 @@ -305,8 +315,7 @@ function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, s axis([1 lep/(Nc*Nb) 0 Nc]) end - figure(7) - clf; + figure(8); clf; subplot(211) [a b] = size(rx_fdm_log); xt1 = (1:b)/Fs; diff --git a/codec2-dev/src/fdmdv.c b/codec2-dev/src/fdmdv.c index ee075016..a81a6bc9 100644 --- a/codec2-dev/src/fdmdv.c +++ b/codec2-dev/src/fdmdv.c @@ -169,7 +169,7 @@ struct FDMDV * fdmdv_create(int Nc) f->pilot_lut_index = 0; f->prev_pilot_lut_index = 3*M_FAC; - for(i=0; irxdec_lpf_mem[i].real = 0.0; f->rxdec_lpf_mem[i].imag = 0.0; } @@ -183,7 +183,7 @@ struct FDMDV * fdmdv_create(int Nc) f->foff_phase_rect.real = 1.0; f->foff_phase_rect.imag = 0.0; - for(i=0; irx_fdm_mem[i].real = 0.0; f->rx_fdm_mem[i].imag = 0.0; } @@ -1006,20 +1006,21 @@ void rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_baseband[NC+1][M_FAC+M_F \*---------------------------------------------------------------------------*/ void rxdec_filter(COMP rx_fdm_filter[], COMP rx_fdm[], COMP rxdec_lpf_mem[], int nin) { - int i,j,k; + int i,j,k,st; - for(i=0; i + nin + |--------------------------|---------| + 1 | + phase_rx(c) + + This means winding phase(c) back from this point + to ensure phase continuity. - This means winding phase(c) back from this point to ensure - phase continuity. - */ + */ //PROFILE_SAMPLE(windback_start); windback_phase = -freq_pol[c]*NFILTER; @@ -1185,7 +1194,7 @@ void down_convert_and_rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_fdm[], /* down convert all samples in buffer */ - st = NFILTER+M_FAC-1; /* end of buffer */ + st = NRX_FDM_MEM-1; /* end of buffer */ st -= nin-1; /* first new sample */ st -= NFILTER; /* first sample used in filtering */ @@ -1195,7 +1204,7 @@ void down_convert_and_rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_fdm[], for(i=0; irx_timing > 2*M_FAC/P) + if (fdmdv->rx_timing > M_FAC/P) *nin += M_FAC/P; - if (fdmdv->rx_timing < 0) + if (fdmdv->rx_timing < -M_FAC/P) *nin -= M_FAC/P; foff_fine = qpsk_to_bits(rx_bits, &sync_bit, fdmdv->Nc, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols, diff --git a/codec2-dev/src/fdmdv_internal.h b/codec2-dev/src/fdmdv_internal.h index 3f830bad..12123296 100644 --- a/codec2-dev/src/fdmdv_internal.h +++ b/codec2-dev/src/fdmdv_internal.h @@ -67,6 +67,9 @@ #define NSYNC_MEM 6 +#define NRX_FDM_MEM (NFILTER+M_FAC+M_FAC/P) /* size of rx filter memory */ +#define NRXDECMEM (NRXDEC+M_FAC+M_FAC/P) /* size of rx decimation filter memory */ + /* averaging filter coeffs */ #define TRACK_COEFF 0.5 @@ -130,8 +133,8 @@ struct FDMDV { /* Demodulator */ - COMP rxdec_lpf_mem[NRXDEC-1+M_FAC]; - COMP rx_fdm_mem[NFILTER+M_FAC]; + COMP rxdec_lpf_mem[NRXDECMEM]; + COMP rx_fdm_mem[NRX_FDM_MEM]; COMP phase_rx[NC+1]; COMP rx_filter_mem_timing[NC+1][NT*P]; float rx_timing; diff --git a/codec2-dev/src/freedv_rx.c b/codec2-dev/src/freedv_rx.c index 785e8e33..2c73dfa2 100644 --- a/codec2-dev/src/freedv_rx.c +++ b/codec2-dev/src/freedv_rx.c @@ -9,8 +9,8 @@ Example usage (all one line): - codec2-dev/build_linux/src$ ./freedv_tx 1600 ../../raw/ve9qrp_10s.raw - | - ./freedv_rx 1600 - - | play -t raw -r 8000 -s -2 - + $ cd codec2-dev/build_linux/src + $ ./freedv_tx 1600 ../../raw/ve9qrp_10s.raw - | ./freedv_rx 1600 - - | aplay -f S16 \*---------------------------------------------------------------------------*/ diff --git a/codec2-dev/unittest/tfdmdv.c b/codec2-dev/unittest/tfdmdv.c index 0cfe8862..ba64bbaa 100644 --- a/codec2-dev/unittest/tfdmdv.c +++ b/codec2-dev/unittest/tfdmdv.c @@ -163,8 +163,7 @@ int main(int argc, char *argv[]) /* baseband processing */ - rxdec_filter(rx_fdm_filter, rx_fdm_fcorr, fdmdv->rxdec_lpf_mem, nin); - + rxdec_filter(rx_fdm_filter, rx_fdm_fcorr, fdmdv->rxdec_lpf_mem, nin); down_convert_and_rx_filter(rx_filt, fdmdv->Nc, rx_fdm_filter, fdmdv->rx_fdm_mem, fdmdv->phase_rx, fdmdv->freq, fdmdv->freq_pol, nin, M_FAC/Q); rx_timing = rx_est_timing(rx_symbols, FDMDV_NC, rx_filt, fdmdv->rx_filter_mem_timing, env, nin, M_FAC);