$ 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:
% [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
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
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
% 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
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;
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;
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
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;
dual_rx_bits = zeros(1,2*Nc*Nb);
end
+ atimer = 0;
+
% Main loop ----------------------------------------------------
for fr=1:frames
[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];
% 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);
[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];
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]);
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')
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
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;
f->pilot_lut_index = 0;
f->prev_pilot_lut_index = 3*M_FAC;
- for(i=0; i<NRXDEC-1+M_FAC; i++) {
+ for(i=0; i<NRXDECMEM; i++) {
f->rxdec_lpf_mem[i].real = 0.0;
f->rxdec_lpf_mem[i].imag = 0.0;
}
f->foff_phase_rect.real = 1.0;
f->foff_phase_rect.imag = 0.0;
- for(i=0; i<NFILTER+M_FAC; i++) {
+ for(i=0; i<NRX_FDM_MEM; i++) {
f->rx_fdm_mem[i].real = 0.0;
f->rx_fdm_mem[i].imag = 0.0;
}
\*---------------------------------------------------------------------------*/
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<NRXDEC-1+M_FAC-nin; i++)
+ for(i=0; i<NRXDECMEM-nin; i++)
rxdec_lpf_mem[i] = rxdec_lpf_mem[i+nin];
- for(i=0, j=NRXDEC-1+M_FAC-nin; i<nin; i++,j++)
+ for(i=0, j=NRXDECMEM-nin; i<nin; i++,j++)
rxdec_lpf_mem[j] = rx_fdm[i];
+ st = NRXDECMEM - nin - NRXDEC + 1;
for(i=0; i<nin; i++) {
rx_fdm_filter[i].real = 0.0;
for(k=0; k<NRXDEC; k++)
- rx_fdm_filter[i].real += rxdec_lpf_mem[i+k].real * rxdec_coeff[k];
+ rx_fdm_filter[i].real += rxdec_lpf_mem[st+i+k].real * rxdec_coeff[k];
rx_fdm_filter[i].imag = 0.0;
for(k=0; k<NRXDEC; k++)
- rx_fdm_filter[i].imag += rxdec_lpf_mem[i+k].imag * rxdec_coeff[k];
+ rx_fdm_filter[i].imag += rxdec_lpf_mem[st+i+k].imag * rxdec_coeff[k];
}
}
int i,k,c,st,Nval;
float windback_phase, mag;
COMP windback_phase_rect;
- COMP rx_baseband[NFILTER+M_FAC];
+ COMP rx_baseband[NRX_FDM_MEM];
COMP f_rect;
//PROFILE_VAR(windback_start, downconvert_start, filter_start);
/* update memory of rx_fdm */
#if 0
- for(i=0; i<NFILTER+M_FAC-nin; i++)
+ for(i=0; i<NRX_FDM_MEM-nin; i++)
rx_fdm_mem[i] = rx_fdm_mem[i+nin];
for(i=NFILTER+M_FAC-nin, k=0; i<NFILTER+M_FAC; i++, k++)
rx_fdm_mem[i] = rx_fdm[k];
#else
// this gives only 40uS gain on STM32 but now that we have, we keep it
- memmove(&rx_fdm_mem[0],&rx_fdm_mem[nin],(NFILTER+M_FAC-nin)*sizeof(COMP));
- memcpy(&rx_fdm_mem[NFILTER+M_FAC-nin],&rx_fdm[0],nin*sizeof(COMP));
+ memmove(&rx_fdm_mem[0],&rx_fdm_mem[nin],(NRX_FDM_MEM-nin)*sizeof(COMP));
+ memcpy(&rx_fdm_mem[NRX_FDM_MEM-nin],&rx_fdm[0],nin*sizeof(COMP));
#endif
for(c=0; c<Nc+1; c++) {
- /*
- now downconvert using current freq offset to get Nfilter+nin
- baseband samples.
+ /*
+
+ 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.
- Nfilter nin
- |--------------------------|---------|
- |
- phase_rx(c)
+
+ <--------------- Nrx_filt_mem ------->
+ 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;
/* 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 */
for(i=0; i<dec_rate-1; i++)
f_rect = cmult(f_rect,freq[c]);
- for(i=st; i<NFILTER+M_FAC; i+=dec_rate) {
+ for(i=st; i<NRX_FDM_MEM; i+=dec_rate) {
phase_rx[c] = cmult(phase_rx[c], f_rect);
rx_baseband[i] = cmult(rx_fdm_mem[i],cconj(phase_rx[c]));
}
//rx_symbols[c] = rx_filter_mem_timing[c][high_sample];
}
+ /* This value will be +/- half a symbol so will wrap around at +/-
+ M/2 or +/- 80 samples with M=160 */
+
return norm_rx_timing*m;
}
*nin = M_FAC;
- if (fdmdv->rx_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,
#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
/* 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;
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
\*---------------------------------------------------------------------------*/
/* 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);