[ ] test rx level sensitivity, i.e. 0 to 20dB attenuation
[ ] try to run from shell script
[ ] arb bit stream input to Octave mod and demod
+[ ] make fine freq indep of amplitude
+ + use angle rather than imag corrd
[ ] C port and UT framework
- [ ] doumnent how to use
+ [ ] document how to use
+ [ ] demo modem C test program
+ [ ] freq corr in func, state vars in struct
+ [ ] fine freq est in func, statevars
+ [ ] demod in func with all vars
+ [ ] mod in func with all vars
+ [ ] check with ch impairments
+ [ ] test with freq offsets
[ ] document use of fdmdv_ut and fdmdv_demod + PathSim
[ ] block diagram
+ [ ] maybe in ascii art
[ ] blog posts(s)
[ ] Codec 2 web page update
-[ ] demo modem C test program
Tests
% freq offset state machine. Moves between acquire and track states based
% on BPSK pilot sequence. Freq offset estimator occasionally makes mistakes
-% when used continuously. So we use it unit we have acquired the BPSK pilot,
+% when used continuously. So we use it until we have acquired the BPSK pilot,
% then switch to a more robust tracking algorithm. If we lose sync we switch
% back to acquire mode for fast-requisition.
rx_bits_offset = zeros(Nc*Nb*2);
prev_tx_symbols = ones(Nc+1,1);
prev_rx_symbols = ones(Nc+1,1);
-f_err = 0;
+ferr = 0;
foff = 0;
foff_log = [];
tx_baseband_log = [];
else
rx_symbols_log = [rx_symbols_log rx_symbols];
endif
- [rx_bits sync f_err] = qpsk_to_bits(prev_rx_symbols, rx_symbols, modulation);
- foff -= 0.5*f_err;
+ [rx_bits sync ferr] = qpsk_to_bits(prev_rx_symbols, rx_symbols, modulation);
+ foff -= 0.5*ferr;
prev_rx_symbols = rx_symbols;
sync_log = [sync_log sync];
passes = fails = 0;
frames = 25;
prev_tx_symbols = ones(Nc+1,1);
+prev_rx_symbols = ones(Nc+1,1);
% Octave outputs we want to collect for comparison to C version
env_log = [];
rx_timing_log = [];
rx_symbols_log = [];
+rx_bits_log = [];
+ferr_log = [];
+sync_bit_log = [];
for f=1:frames
rx_baseband = fdm_downconvert(rx_fdm, M);
rx_baseband_log = [rx_baseband_log rx_baseband];
+
rx_filt = rx_filter(rx_baseband, M);
rx_filt_log = [rx_filt_log rx_filt];
+
[rx_symbols rx_timing env] = rx_est_timing(rx_filt, rx_baseband, M);
env_log = [env_log env];
+
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');
+ prev_rx_symbols = rx_symbols;
+ rx_bits_log = [rx_bits_log rx_bits];
+ ferr_log = [ferr_log ferr];
+ sync_bit_log = [sync_bit_log sync_bit];
end
% Compare to the output from the C version
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' )
-c=10;
+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(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' )
+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' )
+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])
+
% ---------------------------------------------------------------------------------------
% AUTOMATED CHECKS ------------------------------------------
% ---------------------------------------------------------------------------------------
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(rx_baseband_log, rx_baseband_log_c, 'fdm_downconvert');
-check(rx_filt_log, rx_filt_log_c, 'fdm_downconvert');
+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_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);
return NULL;
f->current_test_bit = 0;
+ for(i=0; i<NTEST_BITS; i++)
+ f->rx_test_bits_mem[i] = 0;
+
f->tx_pilot_bit = 0;
+
for(c=0; c<NC+1; c++) {
f->prev_tx_symbols[c].real = 1.0;
f->prev_tx_symbols[c].imag = 0.0;
+ f->prev_rx_symbols[c].real = 1.0;
+ f->prev_rx_symbols[c].imag = 0.0;
+
for(k=0; k<NFILTER; k++) {
f->tx_filter_memory[c][k].real = 0.0;
f->tx_filter_memory[c][k].imag = 0.0;
generate_pilot_lut(f->pilot_lut, &f->freq[NC]);
- /* Freq Offset estimation */
+ /* freq Offset estimation states */
for(i=0; i<NPILOTBASEBAND; i++) {
f->pilot_baseband1[i].real = f->pilot_baseband2[i].real = 0.0;
for(k=s,j=0; k<s+NFILTER; k++,j++)
rx_symbols[c] = cadd(rx_symbols[c], fcmult(gt_alpha5_root[j], rx_baseband_mem_timing[c][k]));
}
- printf("rx_symbols[0] = %f %f\n", rx_symbols[0].real, rx_symbols[0].imag);
return rx_timing;
}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: qpsk_to_bits()
+ AUTHOR......: David Rowe
+ DATE CREATED: 24/4/2012
+
+ Convert DQPSK symbols back to an array of bits, extracts sync bit
+ from DBPSK pilot, and also uses pilot to estimate fine frequency
+ error.
+
+\*---------------------------------------------------------------------------*/
+
+float qpsk_to_bits(int rx_bits[], int *sync_bit, COMP prev_rx_symbols[], COMP rx_symbols[])
+{
+ int c;
+ COMP phase_difference[NC+1];
+ COMP pi_on_4;
+ COMP d;
+ int msb, lsb;
+ float ferr;
+
+ pi_on_4.real = cos(PI/4.0);
+ pi_on_4.imag = sin(PI/4.0);
+
+ /* Extra 45 degree clockwise lets us use real and imag axis as
+ decision boundaries */
+
+ for(c=0; c<NC; c++)
+ phase_difference[c] = cmult(cmult(rx_symbols[c], cconj(prev_rx_symbols[c])), pi_on_4);
+
+ /* map (Nc,1) DQPSK symbols back into an (1,Nc*Nb) array of bits */
+
+ for (c=0; c<NC; c++) {
+ d = phase_difference[c];
+ if ((d.real >= 0) && (d.imag >= 0)) {
+ msb = 0; lsb = 0;
+ }
+ if ((d.real < 0) && (d.imag >= 0)) {
+ msb = 0; lsb = 1;
+ }
+ if ((d.real < 0) && (d.imag < 0)) {
+ msb = 1; lsb = 0;
+ }
+ if ((d.real >= 0) && (d.imag < 0)) {
+ msb = 1; lsb = 1;
+ }
+ rx_bits[2*c] = msb;
+ rx_bits[2*c+1] = lsb;
+ }
+
+ /* Extract DBPSK encoded Sync bit */
+
+ phase_difference[NC] = cmult(rx_symbols[NC], cconj(prev_rx_symbols[NC]));
+ if (phase_difference[NC].real < 0) {
+ *sync_bit = 0;
+ ferr = phase_difference[NC].imag;
+ }
+ else {
+ *sync_bit = 1;
+ ferr = -phase_difference[NC].imag;
+ }
+
+ return ferr;
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: fdmdv_put_test_bits()
+ AUTHOR......: David Rowe
+ DATE CREATED: 24/4/2012
+
+ Accepts nbits from rx and attempts to sync with test_bits sequence.
+ If sync OK measures bit errors.
+
+\*---------------------------------------------------------------------------*/
+
+void fdmdv_put_test_bits(struct FDMDV *f, int *sync, int *bit_errors, int rx_bits[])
+{
+ int i,j;
+ float ber;
+
+ /* Append to our memory */
+
+ for(i=0,j=FDMDV_BITS_PER_FRAME; i<NTEST_BITS-FDMDV_BITS_PER_FRAME; i++)
+ f->rx_test_bits_mem[i] = f->rx_test_bits_mem[j];
+ for(i=NTEST_BITS-FDMDV_BITS_PER_FRAME,j=0; i<NTEST_BITS; i++)
+ f->rx_test_bits_mem[i] = rx_bits[j];
+
+ /* see how many bit errors we get when checked against test sequence */
+
+ *bit_errors = 0;
+ for(i=0; i<FDMDV_BITS_PER_FRAME; i++)
+ *bit_errors += test_bits[i] ^ f->rx_test_bits_mem[i];
+
+ /* if less than a thresh we are aligned and in sync with test sequence */
+
+ ber = *bit_errors/NTEST_BITS;
+
+ *sync = 0;
+ if (ber < 0.2)
+ *sync = 1;
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: freq_state(()
+ AUTHOR......: David Rowe
+ DATE CREATED: 24/4/2012
+
+ Freq offset state machine. Moves between acquire and track states based
+ on BPSK pilot sequence. Freq offset estimator occasionally makes mistakes
+ when used continuously. So we use it until we have acquired the BPSK pilot,
+ then switch to a more robust tracking algorithm. If we lose sync we switch
+ back to acquire mode for fast-requisition.
+
+\*---------------------------------------------------------------------------*/
+
+int freq_state(int sync_bit, int *state)
+{
+ int next_state, track;
+
+ /* acquire state, look for 6 symbol 010101 sequence from sync bit */
+
+ next_state = *state;
+ switch(*state) {
+ case 0:
+ if (sync_bit == 0)
+ next_state = 1;
+ break;
+ case 1:
+ if (sync_bit == 1)
+ next_state = 2;
+ else
+ next_state = 0;
+ break;
+ case 2:
+ if (sync_bit == 0)
+ next_state = 3;
+ else
+ next_state = 0;
+ break;
+ case 3:
+ if (sync_bit == 1)
+ next_state = 4;
+ else
+ next_state = 0;
+ break;
+ case 4:
+ if (sync_bit == 0)
+ next_state = 5;
+ else
+ next_state = 0;
+ break;
+ case 5:
+ if (sync_bit == 1)
+ next_state = 6;
+ else
+ next_state = 0;
+ break;
+
+ /* states 6 and above are track mode, make sure we keep
+ getting 0101 sync bit sequence */
+
+ case 6:
+ if (sync_bit == 0)
+ next_state = 7;
+ else
+ next_state = 0;
+
+ break;
+ case 7:
+ if (sync_bit == 1)
+ next_state = 6;
+ else
+ next_state = 0;
+ break;
+ }
+
+ *state = next_state;
+ if (*state >= 6)
+ track = 1;
+ else
+ track = 0;
+
+ return track;
+}
void fdmdv_demod(struct FDMDV *fdmdv_state, int rx_bits[], int *sync, float rx_fdm[], int *nin);
void fdmdv_get_test_bits(struct FDMDV *fdmdv_state, int tx_bits[]);
-void fdmdv_put_test_bits(struct FDMDV *fdmdv_state, int rx_bits[]);
+void fdmdv_put_test_bits(struct FDMDV *f, int *sync, int *bit_errors, int rx_bits[]);
float fdmdv_get_demod_stats(struct FDMDV *fdmdv_state, struct FDMDV_STATS *fdmdv_stats);
void fdmdv_get_waterfall_line(struct FDMDV *fdmdv_state, float magnitudes[], int *magnitude_points);
struct FDMDV {
int current_test_bit;
+ int rx_test_bits_mem[NTEST_BITS];
int tx_pilot_bit;
COMP prev_tx_symbols[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];
};
/*---------------------------------------------------------------------------*\
float env[],
COMP rx_baseband_mem_timing[NC+1][NFILTERTIMING],
int nin);
+float qpsk_to_bits(int rx_bits[], int *sync_bit, COMP prev_rx_symbols[], COMP rx_symbols[]);
+int freq_state(int sync_bit, int *state);
#endif
float rx_timing;
float env[NT*P];
COMP rx_symbols[NC+1];
-
+ int rx_bits[FDMDV_BITS_PER_FRAME];
+ float ferr;
+ int sync_bit;
+
int tx_bits_log[FDMDV_BITS_PER_FRAME*FRAMES];
COMP tx_symbols_log[(NC+1)*FRAMES];
COMP tx_baseband_log[(NC+1)][M*FRAMES];
float env_log[NT*P*FRAMES];
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];
+ int sync_bit_log[FRAMES];
+
FILE *fout;
int f,c,i;
/* demodulator ----------------------------------------*/
- /* Freq offset estimation and correction */
+ /* freq offset estimation and correction */
foff = rx_est_freq_offset(fdmdv, rx_fdm, nin);
rx_fdm_fcorr[i].imag = 0.0;
}
+ /* 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));
+
/* save log of outputs ------------------------------------------------------*/
memcpy(&tx_bits_log[FDMDV_BITS_PER_FRAME*f], tx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME);
rx_timing_log[f] = rx_timing;
for(c=0; c<NC+1; c++)
rx_symbols_log[c][f] = rx_symbols[c];
+
+ /* qpsk_to_bits() */
+
+ memcpy(&rx_bits_log[FDMDV_BITS_PER_FRAME*f], rx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME);
+ ferr_log[f] = ferr;
+ sync_bit_log[f] = sync_bit;
}
- /* dump logs to Octave file for evaluation by tfdmdv.m Octave script */
+
+ /* dump logs to Octave file for evaluation by tfdmdv.m Octave script ------------------------*/
fout = fopen("tfdmdv_out.txt","wt");
assert(fout != NULL);
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_int(fout, "sync_bit_log_c", sync_bit_log, 1, FRAMES);
fclose(fout);
codec2_destroy(fdmdv);