1 /*---------------------------------------------------------------------------*\
2 
3   FILE........: tofdm.c
4   AUTHORS.....: David Rowe & Steve Sampson
5   DATE CREATED: June 2017
6 
7   Tests for the C version of the OFDM modem.  This program outputs a
8   file of Octave vectors that are loaded and automatically tested
9   against the Octave version of the modem by the Octave script tofdm.m
10 
11 \*---------------------------------------------------------------------------*/
12 
13 /*
14   Copyright (C) 2017 David Rowe
15 
16   All rights reserved.
17 
18   This program is free software; you can redistribute it and/or modify
19   it under the terms of the GNU Lesser General Public License version 2, as
20   published by the Free Software Foundation.  This program is
21   distributed in the hope that it will be useful, but WITHOUT ANY
22   WARRANTY; without even the implied warranty of MERCHANTABILITY or
23   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
24   License for more details.
25 
26   You should have received a copy of the GNU Lesser General Public License
27   along with this program; if not, see <http://www.gnu.org/licenses/>.
28 */
29 
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <math.h>
36 #include <complex.h>
37 #include <getopt.h>
38 
39 #include "ofdm_internal.h"
40 #include "codec2_ofdm.h"
41 #include "octave.h"
42 #include "test_bits_ofdm.h"
43 #include "comp_prim.h"
44 #include "mpdecode_core.h"
45 
46 #include "HRA_112_112.h"          /* generated by ldpc_fsk_lib.m:ldpc_decode() */
47 
48 #define NFRAMES                   10
49 #define SAMPLE_CLOCK_OFFSET_PPM 100
50 #define FOFF_HZ                 0.5f
51 
52 #define ASCALE  (2E5f * 1.1491f / 2.0f)  /* scale from shorts back to floats */
53 
54 #define CODED_BITSPERFRAME 224    /* number of LDPC codeword bits/frame   */
55 
56 /* QPSK constellation for symbol likelihood calculations */
57 
58 static COMP S_matrix[] = {
59     { 1.0f,  0.0f},
60     { 0.0f,  1.0f},
61     { 0.0f, -1.0f},
62     {-1.0f,  0.0f}
63 };
64 
65 /* constants we use a lot and don't want to have to deference all the time  */
66 
67 static float ofdm_tx_centre;        /* TX Center frequency */
68 static float ofdm_rx_centre;        /* RX Center frequency */
69 static float ofdm_fs;               /* Sample rate */
70 static float ofdm_ts;               /* Symbol cycle time */
71 static float ofdm_rs;               /* Symbol rate */
72 static float ofdm_tcp;              /* Cyclic prefix duration */
73 static float ofdm_timing_mx_thresh; /* See 700D Part 4 Acquisition blog post and ofdm_dev.m routines for how this was set */
74 
75 static int ofdm_nc;                 /* NS-1 data symbols between pilots  */
76 static int ofdm_ns;
77 static int ofdm_bps;                /* Bits per symbol */
78 static int ofdm_m;                  /* duration of each symbol in samples */
79 static int ofdm_ncp;                /* duration of CP in samples */
80 
81 static int ofdm_ftwindowwidth;
82 static int ofdm_bitsperframe;
83 static int ofdm_rowsperframe;
84 static int ofdm_samplesperframe;
85 static int ofdm_samplespersymbol;
86 static int ofdm_max_samplesperframe;
87 static int ofdm_nrxbuf;
88 static int ofdm_ntxtbits;           /* reserve bits/frame for auxillary text information */
89 static int ofdm_nuwbits;            /* Unique word, used for positive indication of lock */
90 
91 /*---------------------------------------------------------------------------*\
92 
93   FUNCTION....: fs_offset()
94   AUTHOR......: David Rowe
95   DATE CREATED: May 2015
96 
97   Simulates small Fs offset between mod and demod.
98   (Note: Won't work with float, works OK with double)
99 
100 \*---------------------------------------------------------------------------*/
101 
fs_offset(COMP out[],COMP in[],int n,float sample_rate_ppm)102 static int fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) {
103     double f;
104     double tin = 0.0;
105     double step = 1.0 + sample_rate_ppm/1E6;
106     int t1, t2;
107     int tout = 0;
108 
109     while (tin < (double) (n-1)) {
110       t1 = (int) floor(tin);
111       t2 = (int) ceil(tin);
112       assert(t2 < n);
113       f = tin - (double) t1;
114 
115       out[tout].real = ((double)1.0-f)*(double)in[t1].real + f*(double)in[t2].real;
116       out[tout].imag = ((double)1.0-f)*(double)in[t1].imag + f*(double)in[t2].imag;
117 
118       tin += step;
119       tout++;
120       //printf("tin: %f tout: %d f: %f\n", tin, tout, f);
121     }
122 
123     return tout;
124 }
125 
126 /*---------------------------------------------------------------------------*\
127 
128   FUNCTION....: freq_shift()
129   AUTHOR......: David Rowe
130   DATE CREATED: 26/4/2012
131 
132   Frequency shift modem signal.  The use of complex input and output allows
133   single sided frequency shifting (no images).
134 
135 \*---------------------------------------------------------------------------*/
136 
freq_shift(COMP rx_fdm_fcorr[],COMP rx_fdm[],float foff,COMP * foff_phase_rect,int nin)137 static void freq_shift(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, COMP *foff_phase_rect, int nin) {
138     float temp = (TAU * foff / ofdm_fs);
139     COMP  foff_rect = { cosf(temp), sinf(temp) };
140     int   i;
141 
142     for (i = 0; i < nin; i++) {
143 	*foff_phase_rect = cmult(*foff_phase_rect, foff_rect);
144 	rx_fdm_fcorr[i] = cmult(rx_fdm[i], *foff_phase_rect);
145     }
146 
147     /* normalise digital oscillator as the magnitude can drift over time */
148 
149     float mag = cabsolute(*foff_phase_rect);
150     foff_phase_rect->real /= mag;
151     foff_phase_rect->imag /= mag;
152 }
153 
main(int argc,char * argv[])154 int main(int argc, char *argv[])
155 {
156     int opt_Nc = 0;
157     int ldpc_enable = 1;
158     struct OFDM *ofdm;
159     struct OFDM_CONFIG *ofdm_config;
160 
161     static struct option long_options[] = {
162         {"nc",       required_argument, 0, 'n'},
163         {"noldpc",   no_argument, 0, 'l'},
164         {0, 0, 0, 0}
165     };
166 
167     int opt_index = 0; char c;
168 
169     while ((c = getopt_long (argc, argv, "n:l", long_options, &opt_index)) != -1) {
170         switch (c) {
171         case 'n':
172             opt_Nc = atoi(optarg);
173             fprintf(stderr, "Nc = %d\n", opt_Nc);
174             break;
175         case 'l':
176             ldpc_enable = 0;
177             fprintf(stderr, "LDPC disabled\n");
178             break;
179         default:
180             fprintf(stderr,"usage: %s [Options]:\n  [-l --noldpc]\n  [-n --nc NumberoFCarriers]\n", argv[0]);
181             exit(1);
182         }
183     }
184 
185     // init once to get a copy of default config params
186 
187     ofdm = ofdm_create(NULL);
188     assert(ofdm != NULL);
189     struct OFDM_CONFIG ofdm_config_default;
190     memcpy(&ofdm_config_default, ofdm_get_config_param(ofdm), sizeof(struct OFDM_CONFIG));
191     ofdm_destroy(ofdm);
192 
193     // now do a little customisation on default config, and re-create modem instance
194 
195     if (opt_Nc)
196        ofdm_config_default.nc = opt_Nc;
197     //printf("ofdm_create() 2\n");
198     ofdm = ofdm_create(&ofdm_config_default);
199     assert(ofdm != NULL);
200     ofdm_config = ofdm_get_config_param(ofdm);
201     ofdm_set_tx_bpf(ofdm, false);
202 
203     // same levels as Octave sim
204     ofdm->amp_scale = 1.0;
205 
206     // make local copies for convenience
207     ofdm_tx_centre = ofdm_config->tx_centre;
208     ofdm_rx_centre = ofdm_config->rx_centre;
209     ofdm_fs = ofdm_config->fs;
210     ofdm_ts = ofdm_config->ts;
211     ofdm_rs = ofdm_config->rs;
212     ofdm_tcp = ofdm_config->tcp;
213     ofdm_timing_mx_thresh = ofdm_config->timing_mx_thresh;
214     ofdm_nc = ofdm_config->nc;
215     ofdm_ns = ofdm_config->ns;
216     ofdm_bps = ofdm_config->bps;
217     ofdm_m = (int) (ofdm_config->fs / ofdm_config->rs);
218     ofdm_ncp = (int) (ofdm_config->tcp * ofdm_config->fs);
219     ofdm_ftwindowwidth = ofdm_config->ftwindowwidth;
220     ofdm_bitsperframe = ofdm_get_bits_per_frame(ofdm);
221     ofdm_rowsperframe = ofdm_bitsperframe / (ofdm_config->nc * ofdm_config->bps);
222     ofdm_samplesperframe = ofdm_get_samples_per_frame(ofdm);
223     ofdm_samplespersymbol = (ofdm->m + ofdm->ncp);
224     ofdm_max_samplesperframe = ofdm_get_max_samples_per_frame(ofdm);
225     ofdm_nrxbuf = ofdm->nrxbuf;
226     ofdm_ntxtbits = ofdm_config->txtbits;
227     ofdm_nuwbits = ofdm_config->nuwbits;
228 
229     int tx_bits[ofdm_samplesperframe];
230     COMP tx[ofdm_samplesperframe];         /* one frame of tx samples */
231 
232     int rx_bits[ofdm_bitsperframe];    /* one frame of rx bits    */
233     printf("Nc = %d ofdm_bitsperframe: %d\n", ofdm_nc, ofdm_bitsperframe);
234 
235     /* log arrays */
236 
237     int tx_bits_log[ofdm_bitsperframe*NFRAMES];
238     COMP tx_log[ofdm_samplesperframe*NFRAMES];
239     COMP rx_log[ofdm_samplesperframe*NFRAMES];
240     COMP rxbuf_in_log[ofdm_max_samplesperframe*NFRAMES];
241     COMP rxbuf_log[ofdm_nrxbuf*NFRAMES];
242     COMP rx_sym_log[(ofdm_ns + 3)*NFRAMES][ofdm_nc + 2];
243     float phase_est_pilot_log[ofdm_rowsperframe*NFRAMES][ofdm_nc];
244     COMP rx_np_log[ofdm_rowsperframe*ofdm_nc*NFRAMES];
245     float rx_amp_log[ofdm_rowsperframe*ofdm_nc*NFRAMES];
246     float foff_hz_log[NFRAMES];
247     int rx_bits_log[ofdm_bitsperframe*NFRAMES];
248     int timing_est_log[NFRAMES];
249     int timing_valid_log[NFRAMES];
250     float timing_mx_log[NFRAMES];
251     float coarse_foff_est_hz_log[NFRAMES];
252     int sample_point_log[NFRAMES];
253     float symbol_likelihood_log[ (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) * NFRAMES];
254     float bit_likelihood_log[CODED_BITSPERFRAME * NFRAMES];
255     int detected_data_log[CODED_BITSPERFRAME * NFRAMES];
256     float mean_amp_log[NFRAMES];
257     float snr_log[NFRAMES];
258 
259     FILE *fout;
260     int f,i,j;
261 
262     /* set up LDPC code */
263 
264     struct LDPC   ldpc;
265 
266     ldpc.max_iter = HRA_112_112_MAX_ITER;
267     ldpc.dec_type = 0;
268     ldpc.q_scale_factor = 1;
269     ldpc.r_scale_factor = 1;
270     ldpc.CodeLength = HRA_112_112_CODELENGTH;
271     ldpc.NumberParityBits = HRA_112_112_NUMBERPARITYBITS;
272     ldpc.NumberRowsHcols = HRA_112_112_NUMBERROWSHCOLS;
273     ldpc.max_row_weight = HRA_112_112_MAX_ROW_WEIGHT;
274     ldpc.max_col_weight = HRA_112_112_MAX_COL_WEIGHT;
275     ldpc.H_rows = (uint16_t *)HRA_112_112_H_rows;
276     ldpc.H_cols = (uint16_t *)HRA_112_112_H_cols;
277 
278     /* Main Loop ---------------------------------------------------------------------*/
279 
280     for(f=0; f<NFRAMES; f++) {
281 
282        	/* --------------------------------------------------------*\
283 	                          Mod
284 	      \*---------------------------------------------------------*/
285 
286         /* See CML startup code in tofdm.m */
287 
288         for(i=0; i<ofdm_nuwbits; i++) {
289             tx_bits[i] = ofdm->tx_uw[i];
290         }
291         for(i=ofdm_nuwbits; i<ofdm_nuwbits+ofdm_ntxtbits; i++) {
292             tx_bits[i] = 0;
293         }
294 
295         if (ldpc_enable) {
296             unsigned char ibits[HRA_112_112_NUMBERROWSHCOLS];
297             unsigned char pbits[HRA_112_112_NUMBERPARITYBITS];
298 
299             assert(HRA_112_112_NUMBERROWSHCOLS == ldpc.CodeLength/2);
300             for(i=0; i<ldpc.CodeLength/2; i++) {
301                 ibits[i] = payload_data_bits[i];
302             }
303             encode(&ldpc, ibits, pbits);
304             for(j=0, i=ofdm_nuwbits+ofdm_ntxtbits; j<ldpc.CodeLength/2; i++,j++) {
305                 tx_bits[i] = ibits[j];
306             }
307             for(j=0; j<ldpc.CodeLength/2; i++,j++) {
308                 tx_bits[i] = pbits[j];
309             }
310             assert(i == ofdm_bitsperframe);
311         } else {
312             int Npayloadbits = ofdm_bitsperframe - (ofdm_nuwbits + ofdm_ntxtbits);
313             uint16_t r[Npayloadbits];
314             uint8_t payload_bits[Npayloadbits];
315 
316             ofdm_rand(r, Npayloadbits);
317             for (i = 0; i < Npayloadbits; i++)
318                 payload_bits[i] = r[i] > 16384;
319             uint8_t txt_bits[ofdm_ntxtbits];
320             for (i = 0; i < ofdm_ntxtbits; i++)
321                 txt_bits[i] = 0;
322 
323             uint8_t tx_bits_char[ofdm_bitsperframe];
324             ofdm_assemble_qpsk_modem_packet(ofdm, tx_bits_char, payload_bits, txt_bits);
325             for(i=0; i<ofdm_bitsperframe; i++)
326                 tx_bits[i] = tx_bits_char[i];
327         }
328 
329         ofdm_mod(ofdm, (COMP*)tx, tx_bits);
330 
331         /* tx vector logging */
332 
333 	memcpy(&tx_bits_log[ofdm_bitsperframe*f], tx_bits, sizeof(int)*ofdm_bitsperframe);
334 	memcpy(&tx_log[ofdm_samplesperframe*f], tx, sizeof(COMP)*ofdm_samplesperframe);
335     }
336 
337     /* --------------------------------------------------------*\
338 	                        Channel
339     \*---------------------------------------------------------*/
340 
341     fs_offset(rx_log, tx_log, ofdm_samplesperframe*NFRAMES, SAMPLE_CLOCK_OFFSET_PPM);
342 
343     COMP foff_phase_rect = {1.0f, 0.0f};
344 
345     freq_shift(rx_log, rx_log, FOFF_HZ, &foff_phase_rect, ofdm_samplesperframe * NFRAMES);
346 
347     /* --------------------------------------------------------*\
348 	                        Demod
349     \*---------------------------------------------------------*/
350 
351     /* Init/pre-load rx with ideal timing so we can test with timing estimation disabled */
352 
353     int Nsam = ofdm_samplesperframe*NFRAMES;
354     int prx = 0;
355     int nin = ofdm_samplesperframe + 2*ofdm_samplespersymbol;
356 
357     int  lnew;
358     COMP rxbuf_in[ofdm_max_samplesperframe];
359 
360 #define FRONT_LOAD
361 #ifdef FRONT_LOAD
362     for (i=0; i<nin; i++,prx++) {
363          ofdm->rxbuf[ofdm_nrxbuf-nin+i] = rx_log[prx].real + rx_log[prx].imag * I;
364     }
365 #endif
366 
367     int nin_tot = 0;
368 
369     /* disable estimators for initial testing */
370 
371     ofdm_set_verbose(ofdm, false);
372     ofdm_set_timing_enable(ofdm, true);
373     ofdm_set_foff_est_enable(ofdm, true);
374     ofdm_set_phase_est_enable(ofdm, true);
375 
376 //#define TESTING_FILE
377 #ifdef TESTING_FILE
378     FILE *fin=fopen("~/codec2-dev/octave/ofdm_test.raw", "rb");
379     assert(fin != NULL);
380     int Nbitsperframe = ofdm_bitsperframe;
381     int Nmaxsamperframe = ofdm_max_samplesperframe;
382     short rx_scaled[Nmaxsamperframe];
383 #endif
384 
385     /* start this with something sensible otherwise LDPC decode fails in tofdm.m */
386 
387     ofdm->mean_amp = 1.0;
388 
389     for(f=0; f<NFRAMES; f++) {
390         /* For initial testing, timing est is off, so nin is always
391            fixed.  TODO: we need a constant for rxbuf_in[] size that
392            is the maximum possible nin */
393 
394         nin = ofdm_get_nin(ofdm);
395         assert(nin <= ofdm_max_samplesperframe);
396 
397         /* Insert samples at end of buffer, set to zero if no samples
398            available to disable phase estimation on future pilots on
399            last frame of simulation. */
400 
401         if ((Nsam-prx) < nin) {
402             lnew = Nsam-prx;
403         } else {
404             lnew = nin;
405         }
406         //printf("nin: %d prx: %d lnew: %d\n", nin, prx, lnew);
407         for(i=0; i<nin; i++) {
408             rxbuf_in[i].real = 0.0;
409             rxbuf_in[i].imag = 0.0;
410         }
411 
412         if (lnew) {
413             for(i=0; i<lnew; i++, prx++) {
414                 rxbuf_in[i] = rx_log[prx];
415             }
416         }
417         assert(prx <= ofdm_max_samplesperframe*NFRAMES);
418 
419 #ifdef TESTING_FILE
420         fread(rx_scaled, sizeof(short), nin, fin);
421 
422         for(i=0; i<nin; i++) {
423 	    rxbuf_in[i].real = (float)rx_scaled[i]/ASCALE;
424             rxbuf_in[i].imag = 0.0;
425         }
426 #endif
427 
428         /* uncoded OFDM modem ---------------------------------------*/
429 
430         ofdm_demod(ofdm, rx_bits, rxbuf_in);
431 
432 #ifdef TESTING_FILE
433         int Nerrs = 0;
434         for(i=0; i<Nbitsperframe; i++) {
435             if (test_bits_ofdm[i] != rx_bits[i]) {
436                 Nerrs++;
437             }
438         }
439         printf("f: %d Nerr: %d\n", f, Nerrs);
440 #endif
441 
442         float symbol_likelihood[ (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) ];
443         float bit_likelihood[CODED_BITSPERFRAME];
444         uint8_t out_char[CODED_BITSPERFRAME];
445 
446         if (ldpc_enable) {
447             /* LDPC functions --------------------------------------*/
448 
449             float EsNo = 10;
450 
451             /* first few symbols are used for UW and txt bits, find start of (224,112) LDPC codeword */
452 
453             assert((ofdm_nuwbits+ofdm_ntxtbits+CODED_BITSPERFRAME) == ofdm_bitsperframe);
454 
455             COMP ldpc_codeword_symbols[(CODED_BITSPERFRAME/ofdm_bps)];
456 
457             for(i=0, j=(ofdm_nuwbits+ofdm_ntxtbits)/ofdm_bps; i<(CODED_BITSPERFRAME/ofdm_bps); i++,j++) {
458                 ldpc_codeword_symbols[i].real = crealf(ofdm->rx_np[j]);
459                 ldpc_codeword_symbols[i].imag = cimagf(ofdm->rx_np[j]);
460             }
461 
462             float *ldpc_codeword_symbol_amps = &ofdm->rx_amp[(ofdm_nuwbits+ofdm_ntxtbits)/ofdm_bps];
463 
464             Demod2D(symbol_likelihood, ldpc_codeword_symbols, S_matrix, EsNo, ldpc_codeword_symbol_amps, ofdm->mean_amp, CODED_BITSPERFRAME/ofdm_bps);
465             Somap(bit_likelihood, symbol_likelihood, 1<<ofdm_bps, ofdm_bps, CODED_BITSPERFRAME/ofdm_bps);
466 
467             float  llr[CODED_BITSPERFRAME];
468             int    parityCheckCount;
469 
470 
471             // fprintf(stderr, "\n");
472             for(i=0; i<CODED_BITSPERFRAME; i++) {
473                 llr[i] = -bit_likelihood[i];
474                 // fprintf(stderr, "%f ", llr[i]);
475             }
476 
477             //fprintf(stderr, "\n");
478 
479             run_ldpc_decoder(&ldpc, out_char, llr, &parityCheckCount);
480             /*
481               fprintf(stderr, "iter: %d parityCheckCount: %d\n", iter, parityCheckCount);
482               for(i=0; i<CODED_BITSPERFRAME; i++) {
483               fprintf(stderr, "%d ", out_char[i]);
484               }
485             */
486         }
487 
488         /* rx vector logging -----------------------------------*/
489 
490         assert(nin_tot < ofdm_samplesperframe*NFRAMES);
491 	memcpy(&rxbuf_in_log[nin_tot], rxbuf_in, sizeof(COMP)*nin);
492         nin_tot += nin;
493 
494         for(i=0; i<ofdm_nrxbuf; i++) {
495             rxbuf_log[ofdm_nrxbuf*f+i].real = crealf(ofdm->rxbuf[i]);
496             rxbuf_log[ofdm_nrxbuf*f+i].imag = cimagf(ofdm->rxbuf[i]);
497         }
498 
499         for (i = 0; i < (ofdm_ns + 3); i++) {
500             for (j = 0; j < (ofdm_nc + 2); j++) {
501                 rx_sym_log[(ofdm_ns + 3)*f+i][j].real = crealf(ofdm->rx_sym[i][j]);
502                 rx_sym_log[(ofdm_ns + 3)*f+i][j].imag = cimagf(ofdm->rx_sym[i][j]);
503             }
504         }
505 
506         /* note corrected phase (rx no phase) is one big linear array for frame */
507 
508         for (i = 0; i < ofdm_rowsperframe*ofdm_nc; i++) {
509             rx_np_log[ofdm_rowsperframe*ofdm_nc*f + i].real = crealf(ofdm->rx_np[i]);
510             rx_np_log[ofdm_rowsperframe*ofdm_nc*f + i].imag = cimagf(ofdm->rx_np[i]);
511         }
512 
513         /* note phase/amp ests the same for each col, but check them all anyway */
514 
515         for (i = 0; i < ofdm_rowsperframe; i++) {
516             for (j = 0; j < ofdm_nc; j++) {
517                 phase_est_pilot_log[ofdm_rowsperframe*f+i][j] = ofdm->aphase_est_pilot_log[ofdm_nc*i+j];
518                 rx_amp_log[ofdm_rowsperframe*ofdm_nc*f+ofdm_nc*i+j] = ofdm->rx_amp[ofdm_nc*i+j];
519             }
520         }
521 
522         foff_hz_log[f] = ofdm->foff_est_hz;
523         timing_est_log[f] = ofdm->timing_est + 1;     /* offset by 1 to match Octave */
524         timing_valid_log[f] = ofdm->timing_valid;
525         timing_mx_log[f] = ofdm->timing_mx;
526         coarse_foff_est_hz_log[f] = ofdm->coarse_foff_est_hz;
527         sample_point_log[f] = ofdm->sample_point + 1; /* offset by 1 to match Octave */
528         float EsNodB = ofdm_esno_est_calc(ofdm->rx_np, ofdm_rowsperframe*ofdm_nc);
529         snr_log[f] = ofdm_snr_from_esno(ofdm, EsNodB);
530         mean_amp_log[f] = ofdm->mean_amp;
531 
532         memcpy(&rx_bits_log[ofdm_bitsperframe*f], rx_bits, sizeof(rx_bits));
533 
534         if (ldpc_enable) {
535             for(i=0; i<(CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps); i++) {
536                 symbol_likelihood_log[ (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) * f + i] = symbol_likelihood[i];
537             }
538             for(i=0; i<CODED_BITSPERFRAME; i++) {
539                 bit_likelihood_log[CODED_BITSPERFRAME*f + i] =  bit_likelihood[i];
540                 detected_data_log[CODED_BITSPERFRAME*f + i] = out_char[i];
541             }
542         }
543     }
544 
545     /*---------------------------------------------------------*\
546                Dump logs to Octave file for evaluation
547                       by tofdm.m Octave script
548     \*---------------------------------------------------------*/
549 
550     fout = fopen("tofdm_out.txt","wt");
551     assert(fout != NULL);
552     fprintf(fout, "# Created by tofdm.c\n");
553     octave_save_complex(fout, "pilot_samples_c", (COMP*)ofdm->pilot_samples, 1, ofdm_samplespersymbol, ofdm_samplespersymbol);
554     octave_save_int(fout, "tx_bits_log_c", tx_bits_log, 1, ofdm_bitsperframe*NFRAMES);
555     octave_save_complex(fout, "tx_log_c", (COMP*)tx_log, 1, ofdm_samplesperframe*NFRAMES,  ofdm_samplesperframe*NFRAMES);
556     octave_save_complex(fout, "rx_log_c", (COMP*)rx_log, 1, ofdm_samplesperframe*NFRAMES,  ofdm_samplesperframe*NFRAMES);
557     octave_save_complex(fout, "rxbuf_in_log_c", (COMP*)rxbuf_in_log, 1, nin_tot, nin_tot);
558     octave_save_complex(fout, "rxbuf_log_c", (COMP*)rxbuf_log, 1, ofdm_nrxbuf*NFRAMES,  ofdm_nrxbuf*NFRAMES);
559     octave_save_complex(fout, "rx_sym_log_c", (COMP*)rx_sym_log, (ofdm_ns + 3)*NFRAMES, ofdm_nc + 2, ofdm_nc + 2);
560     octave_save_float(fout, "phase_est_pilot_log_c", (float*)phase_est_pilot_log, ofdm_rowsperframe*NFRAMES, ofdm_nc, ofdm_nc);
561     octave_save_float(fout, "rx_amp_log_c", (float*)rx_amp_log, 1, ofdm_rowsperframe*ofdm_nc*NFRAMES, ofdm_rowsperframe*ofdm_nc*NFRAMES);
562     octave_save_float(fout, "foff_hz_log_c", foff_hz_log, NFRAMES, 1, 1);
563     octave_save_int(fout, "timing_est_log_c", timing_est_log, NFRAMES, 1);
564     octave_save_int(fout, "timing_valid_log_c", timing_valid_log, NFRAMES, 1);
565     octave_save_float(fout, "timing_mx_log_c", timing_mx_log, NFRAMES, 1, 1);
566     octave_save_float(fout, "coarse_foff_est_hz_log_c", coarse_foff_est_hz_log, NFRAMES, 1, 1);
567     octave_save_int(fout, "sample_point_log_c", sample_point_log, NFRAMES, 1);
568     octave_save_complex(fout, "rx_np_log_c", (COMP*)rx_np_log, 1, ofdm_rowsperframe*ofdm_nc*NFRAMES, ofdm_rowsperframe*ofdm_nc*NFRAMES);
569     octave_save_int(fout, "rx_bits_log_c", rx_bits_log, 1, ofdm_bitsperframe*NFRAMES);
570     octave_save_float(fout, "symbol_likelihood_log_c", symbol_likelihood_log, (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) * NFRAMES, 1, 1);
571     octave_save_float(fout, "bit_likelihood_log_c", bit_likelihood_log, CODED_BITSPERFRAME * NFRAMES, 1, 1);
572     octave_save_int(fout, "detected_data_log_c", detected_data_log, 1, CODED_BITSPERFRAME*NFRAMES);
573     octave_save_float(fout, "snr_log_c", snr_log, NFRAMES, 1, 1);
574     octave_save_float(fout, "mean_amp_log_c", mean_amp_log, NFRAMES, 1, 1);
575     fclose(fout);
576 #ifdef TESTING_FILE
577     fclose(fin);
578 #endif
579 
580     ofdm_destroy(ofdm);
581 
582     return 0;
583 }
584