$ ./fdmdv_get_test_bits - 14000 | ./fdmdv_mod - - | ./fdmdv_demod - - demod_dump.txt | ./fdmdv_put_test_bits -
[ ] PAPR idea
- + automatically weak phases to reduce PAPR, e.g. slow variations in freq...
+ + automatically tweak phases to reduce PAPR, e.g. slow variations in freq...
+[ ] implement SNR and ppm measurements
+[ ] add help to each octave script & C program
Tests
[ ] mod_dqpsk_good_4dB_8008hz.raw
[ ] mod_dqpsk_moderate_4dB_8008hz.raw
[ ] mod_dqpsk_moderate_4dB_7992hz.raw
+[ ] time ....
% obtain nin samples of the test input signal
for i=1:nin
- rx_fdm(i) = fread(fin, 1, "short")/gain;
+ rx_fdm(i) = fread(fin, 1, "short")/gain;
end
-
+
rx_fdm_log = [rx_fdm_log rx_fdm(1:nin)];
% frequency offset estimation and correction
[pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, nin);
[foff_coarse S1 S2] = rx_est_freq_offset(rx_fdm, pilot, prev_pilot, nin);
+
if track == 0
foff = foff_coarse;
end
rx_filt = rx_filter(rx_baseband, nin);
[rx_symbols rx_timing] = rx_est_timing(rx_filt, rx_baseband, nin);
+
rx_timing_log = [rx_timing_log rx_timing];
nin = M;
if rx_timing > 2*M/P
foff_phase_rect = 1;
coarse_fine = 0;
fest_state = 0;
+channel = [];
+channel_count = 0;
+next_nin = M;
% Octave outputs we want to collect for comparison to C version
rx_bits_log = [];
sync_bit_log = [];
coarse_fine_log = [];
+nin_log = [];
for f=1:frames
tx_fdm = fdm_upconvert(tx_baseband);
tx_fdm_log = [tx_fdm_log tx_fdm];
- rx_fdm = real(tx_fdm);
+ % channel
+
+ nin = next_nin;
+ %nin = 120;
+ %nin = M;
+ %if (f == 3)
+ % nin = 120;
+ %elseif (f == 4)
+ % nin = 200;
+ %else
+ % nin = M;
+ %end
+ channel = [channel real(tx_fdm)];
+ channel_count += M;
+ rx_fdm = channel(1:nin);
+ channel = channel(nin+1:channel_count);
+ channel_count -= nin;
% demodulator
- [pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, M);
+ [pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, nin);
- [foff_coarse 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, nin);
if coarse_fine == 0
foff = foff_coarse;
end
foff_rect = exp(j*2*pi*foff/Fs);
- for i=1:M
+ for i=1:nin
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 = fdm_downconvert(rx_fdm_fcorr, nin);
rx_baseband_log = [rx_baseband_log rx_baseband];
- rx_filt = rx_filter(rx_baseband, M);
+ rx_filt = rx_filter(rx_baseband, nin);
rx_filt_log = [rx_filt_log rx_filt];
- [rx_symbols rx_timing env] = rx_est_timing(rx_filt, rx_baseband, M);
+ [rx_symbols rx_timing env] = rx_est_timing(rx_filt, rx_baseband, nin);
env_log = [env_log env];
rx_timing_log = [rx_timing_log rx_timing];
rx_symbols_log = [rx_symbols_log rx_symbols];
+ next_nin = M;
+ if rx_timing > 2*M/P
+ next_nin += M/P;
+ end
+ if rx_timing < 0;
+ next_nin -= M/P;
+ end
+ nin_log = [nin_log nin];
+
[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];
% rx_est_freq_offset()
-plot_sig_and_error(5, 211, real(pilot_baseband1_log), real(pilot_baseband1_log - pilot_baseband1_log_c), 'pilot baseband1 real' )
-plot_sig_and_error(5, 212, real(pilot_baseband2_log), real(pilot_baseband2_log - pilot_baseband2_log_c), 'pilot baseband2 real' )
+st=1; en = 3*Npilotbaseband;
+plot_sig_and_error(5, 211, real(pilot_baseband1_log(st:en)), real(pilot_baseband1_log(st:en) - pilot_baseband1_log_c(st:en)), 'pilot baseband1 real' )
+plot_sig_and_error(5, 212, real(pilot_baseband2_log(st:en)), real(pilot_baseband2_log(st:en) - pilot_baseband2_log_c(st:en)), 'pilot baseband2 real' )
-plot_sig_and_error(6, 211, real(pilot_lpf1_log), real(pilot_lpf1_log - pilot_lpf1_log_c), 'pilot lpf1 real' )
-plot_sig_and_error(6, 212, real(pilot_lpf2_log), real(pilot_lpf2_log - pilot_lpf2_log_c), 'pilot lpf2 real' )
+st=1; en = 3*Npilotlpf;
+plot_sig_and_error(6, 211, real(pilot_lpf1_log(st:en)), real(pilot_lpf1_log(st:en) - pilot_lpf1_log_c(st:en)), 'pilot lpf1 real' )
+plot_sig_and_error(6, 212, real(pilot_lpf2_log(st:en)), real(pilot_lpf2_log(st:en) - pilot_lpf2_log_c(st:en)), 'pilot lpf2 real' )
plot_sig_and_error(7, 211, real(S1_log), real(S1_log - S1_log_c), 'S1 real' )
plot_sig_and_error(7, 212, imag(S1_log), imag(S1_log - S1_log_c), 'S1 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(13, 211, env_log, env_log - env_log_c, 'env' )
+st=1; en=3*Nt*P;
+plot_sig_and_error(13, 211, env_log(st:en), env_log(st:en) - env_log_c(st:en), 'env' )
stem_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;
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])
+stem_sig_and_error(15, 212, nin_log_c, nin_log - nin_log_c, 'nin')
% ---------------------------------------------------------------------------------------
% AUTOMATED CHECKS ------------------------------------------
check(rx_bits_log, rx_bits_log_c, 'rx bits');
check(sync_bit_log, sync_bit_log_c, 'sync bit');
check(coarse_fine_log, coarse_fine_log_c, 'coarse_fine');
+check(nin_log, nin_log_c, 'nin');
printf("\npasses: %d fails: %d\n", passes, fails);
float carrier_freq;
assert(FDMDV_BITS_PER_FRAME == NC*NB);
- assert(FDMDV_SAMPLES_PER_FRAME == M);
+ assert(FDMDV_NOM_SAMPLES_PER_FRAME == M);
+ assert(FDMDV_MAX_SAMPLES_PER_FRAME == (M+M/P));
f = (struct FDMDV*)malloc(sizeof(struct FDMDV));
if (f == NULL)
/* maximum number of input samples to demod */
- assert(nin < (M+M/P));
+ assert(nin <= (M+M/P));
/* Nc/2 tones below centre freq */
/* Nc/2 tones above centre freq */
for (c=NC/2; c<NC; c++)
- for (i=0; i<M; i++) {
+ for (i=0; i<nin; i++) {
phase_rx[c] = cmult(phase_rx[c], freq[c]);
rx_baseband[c][i] = cmult(rx_fdm[i], cconj(phase_rx[c]));
}
- /* add centre pilot tone */
+ /* centre pilot tone */
c = NC;
- for (i=0; i<M; i++) {
+ for (i=0; i<nin; i++) {
phase_rx[c] = cmult(phase_rx[c], freq[c]);
rx_baseband[c][i] = cmult(rx_fdm[i], cconj(phase_rx[c]));
}
*/
adjust = P - nin*P/M;
-
+
/* update buffer of NT rate P filtered symbols */
for(c=0; c<NC+1; c++)
M/4 part was adjusted by experiment, I know not why.... */
rx_timing = atan2(x.imag, x.real)*M/(2*PI) + M/4;
+ //printf("%f %f\n", x.real, x.imag);
+
if (rx_timing > M)
rx_timing -= M;
if (rx_timing < -M)
/* freq offset estimation and correction */
+
foff_coarse = rx_est_freq_offset(fdmdv, rx_fdm, *nin);
+
if (fdmdv->coarse_fine == COARSE)
fdmdv->foff = foff_coarse;
freq_shift(rx_fdm_fcorr, rx_fdm, fdmdv->foff, &fdmdv->foff_rect, &fdmdv->foff_phase_rect, *nin);
fdm_downconvert(rx_baseband, rx_fdm_fcorr, fdmdv->phase_rx, fdmdv->freq, *nin);
rx_filter(rx_filt, rx_baseband, fdmdv->rx_filter_memory, *nin);
fdmdv->rx_timing = rx_est_timing(rx_symbols, rx_filt, rx_baseband, fdmdv->rx_filter_mem_timing, env, fdmdv->rx_baseband_mem_timing, *nin);
+
+ /* adjust number of input samples to keep timing within bounds */
+
+ *nin = M;
+
+ if (fdmdv->rx_timing > 2*M/P)
+ *nin += M/P;
+
+ if (fdmdv->rx_timing < 0)
+ *nin -= M/P;
+
foff_fine = qpsk_to_bits(rx_bits, sync_bit, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols);
memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(NC+1));
void fdmdv_get_demod_stats(struct FDMDV *fdmdv, struct FDMDV_STATS *fdmdv_stats)
{
int c;
+ COMP pi_on_4;
+
+ pi_on_4.real = cos(PI/4.0);
+ pi_on_4.imag = sin(PI/4.0);
fdmdv_stats->snr = 0.0; /* TODO - implement SNR estimation */
fdmdv_stats->fest_coarse_fine = fdmdv->coarse_fine;
fdmdv_stats->foff = fdmdv->foff;
- fdmdv_stats->rx_timing = fdmdv->rx_timing/M;
+ fdmdv_stats->rx_timing = fdmdv->rx_timing;
fdmdv_stats->clock_offset = 0.0; /* TODO - implement clock offset estimation */
assert((NC+1) == FDMDV_NSYM);
- for(c=0; c<NC+1; c++) {
+ for(c=0; c<NC; c++) {
fdmdv_stats->rx_symbols[c] = fdmdv->phase_difference[c];
}
+
+ /* place pilots somewhere convenient on scatter diagram */
+
+ fdmdv_stats->rx_symbols[NC] = cmult(fdmdv->phase_difference[NC], pi_on_4);
}
#include "comp.h"
-#define FDMDV_BITS_PER_FRAME 28 /* odd/even frames 56 bits, 1400 bit/s */
-#define FDMDV_SAMPLES_PER_FRAME 160 /* 8000 Hz sample rate */
-#define FDMDV_SCALE 1000 /* suggested scaling for 16 bit shorts */
-#define FDMDV_NSYM 15
+#define FDMDV_BITS_PER_FRAME 28 /* 20ms frames, 1400 bit/s */
+#define FDMDV_NOM_SAMPLES_PER_FRAME 160 /* modulator output samples/frame and nominal demod samples/frame */
+ /* at 8000 Hz sample rate */
+#define FDMDV_MAX_SAMPLES_PER_FRAME 200 /* max demod samples/frame, use this to allocate storage */
+#define FDMDV_SCALE 1000 /* suggested scaling for 16 bit shorts */
+#define FDMDV_NSYM 15
struct FDMDV;
COMP rx_symbols[FDMDV_NSYM]; /* latest received symbols, for scatter plot */
int fest_coarse_fine; /* freq est state, 0-coarse 1-fine */
float foff; /* estimated freq offset in Hz */
- float rx_timing; /* timing offset -1..1 as fraction of symbol period */
+ float rx_timing; /* estimated optimum timing offset in samples */
float clock_offset; /* Estimated tx/rx sample clock offset in ppm */
};
char packed_bits[BYTES_PER_CODEC_FRAME];
int rx_bits[FDMDV_BITS_PER_FRAME];
int codec_bits[2*FDMDV_BITS_PER_FRAME];
- float rx_fdm[FDMDV_SAMPLES_PER_FRAME];
- short rx_fdm_scaled[FDMDV_SAMPLES_PER_FRAME];
+ float rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
+ short rx_fdm_scaled[FDMDV_MAX_SAMPLES_PER_FRAME];
int i, bit, byte, c;
int nin;
int sync_bit;
fdmdv = fdmdv_create();
frames = 0;
state = 0;
- nin = FDMDV_SAMPLES_PER_FRAME;
+ nin = FDMDV_NOM_SAMPLES_PER_FRAME;
while(fread(rx_fdm_scaled, sizeof(short), nin, fin) == nin)
{
- for(i=0; i<FDMDV_SAMPLES_PER_FRAME; i++)
- rx_fdm[i] = rx_fdm_scaled[i]/FDMDV_SCALE;
+ for(i=0; i<nin; i++)
+ rx_fdm[i] = (float)rx_fdm_scaled[i]/FDMDV_SCALE;
fdmdv_demod(fdmdv, rx_bits, &sync_bit, rx_fdm, &nin);
/* log data for optional Octave dump */
/* Optional dump to Octave log file */
- if ( strcmp(argv[3],"|") && (foct = fopen(argv[3],"wt")) == NULL ) {
- fprintf(stderr, "Error opening Octave dump file: %s: %s.\n",
- argv[3], strerror(errno));
- exit(1);
- }
- else {
- octave_save_complex(foct, "rx_symbols_log_c", (COMP*)rx_symbols_log, FDMDV_NSYM, MAX_FRAMES, MAX_FRAMES);
- octave_save_float(foct, "foff_log_c", foff_log, 1, MAX_FRAMES);
- octave_save_float(foct, "rx_timing_log_c", rx_timing_log, 1, MAX_FRAMES);
- octave_save_int(foct, "coarse_fine_log_c", coarse_fine_log, 1, MAX_FRAMES);
- fclose(foct);
+ if (argc == 4) {
+
+ /* make sure 3rd arg is not just the pipe command */
+
+ if (strcmp(argv[3],"|")) {
+ if ((foct = fopen(argv[3],"wt")) == NULL ) {
+ fprintf(stderr, "Error opening Octave dump file: %s: %s.\n",
+ argv[3], strerror(errno));
+ exit(1);
+ }
+ octave_save_complex(foct, "rx_symbols_log_c", (COMP*)rx_symbols_log, FDMDV_NSYM, MAX_FRAMES, MAX_FRAMES);
+ octave_save_float(foct, "foff_log_c", foff_log, 1, MAX_FRAMES);
+ octave_save_float(foct, "rx_timing_log_c", rx_timing_log, 1, MAX_FRAMES);
+ octave_save_int(foct, "coarse_fine_log_c", coarse_fine_log, 1, MAX_FRAMES);
+ fclose(foct);
+ }
}
fclose(fin);
struct FDMDV *fdmdv;
char packed_bits[BYTES_PER_CODEC_FRAME];
int tx_bits[2*FDMDV_BITS_PER_FRAME];
- COMP tx_fdm[2*FDMDV_SAMPLES_PER_FRAME];
- short tx_fdm_scaled[2*FDMDV_SAMPLES_PER_FRAME];
+ COMP tx_fdm[2*FDMDV_NOM_SAMPLES_PER_FRAME];
+ short tx_fdm_scaled[2*FDMDV_NOM_SAMPLES_PER_FRAME];
int frames;
int i, bit, byte;
int sync_bit;
fdmdv_mod(fdmdv, tx_fdm, tx_bits, &sync_bit);
assert(sync_bit == 1);
- fdmdv_mod(fdmdv, &tx_fdm[FDMDV_SAMPLES_PER_FRAME], &tx_bits[FDMDV_BITS_PER_FRAME], &sync_bit);
+ fdmdv_mod(fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &tx_bits[FDMDV_BITS_PER_FRAME], &sync_bit);
assert(sync_bit == 0);
/* scale and save to disk as shorts */
- for(i=0; i<2*FDMDV_SAMPLES_PER_FRAME; i++)
+ for(i=0; i<2*FDMDV_NOM_SAMPLES_PER_FRAME; i++)
tx_fdm_scaled[i] = FDMDV_SCALE * tx_fdm[i].real;
- fwrite(tx_fdm_scaled, sizeof(short), 2*FDMDV_SAMPLES_PER_FRAME, fout);
+ fwrite(tx_fdm_scaled, sizeof(short), 2*FDMDV_NOM_SAMPLES_PER_FRAME, fout);
/* if this is in a pipeline, we probably don't want the usual
buffering to occur */
#include "octave.h"
#define FRAMES 25
+#define CHANNEL_BUF_SIZE (10*M)
int main(int argc, char *argv[])
{
COMP tx_symbols[NC+1];
COMP tx_baseband[NC+1][M];
COMP tx_fdm[M];
+ float channel[CHANNEL_BUF_SIZE];
+ int channel_count;
float rx_fdm[M+M/P];
float foff_coarse;
- int nin;
+ int nin, next_nin;
COMP rx_fdm_fcorr[M+M/P];
COMP rx_baseband[NC+1][M+M/P];
COMP rx_filt[NC+1][P+1];
float foff_fine_log[FRAMES];
int sync_bit_log[FRAMES];
int coarse_fine_log[FRAMES];
+ int nin_log[FRAMES];
FILE *fout;
- int f,c,i;
+ int f,c,i,j;
fdmdv = fdmdv_create();
+ next_nin = M;
+ channel_count = 0;
rx_baseband_log_col_index = 0;
rx_filt_log_col_index = 0;
tx_filter(tx_baseband, tx_symbols, fdmdv->tx_filter_memory);
fdm_upconvert(tx_fdm, tx_baseband, fdmdv->phase_tx, fdmdv->freq);
+ /* --------------------------------------------------------*\
+ Channel
+ \*---------------------------------------------------------*/
+
+ nin = next_nin;
+ /*
+ if (f == 2)
+ nin = 120;
+ if (f == 3)
+ nin = 200;
+ if ((f !=2) && (f != 3))
+ nin = M;
+ */
+ /* add M tx samples to end of buffer */
+
+ assert((channel_count + M) < CHANNEL_BUF_SIZE);
for(i=0; i<M; i++)
- rx_fdm[i] = tx_fdm[i].real;
- nin = M;
+ channel[channel_count+i] = tx_fdm[i].real;
+ channel_count += M;
+
+ /* take nin samples from start of buffer */
+
+ for(i=0; i<nin; i++)
+ rx_fdm[i] = channel[i];
+ /* shift buffer back */
+
+ for(i=0,j=nin; j<channel_count; i++,j++)
+ channel[i] = channel[j];
+ channel_count -= nin;
+
/* --------------------------------------------------------*\
Demodulator
\*---------------------------------------------------------*/
rx_timing = rx_est_timing(rx_symbols, rx_filt, rx_baseband, fdmdv->rx_filter_mem_timing, env, fdmdv->rx_baseband_mem_timing, nin);
foff_fine = qpsk_to_bits(rx_bits, &sync_bit, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols);
memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(NC+1));
+
+ next_nin = M;
+
+ if (rx_timing > 2*M/P)
+ next_nin += M/P;
+
+ if (rx_timing < 0)
+ next_nin -= M/P;
+
fdmdv->coarse_fine = freq_state(sync_bit, &fdmdv->fest_state);
fdmdv->foff -= TRACK_COEFF*foff_fine;
/* rx filtering */
for(c=0; c<NC+1; c++) {
- for(i=0; i<(P*M)/nin; i++)
+ for(i=0; i<(P*nin)/M; i++)
rx_filt_log[c][rx_filt_log_col_index + i] = rx_filt[c][i];
}
- rx_filt_log_col_index += (P*M)/nin;
+ rx_filt_log_col_index += (P*nin)/M;
/* timing estimation */
memcpy(&env_log[NT*P*f], env, sizeof(float)*NT*P);
rx_timing_log[f] = rx_timing;
+ nin_log[f] = nin;
for(c=0; c<NC+1; c++)
rx_symbols_log[c][f] = rx_symbols[c];
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, "coarse_fine_log_c", coarse_fine_log, 1, FRAMES);
+ octave_save_int(fout, "nin_log_c", nin_log, 1, FRAMES);
fclose(fout);
fdmdv_destroy(fdmdv);