mag = abs(phase_tx(c));
phase_tx(c) /= mag;
end
+ mag = abs(fbb_phase_tx);
+ fbb_phase_tx /= mag;
endfunction
% Combined down convert and rx filter, more memory efficient but less intuitive design
+% TODO: Decimate mem update and downconversion, this will save some more CPU and memory
+% note phase would have to advance 4 times as fast
function rx_filt = down_convert_and_rx_filter(rx_fdm, nin, dec_rate)
global Nc;
% Saves rx decimation filter coeffs to a text file in the form of a C array
function rxdec_file(filename)
- global rxdec;
+ global rxdec_coeff;
global Nrxdec;
f=fopen(filename,"wt");
fprintf(f,"/* Generated by rxdec_file() Octave function */\n\n");
- fprintf(f,"const float rxdec[]={\n");
- for m=1:Nfilter-1
- fprintf(f," %g,\n",rxdec(m));
+ fprintf(f,"const float rxdec_coeff[]={\n");
+ for m=1:Nrxdec-1
+ fprintf(f," %g,\n",rxdec_coeff(m));
endfor
- fprintf(f," %g\n};\n",rxdec(Nrxdecr));
+ fprintf(f," %g\n};\n",rxdec_coeff(Nrxdec));
fclose(f);
endfunction
S=fft(spec_mem.*hanning(Nspec)',Nspec);
SdB = 0.9*SdB + 0.1*20*log10(abs(S));
+ % shift down to complex baseband
+
+ for i=1:nin
+ fbb_phase_rx = fbb_phase_rx*fbb_rect';
+ rx_fdm(i) = rx_fdm(i)*fbb_phase_rx;
+ end
+ mag = abs(fbb_phase_rx);
+ fbb_phase_rx /= mag;
+
% 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);
fbb_phase_rx = fbb_phase_rx*fbb_rect';
rx_fdm(i) = rx_fdm(i)*fbb_phase_rx;
end
+ mag = abs(fbb_phase_rx);
+ fbb_phase_rx /= mag;
% frequency offset estimation and correction, need to call rx_est_freq_offset even in sync
% mode to keep states updated
end
rx_fdm_filter = rxdec_filter(rx_fdm, M);
-
- % TODO: Decimate to rate Q at this point, this will save some more CPU
- % in down_convert_and_rx_filter
-
rx_filt = down_convert_and_rx_filter(rx_fdm_filter, M, M/Q);
[rx_symbols rx_timing] = rx_est_timing(rx_filt, M);
% adjust this if the screen is getting a bit cluttered
global no_plot_list;
-no_plot_list = [1 2 3 4 5 6 7 8 16];
+no_plot_list = [];
for f=1:frames
channel = channel(nin+1:channel_count);
channel_count -= nin;
- % demodulator
+ % demodulator --------------------------------------------
+
+ % shift down to complex baseband
+
+ for i=1:nin
+ fbb_phase_rx = fbb_phase_rx*fbb_rect';
+ rx_fdm(i) = rx_fdm(i)*fbb_phase_rx;
+ end
+ mag = abs(fbb_phase_rx);
+ fbb_phase_rx /= mag;
[pilot prev_pilot pilot_lut_index prev_pilot_lut_index] = get_pilot(pilot_lut_index, prev_pilot_lut_index, nin);
rx_fdm_fcorr(i) = rx_fdm(i)*foff_phase_rect;
end
-if 1
- % more memory efficient but more complex
- rx_filt = down_convert_and_rx_filter(rx_fdm_fcorr, nin);
-else
- rx_baseband = fdm_downconvert(rx_fdm_corr,nin);
- rx_baseband_log = [rx_baseband_log rx_baseband];
- rx_filt = rx_filter(rx_baseband, nin);
-end
+ rx_fdm_filter = rxdec_filter(rx_fdm_fcorr, nin);
+ rx_filt = down_convert_and_rx_filter(rx_fdm_filter, nin, M/Q);
rx_filt_log = [rx_filt_log rx_filt];
#include "fdmdv_internal.h"
#include "codec2_fdmdv.h"
#include "rn.h"
+#include "rxdec_coeff.h"
#include "test_bits.h"
#include "pilot_coeff.h"
#include "kiss_fft.h"
}
fdmdv_set_fsep(f, FSEP);
- f->freq[Nc].real = cosf(2.0*PI*FDMDV_FCENTRE/FS);
- f->freq[Nc].imag = sinf(2.0*PI*FDMDV_FCENTRE/FS);
- f->freq_pol[Nc] = 2.0*PI*FDMDV_FCENTRE/FS;
+ f->freq[Nc].real = cosf(2.0*PI*0.0/FS);
+ f->freq[Nc].imag = sinf(2.0*PI*0.0/FS);
+ f->freq_pol[Nc] = 2.0*PI*0.0/FS;
+
+ f->fbb_rect.real = cosf(2.0*PI*FDMDV_FCENTRE/FS);
+ f->fbb_rect.imag = sinf(2.0*PI*FDMDV_FCENTRE/FS);
+ f->fbb_pol = 2.0*PI*FDMDV_FCENTRE/FS;
+ f->fbb_phase_tx.real = 1.0;
+ f->fbb_phase_tx.imag = 0.0;
+ f->fbb_phase_rx.real = 1.0;
+ f->fbb_phase_rx.imag = 0.0;
/* Generate DBPSK pilot Look Up Table (LUT) */
f->pilot_lut_index = 0;
f->prev_pilot_lut_index = 3*M;
+ for(i=0; i<NRXDEC-1+M; i++) {
+ f->rxdec_lpf_mem[i].real = 0.0;
+ f->rxdec_lpf_mem[i].imag = 0.0;
+ }
+
for(i=0; i<NPILOTLPF; i++) {
f->pilot_lpf1[i].real = f->pilot_lpf2[i].real = 0.0;
f->pilot_lpf1[i].imag = f->pilot_lpf2[i].imag = 0.0;
float carrier_freq;
f->fsep = fsep;
+
/* Set up frequency of each carrier */
for(c=0; c<f->Nc/2; c++) {
- carrier_freq = (-f->Nc/2 + c)*f->fsep + FDMDV_FCENTRE;
+ carrier_freq = (-f->Nc/2 + c)*f->fsep;
f->freq[c].real = cosf(2.0*PI*carrier_freq/FS);
f->freq[c].imag = sinf(2.0*PI*carrier_freq/FS);
f->freq_pol[c] = 2.0*PI*carrier_freq/FS;
}
for(c=f->Nc/2; c<f->Nc; c++) {
- carrier_freq = (-f->Nc/2 + c + 1)*f->fsep + FDMDV_FCENTRE;
+ carrier_freq = (-f->Nc/2 + c + 1)*f->fsep;
f->freq[c].real = cosf(2.0*PI*carrier_freq/FS);
f->freq[c].imag = sinf(2.0*PI*carrier_freq/FS);
f->freq_pol[c] = 2.0*PI*carrier_freq/FS;
\*---------------------------------------------------------------------------*/
-void fdm_upconvert(COMP tx_fdm[], int Nc, COMP tx_baseband[NC+1][M], COMP phase_tx[], COMP freq[])
+void fdm_upconvert(COMP tx_fdm[], int Nc, COMP tx_baseband[NC+1][M], COMP phase_tx[], COMP freq[],
+ COMP *fbb_phase, COMP fbb_rect)
{
int i,c;
COMP two = {2.0, 0.0};
tx_fdm[i] = cadd(tx_fdm[i], pilot);
}
+ /* shift whole thing up to carrier freq */
+
+ for (i=0; i<M; i++) {
+ *fbb_phase = cmult(*fbb_phase, fbb_rect);
+ tx_fdm[i] = cmult(tx_fdm[i], *fbb_phase);
+ }
+
/*
Scale such that total Carrier power C of real(tx_fdm) = Nc. This
excludes the power of the pilot tone.
phase_tx[c].real /= mag;
phase_tx[c].imag /= mag;
}
+
+ mag = cabsolute(*fbb_phase);
+ fbb_phase->real /= mag;
+ fbb_phase->imag /= mag;
}
/*---------------------------------------------------------------------------*\
TIMER_SAMPLE_AND_LOG(tx_filter_start, mod_start, " bits_to_dqpsk_symbols");
tx_filter(tx_baseband, fdmdv->Nc, tx_symbols, fdmdv->tx_filter_memory);
TIMER_SAMPLE_AND_LOG(fdm_upconvert_start, tx_filter_start, " tx_filter");
- fdm_upconvert(tx_fdm, fdmdv->Nc, tx_baseband, fdmdv->phase_tx, fdmdv->freq);
+ fdm_upconvert(tx_fdm, fdmdv->Nc, tx_baseband, fdmdv->phase_tx, fdmdv->freq, &fdmdv->fbb_phase_tx, fdmdv->fbb_rect);
TIMER_SAMPLE_AND_LOG2(fdm_upconvert_start, " fdm_upconvert");
*sync_bit = fdmdv->tx_pilot_bit;
assert(j <= (P+1)); /* check for any over runs */
}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: rxdec_filter()
+ AUTHOR......: David Rowe
+ DATE CREATED: 31 July 2014
+
+ +/- 1000Hz low pass filter, allows us to filter at rate Q to save CPU load.
+
+\*---------------------------------------------------------------------------*/
+
+void rxdec_filter(COMP rx_fdm_filter[], COMP rx_fdm[], COMP rxdec_lpf_mem[], int nin) {
+ int i,j,k;
+
+ for(i=0; i<NRXDEC-1+M-nin; i++)
+ rxdec_lpf_mem[i] = rxdec_lpf_mem[i+nin];
+ for(i=0, j=NRXDEC-1+M-nin; i<nin; i++,j++)
+ rxdec_lpf_mem[j] = rx_fdm[i];
+
+ 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].imag = 0.0;
+ for(k=0; k<NRXDEC; k++)
+ rx_fdm_filter[i].imag += rxdec_lpf_mem[i+k].imag * rxdec_coeff[k];
+ }
+}
+
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTION....: fir_filter()
+ AUTHOR......: David Rowe
+ DATE CREATED: July 2014
+
+ Helper fir filter function.
+
+\*---------------------------------------------------------------------------*/
+
+static float fir_filter(float mem[], float coeff[], int dec_rate) {
+ float acc = 0.0;
+ int m;
+
+ for(m=0; m<NFILTER; m+=dec_rate) {
+ acc += coeff[m] * mem[2*m];
+ }
+
+ return dec_rate*acc;
+}
+
+
/*---------------------------------------------------------------------------*\
FUNCTION....: down_convert_and_rx_filter()
void down_convert_and_rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_fdm[],
COMP rx_fdm_mem[], COMP phase_rx[], COMP freq[],
- float freq_pol[], int nin)
+ float freq_pol[], int nin, int dec_rate)
{
- int i,k,m,c,st,N;
+ int i,k,c,st,N;
float windback_phase, mag;
COMP windback_phase_rect;
COMP rx_baseband[NFILTER+M];
N=M/P;
for(i=0, k=0; i<nin; i+=N, k++) {
-
+ #ifdef ORIG
rx_filt[c][k].real = 0.0; rx_filt[c][k].imag = 0.0;
- for(m=0; m<NFILTER; m++)
- rx_filt[c][k] = cadd(rx_filt[c][k], fcmult(gt_alpha5_root[m], rx_baseband[st+i+m]));
+ for(m=0; m<NFILTER; m++)
+ rx_filt[c][k] = cadd(rx_filt[c][k], fcmult(gt_alpha5_root[m], rx_baseband[st+i+m]));
+ #else
+ rx_filt[c][k].real = fir_filter(&rx_baseband[st+i].real, (float*)gt_alpha5_root, dec_rate);
+ rx_filt[c][k].imag = fir_filter(&rx_baseband[st+i].imag, (float*)gt_alpha5_root, dec_rate);
+ #endif
}
TIMER_SAMPLE_AND_LOG2(filter_start, " filter");
{
float foff_coarse, foff_fine;
COMP rx_fdm_fcorr[M+M/P];
+ COMP rx_fdm_filter[M+M/P];
COMP rx_filt[NC+1][P+1];
COMP rx_symbols[NC+1];
float env[NT*P];
TIMER_VAR(demod_start, fdmdv_freq_shift_start, down_convert_and_rx_filter_start);
TIMER_VAR(rx_est_timing_start, qpsk_to_bits_start, snr_update_start, freq_state_start);
+ /* shift down to complex baseband */
+
+ fdmdv_freq_shift(rx_fdm, rx_fdm, -FDMDV_FCENTRE, &fdmdv->fbb_phase_rx, *nin);
+
/* freq offset estimation and correction */
TIMER_SAMPLE(demod_start);
/* baseband processing */
- down_convert_and_rx_filter(rx_filt, fdmdv->Nc, rx_fdm_fcorr, fdmdv->rx_fdm_mem, fdmdv->phase_rx, fdmdv->freq,
- fdmdv->freq_pol, *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/Q);
TIMER_SAMPLE_AND_LOG(rx_est_timing_start, down_convert_and_rx_filter_start, " down_convert_and_rx_filter");
fdmdv->rx_timing = rx_est_timing(rx_symbols, fdmdv->Nc, rx_filt, fdmdv->rx_filter_mem_timing, env, *nin);
TIMER_SAMPLE_AND_LOG(qpsk_to_bits_start, rx_est_timing_start, " rx_est_timing");
#define FSEP 75 /* Default separation between carriers (Hz) */
#define NT 5 /* number of symbols we estimate timing over */
-#define P 4 /* oversample factor used for initial rx symbol filtering */
+#define P 4 /* oversample factor used for initial rx symbol filtering output */
+#define Q (M/4) /* oversample factor used for initial rx symbol filtering input */
+#define NRXDEC 31 /* number of taps in the rx decimation filter */
#define NPILOT_LUT (4*M) /* number of pilot look up table samples */
#define NPILOTCOEFF 30 /* number of FIR filter coeffs in LP filter */
COMP S1[MPILOTFFT];
COMP S2[MPILOTFFT];
+ /* baseband to low IF carrier states */
+
+ COMP fbb_rect;
+ float fbb_pol;
+ COMP fbb_phase_tx;
+ COMP fbb_phase_rx;
+
/* freq offset correction states */
float foff;
/* Demodulator */
+ COMP rxdec_lpf_mem[NRXDEC-1+M];
COMP rx_fdm_mem[NFILTER+M];
COMP phase_rx[NC+1];
COMP rx_filter_mem_timing[NC+1][NT*P];
void bits_to_dqpsk_symbols(COMP tx_symbols[], int Nc, COMP prev_tx_symbols[], int tx_bits[], int *pilot_bit, int old_qpsk_mapping);
void tx_filter(COMP tx_baseband[NC+1][M], int Nc, COMP tx_symbols[], COMP tx_filter_memory[NC+1][NSYM]);
-void fdm_upconvert(COMP tx_fdm[], int Nc, COMP tx_baseband[NC+1][M], COMP phase_tx[], COMP freq_tx[]);
+void fdm_upconvert(COMP tx_fdm[], int Nc, COMP tx_baseband[NC+1][M], COMP phase_tx[], COMP freq_tx[],
+ COMP *fbb_phase, COMP fbb_rect);
void generate_pilot_fdm(COMP *pilot_fdm, int *bit, float *symbol, float *filter_mem, COMP *phase, COMP *freq);
void generate_pilot_lut(COMP pilot_lut[], COMP *pilot_freq);
float rx_est_freq_offset(struct FDMDV *f, COMP rx_fdm[], int nin);
void lpf_peak_pick(float *foff, float *max, COMP pilot_baseband[], COMP pilot_lpf[], kiss_fft_cfg fft_pilot_cfg, COMP S[], int nin);
void fdm_downconvert(COMP rx_baseband[NC+1][M+M/P], int Nc, COMP rx_fdm[], COMP phase_rx[], COMP freq[], int nin);
+void rxdec_filter(COMP rx_fdm_filter[], COMP rx_fdm[], COMP rxdec_lpf_mem[], int nin);
void rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_baseband[NC+1][M+M/P], COMP rx_filter_memory[NC+1][NFILTER], int nin);
void down_convert_and_rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_fdm[],
COMP rx_fdm_mem[], COMP phase_rx[], COMP freq[],
- float freq_pol[], int nin);
+ float freq_pol[], int nin, int dec_rate);
float rx_est_timing(COMP rx_symbols[], int Nc,
COMP rx_filt[NC+1][P+1],
COMP rx_filter_mem_timing[NC+1][NT*P],
--- /dev/null
+/* Generated by rxdec_file() Octave function */
+
+const float rxdec_coeff[]={
+ -0.00125472,
+ -0.00204605,
+ -0.0019897,
+ 0.000163906,
+ 0.00490937,
+ 0.00986375,
+ 0.0096718,
+ -0.000480351,
+ -0.019311,
+ -0.0361822,
+ -0.0341251,
+ 0.000827866,
+ 0.0690577,
+ 0.152812,
+ 0.222115,
+ 0.249004,
+ 0.222115,
+ 0.152812,
+ 0.0690577,
+ 0.000827866,
+ -0.0341251,
+ -0.0361822,
+ -0.019311,
+ -0.000480351,
+ 0.0096718,
+ 0.00986375,
+ 0.00490937,
+ 0.000163906,
+ -0.0019897,
+ -0.00204605,
+ -0.00125472
+};
###################################################
-CFLAGS = -std=gnu99 -O2 -g -Wall -Tstm32_flash.ld -DSTM32F4XX -DCORTEX_M4
+CFLAGS = -std=gnu99 -O3 --param max-unroll-times=200 -g -Wall -Tstm32_flash.ld -DSTM32F4XX -DCORTEX_M4
CFLAGS += -mlittle-endian -mthumb -mthumb-interwork -nostartfiles -mcpu=cortex-m4
ifeq ($(FLOAT_TYPE), hard)
float foff_coarse;
int nin, next_nin;
COMP rx_fdm_fcorr[M+M/P];
+ COMP rx_fdm_filter[M+M/P];
COMP rx_baseband[NC+1][M+M/P];
COMP rx_filt[NC+1][P+1];
float rx_timing;
bits_to_dqpsk_symbols(tx_symbols, FDMDV_NC, fdmdv->prev_tx_symbols, tx_bits, &fdmdv->tx_pilot_bit, 0);
memcpy(fdmdv->prev_tx_symbols, tx_symbols, sizeof(COMP)*(FDMDV_NC+1));
tx_filter(tx_baseband, FDMDV_NC, tx_symbols, fdmdv->tx_filter_memory);
- fdm_upconvert(tx_fdm, FDMDV_NC, tx_baseband, fdmdv->phase_tx, fdmdv->freq);
+ fdm_upconvert(tx_fdm, FDMDV_NC, tx_baseband, fdmdv->phase_tx, fdmdv->freq, &fdmdv->fbb_phase_tx, fdmdv->fbb_rect);
/* --------------------------------------------------------*\
Channel
Demodulator
\*---------------------------------------------------------*/
+ /* shift down to complex baseband */
+
+ fdmdv_freq_shift(rx_fdm, rx_fdm, -FDMDV_FCENTRE, &fdmdv->fbb_phase_rx, nin);
+
/* freq offset estimation and correction */
foff_coarse = rx_est_freq_offset(fdmdv, rx_fdm, nin);
/* baseband processing */
- down_convert_and_rx_filter(rx_filt, fdmdv->Nc, rx_fdm_fcorr, fdmdv->rx_fdm_mem, fdmdv->phase_rx, fdmdv->freq,
- fdmdv->freq_pol, 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/Q);
rx_timing = rx_est_timing(rx_symbols, FDMDV_NC, rx_filt, fdmdv->rx_filter_mem_timing, env, nin);
foff_fine = qpsk_to_bits(rx_bits, &sync_bit, FDMDV_NC, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols, 0);