build_linux/src$ ./ofdm_demod ../../octave/ofdm_test.raw - | ./ofdm_put_test_bits -
+Acceptance Tests
+----------------
+
+The rate 1/2 LPDC code can correct up to about 10% raw BER, so a good
+test is to run the modem at Eb/No operating points that produce just
+less that BER=0.1
+
+The BER2 measure truncates the effect of any start up transients,
+e.g. as the frequency offset is tracked out.
+
+1/ HF Multipath:
+
+ octave:580> ofdm_tx("ofdm_test.raw",60,4,'hf',20,-0.1)
+ octave:581> ofdm_rx("ofdm_test.raw")
+ BER2.: 0.0997 Tbits: 93752 Terrs: 9344
+
+2/ AWGN:
+
+ octave:582> ofdm_tx("ofdm_test.raw",60,0,'awgn')
+ octave:583> ofdm_rx("ofdm_test.raw")
+ BER2.: 0.0827 Tbits: 96846 Terrs: 8008
+
C Code
------
AUTHORS.....: David Rowe & Steve Sampson
DATE CREATED: June 2017
- A Library of functions that implement a BPSK/QPSK OFDM modem
+ A Library of functions that implement a QPSK OFDM modem, C port of
+ the Octave functions in ofdm_lib.m
\*---------------------------------------------------------------------------*/
/*
- Copyright (C) 2017 David Rowe
+ Copyright (C) 2017/2018 David Rowe
All rights reserved.
ofdm->timing_mx = 0.0f;
ofdm->nin = OFDM_SAMPLESPERFRAME;
+ /* sync state machine */
+
+ strcpy(ofdm->sync_state,"searching");
+ strcpy(ofdm->last_sync_state,"searching");
+ ofdm->uw_errors = 0;
+ ofdm->sync_counter = 0;
+ ofdm->frame_count = 0;
+ ofdm->sync_start = 0;
+ ofdm->sync_end = 0;
+
/* create the OFDM waveform */
complex float temp[OFDM_M];
ofdm->timing_est += (ft_est - ceilf(OFDM_FTWINDOWWIDTH / 2));
if (ofdm->verbose > 1) {
- fprintf(stdout, " ft_est: %2d timing_est: %2d sample_point: %2d\n", ft_est, ofdm->timing_est, ofdm->sample_point);
+ fprintf(stderr, " ft_est: %2d timing_est: %2d sample_point: %2d\n", ft_est, ofdm->timing_est, ofdm->sample_point);
}
/* Black magic to keep sample_point inside cyclic prefix. Or something like that. */
}
}
+
+/* iterate state machine ------------------------------------*/
+
+void ofdm_sync_state_machine(struct OFDM *ofdm, int *rx_uw) {
+ char next_state[OFDM_STATE_STR];
+ int i, j, sync_counter_thresh;
+
+ strcpy(next_state, ofdm->sync_state);
+ ofdm->sync_start = ofdm->sync_end = 0;
+
+ if (strcmp(ofdm->sync_state,"searching") == 0) {
+
+ if (ofdm->timing_valid) {
+
+ /* freq offset est has some bias, but this refinement step fixes bias */
+
+ int st = OFDM_M + OFDM_NCP + OFDM_SAMPLESPERFRAME;
+ int en = st + 2*OFDM_SAMPLESPERFRAME;
+ float woff_est = TAU * ofdm->foff_est_hz / OFDM_FS;
+
+ complex float work[(en - st)];
+
+ for (i = st, j = 0; i < en; i++, j++) {
+ work[j] = ofdm->rxbuf[i] * cexpf(-I * woff_est * i);
+ }
+
+ float foff_est;
+ coarse_sync(ofdm, work, (en - st), &foff_est);
+ if (ofdm->verbose) {
+ fprintf(stderr, " coarse_foff: %4.1f refine: %4.1f combined: %4.1f\n", ofdm->foff_est_hz, foff_est, ofdm->foff_est_hz+foff_est);
+ }
+ ofdm->foff_est_hz += foff_est;
+ ofdm->frame_count = 0;
+ ofdm->sync_counter = 0;
+ ofdm->sync_start = 1;
+ strcpy(next_state, "trial_sync");
+ }
+ }
+
+ if ((strcmp(ofdm->sync_state,"synced") == 0) || (strcmp(ofdm->sync_state, "trial_sync") == 0)) {
+
+ ofdm->frame_count++;
+
+ /* during trial sync we don't tolerate errors so much, once we have synced up
+ we are willing to wait out a fade */
+
+ if (ofdm->frame_count == 3) {
+ strcpy(next_state, "synced");
+ }
+
+ if (strcmp(ofdm->sync_state, "synced") == 0) {
+ sync_counter_thresh = 6;
+ } else {
+ sync_counter_thresh = 3;
+ }
+
+ /* freq offset est may be too far out, and has aliases every 1/Ts, so
+ we use a Unique Word to get a really solid indication of sync. */
+
+ ofdm->uw_errors = 0;
+ for (i=0; i<OFDM_UW_LEN; i++) {
+ ofdm->uw_errors += rx_uw[i];
+ }
+ if (ofdm->uw_errors > 2) {
+ ofdm->sync_counter++;
+ }
+ if (ofdm->sync_counter == sync_counter_thresh) {
+ strcpy(next_state, "searching");
+ } else {
+ ofdm->sync_counter = 0;
+ }
+ }
+
+ strcpy(ofdm->last_sync_state, ofdm->sync_state);
+ strcpy(ofdm->sync_state, next_state);
+}
+
#define TAU (2.0f * M_PI) /* mathematical constant */
-#define OFDM_NC 16 /* N Carriers */
+#define OFDM_NC 17 /* N Carriers */
#define OFDM_TS 0.018f /* Symbol time */
#define OFDM_RS (1.0f / OFDM_TS) /* Symbol rate */
#define OFDM_FS 8000.0f /* Sample rate */
#define OFDM_NCP ((int)(OFDM_TCP * OFDM_FS))
#endif
-/* ? */
+/* number of symbols we estimate fine timing over */
#define OFDM_FTWINDOWWIDTH 11
/* Bits per frame (duh) */
#define OFDM_BITSPERFRAME ((OFDM_NS - 1) * (OFDM_NC * OFDM_BPS))
#define OFDM_MAX_SAMPLESPERFRAME (OFDM_SAMPLESPERFRAME + (OFDM_M + OFDM_NCP)/4)
#define OFDM_RXBUF (3 * OFDM_SAMPLESPERFRAME + 3 * (OFDM_M + OFDM_NCP))
-#define OFDM_TIMING_MX_THRESH 0.3
+#define OFDM_TIMING_MX_THRESH 0.3
+
+/* reserve 4 bits/frame for auxillary text information */
+
+#define OFDM_TXT_LEN 4
+
+/* Unique word, used for positive indication of lock */
+
+#define OFDM_UW_LEN ((OFDM_NS-1)*OFDM_BPS - OFDM_TXT_LEN)
+
+#define OFDM_STATE_STR 16
/* Dummy struct for now, will contain constant configuration for OFDM modem */
struct OFDM_CONFIG{
complex float rx_np[OFDM_ROWSPERFRAME * OFDM_NC];
float rx_amp[OFDM_ROWSPERFRAME * OFDM_NC];
float aphase_est_pilot_log[OFDM_ROWSPERFRAME * OFDM_NC];
+
+ /* sync state machine */
+
+ char sync_state[OFDM_STATE_STR];
+ char last_sync_state[OFDM_STATE_STR];
+ int uw_errors;
+ int sync_counter;
+ int frame_count;
+ int sync_start;
+ int sync_end;
};
#ifdef __cplusplus