--- /dev/null
+% tfft_log.m
+%
+% Used in conjunction swith src/fdmdv_demod to test the
+% fdmdv_get_rx_spectrum() function.
+%
+% codec2-dev/src$ ./fdmdv_demod fdmdv_mod.raw tmp.c2 dump.txt
+% octave:3> tfft_log("../src/dump.txt")
+%
+% Copyright David Rowe 2012
+% This program is distributed under the terms of the GNU General Public License
+% Version 2
+%
+
+function tfft_log(dumpfilename)
+
+ load(dumpfilename);
+
+ [rows cols] = size(rx_spec_log_c);
+ Fs = 8000; low_freq = 0; high_freq = 2500;
+ res = (Fs/2)/cols;
+ st_bin = low_freq/res + 1;
+ en_bin = high_freq/res;
+ xaxis = (st_bin:en_bin)*res;
+
+ f_start = 2; f_end = 100;
+ beta = 0.1;
+
+ av = zeros(f_end, en_bin-st_bin+1);
+ for r=f_start:f_end
+ x = (1-beta)*av(r-1,:) + beta*rx_spec_log_c(r,st_bin:en_bin);
+ av(r,:) = x;
+ end
+
+ % spectrogram (waterfall)
+
+ figure(1)
+ clf;
+ imagesc(av,[-40 0]);
+
+ % animated spectrum display
+
+ figure(2)
+ clf;
+ for r=f_start:f_end
+ plot(xaxis, av(r,:))
+ axis([ low_freq high_freq -40 0])
+ sleep(0.1)
+ end
+endfunction
f->noise_est[c] = 0.0;
}
- for(i=0; i<2*FDMDV_NFFT; i++)
+ for(i=0; i<2*FDMDV_NSPEC; i++)
f->fft_buf[i] = 0.0;
- f->fft_cfg = kiss_fft_alloc (2*FDMDV_NFFT, 0, NULL, NULL);
+ f->fft_cfg = kiss_fft_alloc (2*FDMDV_NSPEC, 0, NULL, NULL);
assert(f->fft_cfg != NULL);
/*---------------------------------------------------------------------------*\
- FUNCTION....: fdmdv_get_fft()
+ FUNCTION....: fdmdv_get_rx_spectrum()
AUTHOR......: David Rowe
DATE CREATED: 9 June 2012
- Performs a FFT on the received modem signal at the input of the
- demod, returns the FDMDV_NFFT point magnitiude spectrum in dB. 0dB
- is a signal with amplitude +/- 2^15.
+ Returns the FDMDV_NSPEC point magnitude spectrum of the rx signal in
+ dB. The spectral samples are scaled so that 0dB is the peak, a good
+ range for plotting is 0 to -40dB.
- The output can be used to plot a spectrum of the demod input.
- Sucessive calls can be used to build up a waterfall or spectrogram
+ Successive calls can be used to build up a waterfall or spectrogram
plot, by mapping the received levels to colours.
- The time-frequency resolution of the FFT can be adjusted by varying
- FDMDV_NFFT. Note that a 2*FDMDV_NFFT size FFT is reqd to get
- FDMDV_NFFT output points.
+ The time-frequency resolution of the spectrum can be adjusted by varying
+ FDMDV_NSPEC. Note that a 2*FDMDV_NSPEC size FFT is reqd to get
+ FDMDV_NSPEC output points. FDMDV_NSPEC must be a power of 2.
+
+ See octave/tfft_log.m for a demo real time spectral display using
+ Octave. This demo averages the output over time to get a smoother
+ display:
+
+ av = 0.9*av + 0.1*mag_dB
\*---------------------------------------------------------------------------*/
-void CODEC2_WIN32SUPPORT fdmdv_get_fft(struct FDMDV *f, float mag_dB[], float rx_fdm[], int nin)
+void CODEC2_WIN32SUPPORT fdmdv_get_rx_spectrum(struct FDMDV *f, float mag_spec_dB[],
+ float rx_fdm[], int nin)
{
int i,j;
- COMP fft_in[2*FDMDV_NFFT];
- COMP fft_out[2*FDMDV_NFFT];
- float fullscale_dB;
+ COMP fft_in[2*FDMDV_NSPEC];
+ COMP fft_out[2*FDMDV_NSPEC];
+ float full_scale_dB;
/* update buffer of input samples */
- for(i=0; i<2*FDMDV_NFFT-nin; i++)
+ for(i=0; i<2*FDMDV_NSPEC-nin; i++)
f->fft_buf[i] = f->fft_buf[i+nin];
for(j=0; j<nin; j++,i++)
f->fft_buf[i] = rx_fdm[j];
- assert(i == 2*FDMDV_NFFT);
+ assert(i == 2*FDMDV_NSPEC);
/* window and FFT */
- for(i=0; i<2*FDMDV_NFFT; i++) {
- fft_in[i].real = f->fft_buf[i] * (0.5 - 0.5*cos((float)i*2.0*PI/(2*FDMDV_NFFT)));
+ for(i=0; i<2*FDMDV_NSPEC; i++) {
+ fft_in[i].real = f->fft_buf[i] * (0.5 - 0.5*cos((float)i*2.0*PI/(2*FDMDV_NSPEC)));
fft_in[i].imag = 0.0;
}
kiss_fft(f->fft_cfg, (kiss_fft_cpx *)fft_in, (kiss_fft_cpx *)fft_out);
- /* scale and convert to dB */
+ /* FFT scales up a signal of level 1 FDMDV_NSPEC */
- fullscale_dB = 20.0*log10(FDMDV_NFFT*32767.0);
+ full_scale_dB = 20*log10(FDMDV_NSPEC);
+
+ /* scale and convert to dB */
- for(i=0; i<FDMDV_NFFT; i++) {
- mag_dB[i] = 10.0*log10(fft_out[i].real*fft_out[i].real + fft_out[i].imag*fft_out[i].imag);
- mag_dB[i] -= fullscale_dB;
+ for(i=0; i<FDMDV_NSPEC; i++) {
+ mag_spec_dB[i] = 10.0*log10(fft_out[i].real*fft_out[i].real + fft_out[i].imag*fft_out[i].imag);
+ mag_spec_dB[i] -= full_scale_dB;
}
}
/* FFT points */
-#define FDMDV_NFFT 256
+#define FDMDV_NSPEC 512
/* FDMDV states and stats structures */
void CODEC2_WIN32SUPPORT fdmdv_put_test_bits(struct FDMDV *f, int *sync, int *bit_errors, int *ntest_bits, int rx_bits[]);
void CODEC2_WIN32SUPPORT fdmdv_get_demod_stats(struct FDMDV *fdmdv_state, struct FDMDV_STATS *fdmdv_stats);
-void CODEC2_WIN32SUPPORT fdmdv_get_fft(struct FDMDV *fdmdv_state, float mag_dB[], float rx_fdm[], int nin);
+void CODEC2_WIN32SUPPORT fdmdv_get_rx_spectrum(struct FDMDV *fdmdv_state, float mag_dB[], float rx_fdm[], int nin);
void CODEC2_WIN32SUPPORT fdmdv_8_to_48(float out48k[], float in8k[], int n);
void CODEC2_WIN32SUPPORT fdmdv_48_to_8(float out8k[], float in48k[], int n);
int sync_bit_log[MAX_FRAMES];
int rx_bits_log[FDMDV_BITS_PER_FRAME*MAX_FRAMES];
float snr_est_log[MAX_FRAMES];
- float *fft_log;
-
+ float *rx_spec_log;
+ int max_frames_reached;
+
if (argc < 3) {
printf("usage: %s InputModemRawFile OutputBitFile [OctaveDumpFile]\n", argv[0]);
printf("e.g %s hts1a_fdmdv.raw hts1a.c2\n", argv[0]);
rx_fdm_log = (float*)malloc(sizeof(float)*FDMDV_MAX_SAMPLES_PER_FRAME*MAX_FRAMES);
assert(rx_fdm_log != NULL);
- fft_log = (float*)malloc(sizeof(float)*FDMDV_NFFT*MAX_FRAMES);
- assert(fft_log != NULL);
+ rx_spec_log = (float*)malloc(sizeof(float)*FDMDV_NSPEC*MAX_FRAMES);
+ assert(rx_spec_log != NULL);
fdmdv = fdmdv_create();
f = 0;
state = 0;
nin = FDMDV_NOM_SAMPLES_PER_FRAME;
rx_fdm_log_col_index = 0;
+ max_frames_reached = 0;
while(fread(rx_fdm_scaled, sizeof(short), nin, fin) == nin)
{
memcpy(&rx_bits_log[FDMDV_BITS_PER_FRAME*f], rx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME);
snr_est_log[f] = stats.snr_est;
- fdmdv_get_fft(fdmdv, &fft_log[f*FDMDV_NFFT], rx_fdm, nin_prev);
+ fdmdv_get_rx_spectrum(fdmdv, &rx_spec_log[f*FDMDV_NSPEC], rx_fdm, nin_prev);
f++;
}
- else
+
+ if ((f == MAX_FRAMES) && !max_frames_reached) {
fprintf(stderr,"MAX_FRAMES exceed in Octave log, log truncated\n");
+ max_frames_reached = 1;
+ }
/* state machine to output codec bits only if we have a 0,1
sync bit sequence */
octave_save_int(foct, "rx_bits_log_c", rx_bits_log, 1, FDMDV_BITS_PER_FRAME*f);
octave_save_int(foct, "sync_bit_log_c", sync_bit_log, 1, f);
octave_save_float(foct, "snr_est_log_c", snr_est_log, 1, f, MAX_FRAMES);
- //octave_save_float(foct, "fft_log_c", fft_log, f, FDMDV_NFFT, FDMDV_NFFT);
+ octave_save_float(foct, "rx_spec_log_c", rx_spec_log, f, FDMDV_NSPEC, FDMDV_NSPEC);
fclose(foct);
}
}
fclose(fin);
fclose(fout);
free(rx_fdm_log);
- free(fft_log);
+ free(rx_spec_log);
fdmdv_destroy(fdmdv);
return 0;
/* Buf for FFT/waterfall */
- float fft_buf[2*FDMDV_NFFT];
+ float fft_buf[2*FDMDV_NSPEC];
kiss_fft_cfg fft_cfg;
};