TODO
+[ ] consider moving this file to root
+ [ ] sep SVN repo, automake etc?
+[ ] list each fdmdv.m script (ut, mod, demod) and Cprogram/src and what it does
+ [ ] example usage
+[ ] repair fdmdv_ut and mod/demod after new statres exposed and var renames
[X] Get file based mod and demod working again
[ ] try interfering sine wave
+ maybe swept
[ ] mod in func with all vars
[ ] check with ch impairments
[ ] test with freq offsets
+ [ ] measure execution speed
[ ] document use of fdmdv_ut and fdmdv_demod + PathSim
[ ] block diagram
[ ] maybe in ascii art
else
rx_symbols_log = [rx_symbols_log rx_symbols];
endif
- [rx_bits sync ferr] = qpsk_to_bits(prev_rx_symbols, rx_symbols, modulation);
+ [rx_bits sync foff_fine] = qpsk_to_bits(prev_rx_symbols, rx_symbols, modulation);
foff -= 0.5*ferr;
prev_rx_symbols = rx_symbols;
sync_log = [sync_log sync];
frames = 25;
prev_tx_symbols = ones(Nc+1,1);
prev_rx_symbols = ones(Nc+1,1);
+foff_phase_rect = 1;
+track = 0;
+fest_state = 0;
% Octave outputs we want to collect for comparison to C version
pilot_lpf2_log = [];
S1_log = [];
S2_log = [];
+foff_coarse_log = [];
+foff_fine_log = [];
foff_log = [];
rx_baseband_log = [];
rx_filt_log = [];
rx_timing_log = [];
rx_symbols_log = [];
rx_bits_log = [];
-ferr_log = [];
sync_bit_log = [];
+track_log = [];
for f=1:frames
[pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, M);
- [foff S1 S2] = rx_est_freq_offset(rx_fdm, pilot, prev_pilot, M);
+ [foff_coarse S1 S2] = rx_est_freq_offset(rx_fdm, pilot, prev_pilot, M);
+ if track == 0
+ foff = foff_coarse;
+ end
foff_log = [foff_log foff];
+ foff_coarse_log = [foff_coarse_log foff_coarse];
pilot_baseband1_log = [pilot_baseband1_log pilot_baseband1];
pilot_baseband2_log = [pilot_baseband2_log pilot_baseband2];
S1_log = [S1_log S1];
S2_log = [S2_log S2];
- rx_baseband = fdm_downconvert(rx_fdm, M);
+ foff_rect = exp(j*2*pi*foff/Fs);
+
+ for i=1:M
+ foff_phase_rect *= foff_rect';
+ rx_fdm_fcorr(i) = rx_fdm(i)*foff_phase_rect;
+ end
+
+ rx_baseband = fdm_downconvert(rx_fdm_fcorr, M);
rx_baseband_log = [rx_baseband_log rx_baseband];
rx_filt = rx_filter(rx_baseband, M);
rx_timing_log = [rx_timing_log rx_timing];
rx_symbols_log = [rx_symbols_log rx_symbols];
- [rx_bits sync_bit ferr] = qpsk_to_bits(prev_rx_symbols, rx_symbols, 'dqpsk');
+ [rx_bits sync_bit foff_fine] = qpsk_to_bits(prev_rx_symbols, rx_symbols, 'dqpsk');
prev_rx_symbols = rx_symbols;
rx_bits_log = [rx_bits_log rx_bits];
- ferr_log = [ferr_log ferr];
+ foff_fine_log = [foff_fine_log foff_fine];
sync_bit_log = [sync_bit_log sync_bit];
+
+ % freq est state machine
+
+ [track fest_state] = freq_state(sync_bit, fest_state);
+ track_log = [track_log track];
end
% Compare to the output from the C version
plot_sig_and_error(8, 211, real(S2_log), real(S2_log - S2_log_c), 'S2 real' )
plot_sig_and_error(8, 212, imag(S2_log), imag(S2_log - S2_log_c), 'S2 imag' )
-plot_sig_and_error(9, 211, real(foff_log), real(foff_log - foff_log_c), 'Freq Offset' )
-plot_sig_and_error(9, 212, rx_timing_log, rx_timing_log - rx_timing_log_c, 'Rx Timing' )
+plot_sig_and_error(9, 211, foff_coarse_log, foff_coarse_log - foff_coarse_log_c, 'Coarse Freq Offset' )
+plot_sig_and_error(9, 212, foff_fine_log, foff_fine_log - foff_fine_log_c, 'Fine Freq Offset' )
+
+plot_sig_and_error(10, 211, foff_log, foff_log - foff_log_c, 'Freq Offset' )
+plot_sig_and_error(10, 212, track_log, track_log - track_log_c, 'Freq Track' )
c=15;
-plot_sig_and_error(10, 211, real(rx_baseband_log(c,:)), real(rx_baseband_log(c,:) - rx_baseband_log_c(c,:)), 'Rx baseband real' )
-plot_sig_and_error(10, 212, imag(rx_baseband_log(c,:)), imag(rx_baseband_log(c,:) - rx_baseband_log_c(c,:)), 'Rx baseband imag' )
+plot_sig_and_error(11, 211, real(rx_baseband_log(c,:)), real(rx_baseband_log(c,:) - rx_baseband_log_c(c,:)), 'Rx baseband real' )
+plot_sig_and_error(11, 212, imag(rx_baseband_log(c,:)), imag(rx_baseband_log(c,:) - rx_baseband_log_c(c,:)), 'Rx baseband imag' )
-plot_sig_and_error(11, 211, real(rx_filt_log(c,:)), real(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'Rx filt real' )
-plot_sig_and_error(11, 212, imag(rx_filt_log(c,:)), imag(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'Rx filt imag' )
+plot_sig_and_error(12, 211, real(rx_filt_log(c,:)), real(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'Rx filt real' )
+plot_sig_and_error(12, 212, imag(rx_filt_log(c,:)), imag(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'Rx filt imag' )
-plot_sig_and_error(12, 211, env_log, env_log - env_log_c, 'env' )
-plot_sig_and_error(12, 212, real(rx_symbols_log(c,:)), real(rx_symbols_log(c,:) - rx_symbols_log_c(c,:)), 'rx symbols' )
+plot_sig_and_error(13, 211, env_log, env_log - env_log_c, 'env' )
+plot_sig_and_error(13, 212, real(rx_symbols_log(c,:)), real(rx_symbols_log(c,:) - rx_symbols_log_c(c,:)), 'rx symbols' )
st=10*28;
en = 12*28;
-stem_sig_and_error(13, 211, rx_bits_log_c(st:en), rx_bits_log(st:en) - rx_bits_log_c(st:en), 'RX bits', [1 en-st -1.5 1.5])
-
-plot_sig_and_error(14, 211, ferr_log, ferr_log - ferr_log_c, 'Fine freq error' )
+plot_sig_and_error(14, 211, rx_timing_log, rx_timing_log - rx_timing_log_c, 'Rx Timing' )
stem_sig_and_error(14, 212, sync_bit_log_c, sync_bit_log - sync_bit_log_c, 'Sync bit', [1 n -1.5 1.5])
+stem_sig_and_error(15, 211, rx_bits_log_c(st:en), rx_bits_log(st:en) - rx_bits_log_c(st:en), 'RX bits', [1 en-st -1.5 1.5])
+
% ---------------------------------------------------------------------------------------
% AUTOMATED CHECKS ------------------------------------------
% ---------------------------------------------------------------------------------------
printf(".");
end
printf(": ");
-
- if sum(a - b)/n < 1E-3
+
+ if abs(sum(a - b))/n < 1E-3
printf("OK\n");
passes++;
else
end
endfunction
-check(tx_bits_log, tx_bits_log_c, 'fdmdv_get_test_bits');
-check(tx_symbols_log, tx_symbols_log_c, 'bits_to_dqpsk_symbols');
-check(tx_baseband_log, tx_baseband_log_c, 'tx_filter');
+check(tx_bits_log, tx_bits_log_c, 'tx_bits');
+check(tx_symbols_log, tx_symbols_log_c, 'tx_symbols');
+check(tx_baseband_log, tx_baseband_log_c, 'tx_baseband');
check(tx_fdm_log, tx_fdm_log_c, 'tx_fdm');
check(pilot_lut, pilot_lut_c, 'pilot_lut');
check(pilot_baseband1_log, pilot_baseband1_log_c, 'pilot lpf1');
check(pilot_baseband2_log, pilot_baseband2_log_c, 'pilot lpf2');
check(S1_log, S1_log_c, 'S1');
check(S2_log, S2_log_c, 'S2');
-check(foff_log, foff_log_c, 'rx_est_freq_offset');
+check(foff_coarse_log, foff_coarse_log_c, 'foff_coarse');
+check(foff_fine_log, foff_fine_log_c, 'foff_fine');
+check(foff_log, foff_log_c, 'foff');
check(rx_baseband_log, rx_baseband_log_c, 'rx baseband');
check(rx_filt_log, rx_filt_log_c, 'rx filt');
check(env_log, env_log_c, 'env');
-check(rx_timing_log, rx_timing_log_c, 'rx_est_timing');
+check(rx_timing_log, rx_timing_log_c, 'rx_timing');
check(rx_symbols_log, rx_symbols_log_c, 'rx_symbols');
check(rx_bits_log, rx_bits_log_c, 'rx bits');
-check(ferr_log, ferr_log_c, 'fine freq error');
check(sync_bit_log, sync_bit_log_c, 'sync bit');
printf("\npasses: %d fails: %d\n", passes, fails);
f->pilot_lpf1[i].imag = f->pilot_lpf2[i].imag = 0.0;
}
+ f->foff_rect.real = 1.0;
+ f->foff_rect.imag = 0.0;
+ f->foff_phase_rect.real = 1.0;
+ f->foff_phase_rect.imag = 0.0;
+
+ f->fest_state = 0;
+ f->track = 0;
+
return f;
}
}
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: fdmdv_mod()
+ AUTHOR......: David Rowe
+ DATE CREATED: 26/4/2012
+
+ FDMDV modulator, take a frame of FDMDV_BITS_PER_FRAME bits and
+ generates a frame of FDMDV_SAMPLES_PER_FRAME modulated symbols.
+ Sync bit is returned to aid alignment of your next frame.
+
+\*---------------------------------------------------------------------------*/
+
+void fdmdv_mod(struct FDMDV *fdmdv, COMP tx_fdm[], int tx_bits[], int *sync_bit)
+{
+ COMP tx_symbols[NC+1];
+ COMP tx_baseband[NC+1][M];
+
+ bits_to_dqpsk_symbols(tx_symbols, fdmdv->prev_tx_symbols, tx_bits, &fdmdv->tx_pilot_bit);
+ memcpy(fdmdv->prev_tx_symbols, tx_symbols, sizeof(COMP)*(NC+1));
+ tx_filter(tx_baseband, tx_symbols, fdmdv->tx_filter_memory);
+ fdm_upconvert(tx_fdm, tx_baseband, fdmdv->phase_tx, fdmdv->freq);
+
+ *sync_bit = fdmdv->tx_pilot_bit;
+}
+
/*---------------------------------------------------------------------------*\
FUNCTION....: generate_pilot_fdm()
return foff;
}
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: freq_shift()
+ AUTHOR......: David Rowe
+ DATE CREATED: 26/4/2012
+
+ Frequency shift modem signal.
+
+\*---------------------------------------------------------------------------*/
+
+void freq_shift(COMP rx_fdm_fcorr[], float rx_fdm[], float foff, COMP *foff_rect, COMP *foff_phase_rect, int nin)
+{
+ int i;
+
+ foff_rect->real = cos(2.0*PI*foff/FS);
+ foff_rect->imag = sin(2.0*PI*foff/FS);
+ for(i=0; i<nin; i++) {
+ *foff_phase_rect = cmult(*foff_phase_rect, cconj(*foff_rect));
+ rx_fdm_fcorr[i] = fcmult(rx_fdm[i], *foff_phase_rect);
+ }
+
+}
+
/*---------------------------------------------------------------------------*\
FUNCTION....: fdm_downconvert()
return track;
}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: fdmdv_demod()
+ AUTHOR......: David Rowe
+ DATE CREATED: 26/4/2012
+
+ FDMDV demodulator, take an array of FDMDV_SAMPLES_PER_FRAME
+ modulated symbols, returns an array of FDMDV_BITS_PER_FRAME bits,
+ plus the sync bit.
+
+ The number of input samples nin will normally be M ==
+ FDMDV_SAMPLES_PER_FRAME. However to adjust for differences in
+ transmit and receive sample clocks nin will occasionally be M-M/P,
+ or M+M/P.
+
+\*---------------------------------------------------------------------------*/
+
+void fdmdv_demod(struct FDMDV *fdmdv, int rx_bits[], int *sync_bit, float rx_fdm[], int *nin)
+{
+ float foff;
+ COMP rx_fdm_fcorr[M+M/P];
+ COMP rx_baseband[NC+1][M+M/P];
+ COMP rx_filt[NC+1][P+1];
+ float rx_timing;
+ float env[NT*P];
+ COMP rx_symbols[NC+1];
+ float ferr;
+
+ /* freq offset estimation and correction */
+
+ foff = rx_est_freq_offset(fdmdv, rx_fdm, *nin);
+ freq_shift(rx_fdm_fcorr, rx_fdm, foff, &fdmdv->foff_rect, &fdmdv->foff_phase_rect, *nin);
+
+ /* baseband processing */
+
+ fdm_downconvert(rx_baseband, rx_fdm_fcorr, fdmdv->phase_rx, fdmdv->freq, *nin);
+ rx_filter(rx_filt, rx_baseband, fdmdv->rx_filter_memory, *nin);
+ rx_timing = rx_est_timing(rx_symbols, rx_filt, rx_baseband, fdmdv->rx_filter_mem_timing, env, fdmdv->rx_baseband_mem_timing, *nin);
+ ferr = qpsk_to_bits(rx_bits, sync_bit, fdmdv->prev_rx_symbols, rx_symbols);
+ memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(NC+1));
+}
+
struct FDMDV *fdmdv_create(void);
void fdmdv_destroy(struct FDMDV *fdmdv_state);
-void fdmdv_mod(struct FDMDV *fdmdv_state, COMP tx_fdm[], int tx_bits[], int *sync);
-void fdmdv_demod(struct FDMDV *fdmdv_state, int rx_bits[], int *sync, float rx_fdm[], int *nin);
+void fdmdv_mod(struct FDMDV *fdmdv_state, COMP tx_fdm[], int tx_bits[], int *sync_bit);
+void fdmdv_demod(struct FDMDV *fdmdv_state, int rx_bits[], int *sync_bit, float rx_fdm[], int *nin);
void fdmdv_get_test_bits(struct FDMDV *fdmdv_state, int tx_bits[]);
void fdmdv_put_test_bits(struct FDMDV *f, int *sync, int *bit_errors, int rx_bits[]);
COMP S1[MPILOTFFT];
COMP S2[MPILOTFFT];
+ COMP foff_rect;
+ COMP foff_phase_rect;
+
COMP phase_rx[NC+1];
COMP rx_filter_memory[NC+1][NFILTER];
COMP rx_filter_mem_timing[NC+1][NT*P];
COMP rx_baseband_mem_timing[NC+1][NFILTERTIMING];
COMP prev_rx_symbols[NC+1];
+
+ int fest_state;
+ int track;
};
/*---------------------------------------------------------------------------*\
void generate_pilot_lut(COMP pilot_lut[], COMP *pilot_freq);
float rx_est_freq_offset(struct FDMDV *f, float rx_fdm[], int nin);
void lpf_peak_pick(float *foff, float *max, COMP pilot_baseband[], COMP pilot_lpf[], COMP S[], int nin);
+void freq_shift(COMP rx_fdm_fcorr[], float rx_fdm[], float foff, COMP *foff_rect, COMP *foff_phase_rect, int nin);
void fdm_downconvert(COMP rx_baseband[NC+1][M+M/P], COMP rx_fdm[], COMP phase_rx[], COMP freq[], int nin);
void rx_filter(COMP rx_filt[NC+1][P+1], COMP rx_baseband[NC+1][M+M/P], COMP rx_filter_memory[NC+1][NFILTER], int nin);
float rx_est_timing(COMP rx_symbols[],
COMP tx_baseband[NC+1][M];
COMP tx_fdm[M];
float rx_fdm[M+M/P];
+ float foff_coarse;
float foff;
- COMP foff_rect;
- COMP foff_phase_rect;
int nin;
COMP rx_fdm_fcorr[M+M/P];
COMP rx_baseband[NC+1][M+M/P];
float env[NT*P];
COMP rx_symbols[NC+1];
int rx_bits[FDMDV_BITS_PER_FRAME];
- float ferr;
+ float foff_fine;
int sync_bit;
-
+ int fest_state;
+
int tx_bits_log[FDMDV_BITS_PER_FRAME*FRAMES];
COMP tx_symbols_log[(NC+1)*FRAMES];
COMP tx_baseband_log[(NC+1)][M*FRAMES];
COMP pilot_lpf2_log[NPILOTLPF*FRAMES];
COMP S1_log[MPILOTFFT*FRAMES];
COMP S2_log[MPILOTFFT*FRAMES];
+ float foff_coarse_log[FRAMES];
float foff_log[FRAMES];
COMP rx_baseband_log[(NC+1)][(M+M/P)*FRAMES];
int rx_baseband_log_col_index;
float rx_timing_log[FRAMES];
COMP rx_symbols_log[NC+1][FRAMES];
int rx_bits_log[FDMDV_BITS_PER_FRAME*FRAMES];
- float ferr_log[FRAMES];
+ float foff_fine_log[FRAMES];
int sync_bit_log[FRAMES];
+ int track_log[FRAMES];
FILE *fout;
int f,c,i;
fdmdv = fdmdv_create();
- foff_phase_rect.real = 0.0; foff_phase_rect.imag = 0.0;
rx_baseband_log_col_index = 0;
rx_filt_log_col_index = 0;
/* freq offset estimation and correction */
- foff = rx_est_freq_offset(fdmdv, rx_fdm, nin);
+ foff_coarse = rx_est_freq_offset(fdmdv, rx_fdm, nin);
+ if (fdmdv->track == 0)
+ foff = foff_coarse;
freq_shift(rx_fdm_fcorr, rx_fdm, foff, &fdmdv->foff_rect, &fdmdv->foff_phase_rect, nin);
/* baseband processing */
fdm_downconvert(rx_baseband, rx_fdm_fcorr, fdmdv->phase_rx, fdmdv->freq, nin);
rx_filter(rx_filt, rx_baseband, fdmdv->rx_filter_memory, nin);
rx_timing = rx_est_timing(rx_symbols, rx_filt, rx_baseband, fdmdv->rx_filter_mem_timing, env, fdmdv->rx_baseband_mem_timing, nin);
- ferr = qpsk_to_bits(rx_bits, &sync_bit, fdmdv->prev_rx_symbols, rx_symbols);
+ foff_fine = qpsk_to_bits(rx_bits, &sync_bit, fdmdv->prev_rx_symbols, rx_symbols);
memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(NC+1));
-
+ fdmdv->track = freq_state(sync_bit, &fdmdv->fest_state);
+
/* --------------------------------------------------------*\
Log each vector
\*---------------------------------------------------------*/
memcpy(&pilot_lpf2_log[f*NPILOTLPF], fdmdv->pilot_lpf2, sizeof(COMP)*NPILOTLPF);
memcpy(&S1_log[f*MPILOTFFT], fdmdv->S1, sizeof(COMP)*MPILOTFFT);
memcpy(&S2_log[f*MPILOTFFT], fdmdv->S2, sizeof(COMP)*MPILOTFFT);
+ foff_coarse_log[f] = foff_coarse;
foff_log[f] = foff;
/* rx down conversion */
/* qpsk_to_bits() */
memcpy(&rx_bits_log[FDMDV_BITS_PER_FRAME*f], rx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME);
- ferr_log[f] = ferr;
+ foff_fine_log[f] = foff_fine;
sync_bit_log[f] = sync_bit;
+
+ track_log[f] = fdmdv->track;
}
octave_save_complex(fout, "S1_log_c", S1_log, 1, MPILOTFFT*FRAMES, MPILOTFFT*FRAMES);
octave_save_complex(fout, "S2_log_c", S2_log, 1, MPILOTFFT*FRAMES, MPILOTFFT*FRAMES);
octave_save_float(fout, "foff_log_c", foff_log, 1, FRAMES);
+ octave_save_float(fout, "foff_coarse_log_c", foff_coarse_log, 1, FRAMES);
octave_save_complex(fout, "rx_baseband_log_c", (COMP*)rx_baseband_log, (NC+1), rx_baseband_log_col_index, (M+M/P)*FRAMES);
octave_save_complex(fout, "rx_filt_log_c", (COMP*)rx_filt_log, (NC+1), rx_filt_log_col_index, (P+1)*FRAMES);
octave_save_float(fout, "env_log_c", env_log, 1, NT*P*FRAMES);
octave_save_float(fout, "rx_timing_log_c", rx_timing_log, 1, FRAMES);
octave_save_complex(fout, "rx_symbols_log_c", (COMP*)rx_symbols_log, (NC+1), FRAMES, FRAMES);
octave_save_int(fout, "rx_bits_log_c", rx_bits_log, 1, FDMDV_BITS_PER_FRAME*FRAMES);
- octave_save_float(fout, "ferr_log_c", ferr_log, 1, FRAMES);
+ octave_save_float(fout, "foff_fine_log_c", foff_fine_log, 1, FRAMES);
octave_save_int(fout, "sync_bit_log_c", sync_bit_log, 1, FRAMES);
+ octave_save_int(fout, "track_log_c", track_log, 1, FRAMES);
fclose(fout);
codec2_destroy(fdmdv);