1 /*
2  * Copyright (c) 2007 - 2015 Joseph Gaeddert
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 //
24 // flexframesync.c
25 //
26 // basic frame synchronizer
27 //
28 
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <math.h>
33 #include <complex.h>
34 #include <assert.h>
35 
36 #include "liquid.internal.h"
37 
38 #define DEBUG_FLEXFRAMESYNC         1
39 #define DEBUG_FLEXFRAMESYNC_PRINT   0
40 #define DEBUG_FILENAME              "flexframesync_internal_debug.m"
41 #define DEBUG_BUFFER_LEN            (2000)
42 
43 #define FLEXFRAMESYNC_ENABLE_EQ     0
44 
45 // push samples through detection stage
46 void flexframesync_execute_seekpn(flexframesync _q,
47                                   float complex _x);
48 
49 // step receiver mixer, matched filter, decimator
50 //  _q      :   frame synchronizer
51 //  _x      :   input sample
52 //  _y      :   output symbol
53 int flexframesync_step(flexframesync   _q,
54                        float complex   _x,
55                        float complex * _y);
56 
57 // push samples through synchronizer, saving received p/n symbols
58 void flexframesync_execute_rxpreamble(flexframesync _q,
59                                       float complex _x);
60 
61 // decode header and reconfigure payload
62 void flexframesync_decode_header(flexframesync _q);
63 
64 // receive header symbols
65 void flexframesync_execute_rxheader(flexframesync _q,
66                                     float complex _x);
67 
68 // receive payload symbols
69 void flexframesync_execute_rxpayload(flexframesync _q,
70                                      float complex _x);
71 
72 static flexframegenprops_s flexframesyncprops_header_default = {
73    FLEXFRAME_H_CRC,
74    FLEXFRAME_H_FEC0,
75    FLEXFRAME_H_FEC1,
76    FLEXFRAME_H_MOD,
77 };
78 
79 // flexframesync object structure
80 struct flexframesync_s {
81     // callback
82     framesync_callback  callback;       // user-defined callback function
83     void *              userdata;       // user-defined data structure
84     framesyncstats_s    framesyncstats; // frame statistic object (synchronizer)
85     framedatastats_s    framedatastats; // frame statistic object (synchronizer)
86 
87     // synchronizer objects
88     unsigned int    m;                  // filter delay (symbols)
89     float           beta;               // filter excess bandwidth factor
90     qdetector_cccf  detector;           // pre-demod detector
91     float           tau_hat;            // fractional timing offset estimate
92     float           dphi_hat;           // carrier frequency offset estimate
93     float           phi_hat;            // carrier phase offset estimate
94     float           gamma_hat;          // channel gain estimate
95     nco_crcf        mixer;              // carrier frequency recovery (coarse)
96     nco_crcf        pll;                // carrier frequency recovery (fine)
97 
98     // timing recovery objects, states
99     firpfb_crcf     mf;                 // matched filter decimator
100     unsigned int    npfb;               // number of filters in symsync
101     int             mf_counter;         // matched filter output timer
102     unsigned int    pfb_index;          // filterbank index
103 #if FLEXFRAMESYNC_ENABLE_EQ
104     eqlms_cccf      equalizer;          // equalizer (trained on p/n sequence)
105 #endif
106 
107     // preamble
108     float complex * preamble_pn;        // known 64-symbol p/n sequence
109     float complex * preamble_rx;        // received p/n symbols
110 
111     // header
112     int             header_soft;        // header performs soft demod
113     float complex * header_sym;         // header symbols with pilots (received)
114     unsigned int    header_sym_len;     // header symbols with pilots (length)
115     qpilotsync      header_pilotsync;   // header demodulator/decoder
116     float complex * header_mod;         // header symbols (received)
117     unsigned int    header_mod_len;     // header symbols (length)
118     qpacketmodem    header_decoder;     // header demodulator/decoder
119     unsigned int    header_user_len;    // length of user-defined array
120     unsigned int    header_dec_len;     // length of header (decoded)
121     unsigned char * header_dec;         // header bytes (decoded)
122     int             header_valid;       // header CRC flag
123 
124     flexframegenprops_s header_props;   // header properties
125 
126     // payload
127     int             payload_soft;       // payload performs soft demod
128     modem           payload_demod;      // payload demod (for phase recovery only)
129     float complex * payload_sym;        // payload symbols (received)
130     unsigned int    payload_sym_len;    // payload symbols (length)
131     qpacketmodem    payload_decoder;    // payload demodulator/decoder
132     unsigned char * payload_dec;        // payload data (bytes)
133     unsigned int    payload_dec_len;    // payload data (length)
134     int             payload_valid;      // payload CRC flag
135 
136     // status variables
137     unsigned int    preamble_counter;   // counter: num of p/n syms received
138     unsigned int    symbol_counter;     // counter: num of symbols received
139     enum {
140         FLEXFRAMESYNC_STATE_DETECTFRAME=0,  // detect frame (seek p/n sequence)
141         FLEXFRAMESYNC_STATE_RXPREAMBLE,     // receive p/n sequence
142         FLEXFRAMESYNC_STATE_RXHEADER,       // receive header data
143         FLEXFRAMESYNC_STATE_RXPAYLOAD,      // receive payload data
144     }               state;                  // receiver state
145 
146 #if DEBUG_FLEXFRAMESYNC
147     int         debug_enabled;          // debugging enabled?
148     int         debug_objects_created;  // debugging objects created?
149     int         debug_qdetector_flush;  // debug: flag to set if we are flushing detector
150     windowcf    debug_x;                // debug: raw input samples
151 #endif
152 };
153 
154 // create flexframesync object
155 //  _callback       :   callback function invoked when frame is received
156 //  _userdata       :   user-defined data object passed to callback
flexframesync_create(framesync_callback _callback,void * _userdata)157 flexframesync flexframesync_create(framesync_callback _callback,
158                                    void *             _userdata)
159 {
160     flexframesync q = (flexframesync) malloc(sizeof(struct flexframesync_s));
161     q->callback = _callback;
162     q->userdata = _userdata;
163     q->m        = 7;    // filter delay (symbols)
164     q->beta     = 0.3f; // excess bandwidth factor
165 
166     unsigned int i;
167 
168     // generate p/n sequence
169     q->preamble_pn = (float complex*) malloc(64*sizeof(float complex));
170     q->preamble_rx = (float complex*) malloc(64*sizeof(float complex));
171     msequence ms = msequence_create(7, 0x0089, 1);
172     for (i=0; i<64; i++) {
173         q->preamble_pn[i] = (msequence_advance(ms) ? M_SQRT1_2 : -M_SQRT1_2);
174         q->preamble_pn[i] += (msequence_advance(ms) ? M_SQRT1_2 : -M_SQRT1_2) * _Complex_I;
175     }
176     msequence_destroy(ms);
177 
178     // create frame detector
179     unsigned int k = 2; // samples/symbol
180     q->detector = qdetector_cccf_create_linear(q->preamble_pn, 64, LIQUID_FIRFILT_ARKAISER, k, q->m, q->beta);
181     qdetector_cccf_set_threshold(q->detector, 0.5f);
182 
183     // create symbol timing recovery filters
184     q->npfb = 32;   // number of filters in the bank
185     q->mf   = firpfb_crcf_create_rnyquist(LIQUID_FIRFILT_ARKAISER, q->npfb,k,q->m,q->beta);
186 
187 #if FLEXFRAMESYNC_ENABLE_EQ
188     // create equalizer
189     unsigned int p = 3;
190     q->equalizer = eqlms_cccf_create_lowpass(2*k*p+1, 0.4f);
191     eqlms_cccf_set_bw(q->equalizer, 0.05f);
192 #endif
193 
194     // create down-coverters for carrier phase tracking
195     q->mixer = nco_crcf_create(LIQUID_NCO);
196     q->pll   = nco_crcf_create(LIQUID_NCO);
197     nco_crcf_pll_set_bandwidth(q->pll, 1e-4f); // very low bandwidth
198 
199     // header demodulator/decoder
200     q->header_sym = NULL;
201     q->header_mod = NULL;
202     q->header_dec = NULL;
203     q->header_pilotsync = NULL;
204     q->header_decoder = NULL;
205     q->header_user_len = FLEXFRAME_H_USER_DEFAULT;
206     q->header_soft = 0;
207     flexframesync_set_header_props(q, NULL);
208 
209     // payload demodulator for phase recovery
210     q->payload_demod = modem_create(LIQUID_MODEM_QPSK);
211 
212     // create payload demodulator/decoder object
213     q->payload_dec_len = 64;
214     int check      = LIQUID_CRC_24;
215     int fec0       = LIQUID_FEC_NONE;
216     int fec1       = LIQUID_FEC_GOLAY2412;
217     int mod_scheme = LIQUID_MODEM_BPSK;
218     q->payload_decoder = qpacketmodem_create();
219     qpacketmodem_configure(q->payload_decoder, q->payload_dec_len, check, fec0, fec1, mod_scheme);
220     //qpacketmodem_print(q->payload_decoder);
221     //assert( qpacketmodem_get_frame_len(q->payload_decoder)==600 );
222     q->payload_sym_len = qpacketmodem_get_frame_len(q->payload_decoder);
223 
224     // allocate memory for payload symbols and recovered data bytes
225     q->payload_sym = (float complex*) malloc(q->payload_sym_len*sizeof(float complex));
226     q->payload_dec = (unsigned char*) malloc(q->payload_dec_len*sizeof(unsigned char));
227     q->payload_soft = 0;
228 
229     // reset global data counters
230     flexframesync_reset_framedatastats(q);
231 
232 #if DEBUG_FLEXFRAMESYNC
233     // set debugging flags, objects to NULL
234     q->debug_enabled         = 0;
235     q->debug_objects_created = 0;
236     q->debug_qdetector_flush = 0;
237     q->debug_x               = NULL;
238 #endif
239 
240     // reset state and return
241     flexframesync_reset(q);
242     return q;
243 }
244 
245 // destroy frame synchronizer object, freeing all internal memory
flexframesync_destroy(flexframesync _q)246 void flexframesync_destroy(flexframesync _q)
247 {
248 #if DEBUG_FLEXFRAMESYNC
249     // clean up debug objects (if created)
250     if (_q->debug_objects_created)
251         windowcf_destroy(_q->debug_x);
252 #endif
253 
254     // free allocated arrays
255     free(_q->preamble_pn);
256     free(_q->preamble_rx);
257     free(_q->header_sym);
258     free(_q->header_mod);
259     free(_q->header_dec);
260     free(_q->payload_sym);
261     free(_q->payload_dec);
262 
263     // destroy synchronization objects
264     qpilotsync_destroy    (_q->header_pilotsync); // header demodulator/decoder
265     qpacketmodem_destroy  (_q->header_decoder);   // header demodulator/decoder
266     modem_destroy         (_q->payload_demod);    // payload demodulator (for PLL)
267     qpacketmodem_destroy  (_q->payload_decoder);  // payload demodulator/decoder
268     qdetector_cccf_destroy(_q->detector);         // frame detector
269     firpfb_crcf_destroy   (_q->mf);               // matched filter
270     nco_crcf_destroy      (_q->mixer);            // oscillator (coarse)
271     nco_crcf_destroy      (_q->pll);              // oscillator (fine)
272 #if FLEXFRAMESYNC_ENABLE_EQ
273     eqlms_cccf_destroy    (_q->equalizer);        // LMS equalizer
274 #endif
275 
276     // free main object memory
277     free(_q);
278 }
279 
280 // print frame synchronizer object internals
flexframesync_print(flexframesync _q)281 void flexframesync_print(flexframesync _q)
282 {
283     printf("flexframesync:\n");
284     framedatastats_print(&_q->framedatastats);
285 }
286 
287 // reset frame synchronizer object
flexframesync_reset(flexframesync _q)288 void flexframesync_reset(flexframesync _q)
289 {
290     // reset binary pre-demod synchronizer
291     qdetector_cccf_reset(_q->detector);
292 
293     // reset carrier recovery objects
294     nco_crcf_reset(_q->mixer);
295     nco_crcf_reset(_q->pll);
296 
297     // reset symbol timing recovery state
298     firpfb_crcf_reset(_q->mf);
299 
300     // reset state
301     _q->state           = FLEXFRAMESYNC_STATE_DETECTFRAME;
302     _q->preamble_counter= 0;
303     _q->symbol_counter  = 0;
304 
305     // reset frame statistics
306     _q->framesyncstats.evm = 0.0f;
307 }
308 
flexframesync_is_frame_open(flexframesync _q)309 int flexframesync_is_frame_open(flexframesync _q)
310 {
311     return (_q->state == FLEXFRAMESYNC_STATE_DETECTFRAME) ? 0 : 1;
312 }
313 
flexframesync_set_header_len(flexframesync _q,unsigned int _len)314 void flexframesync_set_header_len(flexframesync _q,
315                                   unsigned int  _len)
316 {
317     _q->header_user_len = _len;
318     _q->header_dec_len = FLEXFRAME_H_DEC + _q->header_user_len;
319     _q->header_dec     = (unsigned char *) realloc(_q->header_dec, _q->header_dec_len*sizeof(unsigned char));
320     if (_q->header_decoder) {
321         qpacketmodem_destroy(_q->header_decoder);
322     }
323     _q->header_decoder = qpacketmodem_create();
324     qpacketmodem_configure(_q->header_decoder,
325                            _q->header_dec_len,
326                            _q->header_props.check,
327                            _q->header_props.fec0,
328                            _q->header_props.fec1,
329                            _q->header_props.mod_scheme);
330     _q->header_mod_len = qpacketmodem_get_frame_len(_q->header_decoder);
331     _q->header_mod     = (float complex*) realloc(_q->header_mod, _q->header_mod_len*sizeof(float complex));
332 
333     // header pilot synchronizer
334     if (_q->header_pilotsync) {
335         qpilotsync_destroy(_q->header_pilotsync);
336     }
337     _q->header_pilotsync = qpilotsync_create(_q->header_mod_len, 16);
338     _q->header_sym_len   = qpilotsync_get_frame_len(_q->header_pilotsync);
339     _q->header_sym       = (float complex*) realloc(_q->header_sym, _q->header_sym_len*sizeof(float complex));
340 }
341 
flexframesync_decode_header_soft(flexframesync _q,int _soft)342 void flexframesync_decode_header_soft(flexframesync _q,
343                                       int           _soft)
344 {
345     _q->header_soft = _soft;
346 }
347 
flexframesync_decode_payload_soft(flexframesync _q,int _soft)348 void flexframesync_decode_payload_soft(flexframesync _q,
349                                        int           _soft)
350 {
351     _q->payload_soft = _soft;
352 }
353 
flexframesync_set_header_props(flexframesync _q,flexframegenprops_s * _props)354 int flexframesync_set_header_props(flexframesync          _q,
355                                    flexframegenprops_s * _props)
356 {
357     if (_props == NULL) {
358         _props = &flexframesyncprops_header_default;
359     }
360 
361     // validate input
362     if (_props->check == LIQUID_CRC_UNKNOWN || _props->check >= LIQUID_CRC_NUM_SCHEMES) {
363         fprintf(stderr, "error: flexframesync_set_header_props(), invalid/unsupported CRC scheme\n");
364         exit(1);
365     } else if (_props->fec0 == LIQUID_FEC_UNKNOWN || _props->fec1 == LIQUID_FEC_UNKNOWN) {
366         fprintf(stderr, "error: flexframesync_set_header_props(), invalid/unsupported FEC scheme\n");
367         exit(1);
368     } else if (_props->mod_scheme == LIQUID_MODEM_UNKNOWN ) {
369         fprintf(stderr, "error: flexframesync_set_header_props(), invalid/unsupported modulation scheme\n");
370         exit(1);
371     }
372 
373     // copy properties to internal structure
374     memmove(&_q->header_props, _props, sizeof(flexframegenprops_s));
375 
376     // reconfigure payload buffers (reallocate as necessary)
377     flexframesync_set_header_len(_q, _q->header_user_len);
378 
379     return 0;
380 }
381 
382 // execute frame synchronizer
383 //  _q  :   frame synchronizer object
384 //  _x  :   input sample array [size: _n x 1]
385 //  _n  :   number of input samples
flexframesync_execute(flexframesync _q,float complex * _x,unsigned int _n)386 void flexframesync_execute(flexframesync   _q,
387                            float complex * _x,
388                            unsigned int    _n)
389 {
390     unsigned int i;
391     for (i=0; i<_n; i++) {
392 #if DEBUG_FLEXFRAMESYNC
393         // write samples to debug buffer
394         // NOTE: the debug_qdetector_flush prevents samples from being written twice
395         if (_q->debug_enabled && !_q->debug_qdetector_flush)
396             windowcf_push(_q->debug_x, _x[i]);
397 #endif
398         switch (_q->state) {
399         case FLEXFRAMESYNC_STATE_DETECTFRAME:
400             // detect frame (look for p/n sequence)
401             flexframesync_execute_seekpn(_q, _x[i]);
402             break;
403         case FLEXFRAMESYNC_STATE_RXPREAMBLE:
404             // receive p/n sequence symbols
405             flexframesync_execute_rxpreamble(_q, _x[i]);
406             break;
407         case FLEXFRAMESYNC_STATE_RXHEADER:
408             // receive header symbols
409             flexframesync_execute_rxheader(_q, _x[i]);
410             break;
411         case FLEXFRAMESYNC_STATE_RXPAYLOAD:
412             // receive payload symbols
413             flexframesync_execute_rxpayload(_q, _x[i]);
414             break;
415         default:
416             fprintf(stderr,"error: flexframesync_exeucte(), unknown/unsupported state\n");
417             exit(1);
418         }
419     }
420 }
421 
422 //
423 // internal methods
424 //
425 
426 // execute synchronizer, seeking p/n sequence
427 //  _q      :   frame synchronizer object
428 //  _x      :   input sample
429 //  _sym    :   demodulated symbol
flexframesync_execute_seekpn(flexframesync _q,float complex _x)430 void flexframesync_execute_seekpn(flexframesync _q,
431                                   float complex _x)
432 {
433     // push through pre-demod synchronizer
434     float complex * v = qdetector_cccf_execute(_q->detector, _x);
435 
436     // check if frame has been detected
437     if (v == NULL)
438         return;
439 
440     // get estimates
441     _q->tau_hat   = qdetector_cccf_get_tau  (_q->detector);
442     _q->gamma_hat = qdetector_cccf_get_gamma(_q->detector);
443     _q->dphi_hat  = qdetector_cccf_get_dphi (_q->detector);
444     _q->phi_hat   = qdetector_cccf_get_phi  (_q->detector);
445 
446 #if DEBUG_FLEXFRAMESYNC_PRINT
447     printf("***** frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n",
448             _q->tau_hat, _q->dphi_hat, 20*log10f(_q->gamma_hat));
449 #endif
450 
451     // set appropriate filterbank index
452     if (_q->tau_hat > 0) {
453         _q->pfb_index = (unsigned int)(      _q->tau_hat  * _q->npfb) % _q->npfb;
454         _q->mf_counter = 0;
455     } else {
456         _q->pfb_index = (unsigned int)((1.0f+_q->tau_hat) * _q->npfb) % _q->npfb;
457         _q->mf_counter = 1;
458     }
459 
460     // output filter scale (gain estimate, scaled by 1/2 for k=2 samples/symbol)
461     firpfb_crcf_set_scale(_q->mf, 0.5f / _q->gamma_hat);
462 
463     // set frequency/phase of mixer
464     nco_crcf_set_frequency(_q->mixer, _q->dphi_hat);
465     nco_crcf_set_phase    (_q->mixer, _q->phi_hat );
466 
467     // update state
468     _q->state = FLEXFRAMESYNC_STATE_RXPREAMBLE;
469 
470 #if DEBUG_FLEXFRAMESYNC
471     // the debug_qdetector_flush prevents samples from being written twice
472     _q->debug_qdetector_flush = 1;
473 #endif
474     // run buffered samples through synchronizer
475     unsigned int buf_len = qdetector_cccf_get_buf_len(_q->detector);
476     flexframesync_execute(_q, v, buf_len);
477 #if DEBUG_FLEXFRAMESYNC
478     _q->debug_qdetector_flush = 0;
479 #endif
480 }
481 
482 // step receiver mixer, matched filter, decimator
483 //  _q      :   frame synchronizer
484 //  _x      :   input sample
485 //  _y      :   output symbol
flexframesync_step(flexframesync _q,float complex _x,float complex * _y)486 int flexframesync_step(flexframesync   _q,
487                        float complex   _x,
488                        float complex * _y)
489 {
490     // mix sample down
491     float complex v;
492     nco_crcf_mix_down(_q->mixer, _x, &v);
493     nco_crcf_step    (_q->mixer);
494 
495     // push sample into filterbank
496     firpfb_crcf_push   (_q->mf, v);
497     firpfb_crcf_execute(_q->mf, _q->pfb_index, &v);
498 
499 #if FLEXFRAMESYNC_ENABLE_EQ
500     // push sample through equalizer
501     eqlms_cccf_push(_q->equalizer, v);
502 #endif
503 
504     // increment counter to determine if sample is available
505     _q->mf_counter++;
506     int sample_available = (_q->mf_counter >= 1) ? 1 : 0;
507 
508     // set output sample if available
509     if (sample_available) {
510 #if FLEXFRAMESYNC_ENABLE_EQ
511         // compute equalizer output
512         eqlms_cccf_execute(_q->equalizer, &v);
513 #endif
514 
515         // set output
516         *_y = v;
517 
518         // decrement counter by k=2 samples/symbol
519         _q->mf_counter -= 2;
520     }
521 
522     // return flag
523     return sample_available;
524 }
525 
526 // execute synchronizer, receiving p/n sequence
527 //  _q     :   frame synchronizer object
528 //  _x      :   input sample
529 //  _sym    :   demodulated symbol
flexframesync_execute_rxpreamble(flexframesync _q,float complex _x)530 void flexframesync_execute_rxpreamble(flexframesync _q,
531                                       float complex _x)
532 {
533     // step synchronizer
534     float complex mf_out = 0.0f;
535     int sample_available = flexframesync_step(_q, _x, &mf_out);
536 
537     // compute output if timeout
538     if (sample_available) {
539 
540         // save output in p/n symbols buffer
541 #if FLEXFRAMESYNC_ENABLE_EQ
542         unsigned int delay = 2*_q->m + 3; // delay from matched filter and equalizer
543 #else
544         unsigned int delay = 2*_q->m;     // delay from matched filter
545 #endif
546         if (_q->preamble_counter >= delay) {
547             unsigned int index = _q->preamble_counter-delay;
548 
549             _q->preamble_rx[index] = mf_out;
550 
551 #if FLEXFRAMESYNC_ENABLE_EQ
552             // train equalizer
553             eqlms_cccf_step(_q->equalizer, _q->preamble_pn[index], mf_out);
554 #endif
555         }
556 
557         // update p/n counter
558         _q->preamble_counter++;
559 
560         // update state
561         if (_q->preamble_counter == 64 + delay)
562             _q->state = FLEXFRAMESYNC_STATE_RXHEADER;
563     }
564 }
565 
566 // execute synchronizer, receiving header
567 //  _q      :   frame synchronizer object
568 //  _x      :   input sample
569 //  _sym    :   demodulated symbol
flexframesync_execute_rxheader(flexframesync _q,float complex _x)570 void flexframesync_execute_rxheader(flexframesync _q,
571                                     float complex _x)
572 {
573     // step synchronizer
574     float complex mf_out = 0.0f;
575     int sample_available = flexframesync_step(_q, _x, &mf_out);
576 
577     // compute output if timeout
578     if (sample_available) {
579         // save payload symbols (modem input/output)
580         _q->header_sym[_q->symbol_counter] = mf_out;
581 
582         // increment counter
583         _q->symbol_counter++;
584 
585         if (_q->symbol_counter == _q->header_sym_len) {
586             // decode header
587             flexframesync_decode_header(_q);
588 
589             if (_q->header_valid) {
590                 // continue on to decoding payload
591                 _q->symbol_counter = 0;
592                 _q->state = FLEXFRAMESYNC_STATE_RXPAYLOAD;
593                 return;
594             }
595 
596             // update statistics
597             _q->framedatastats.num_frames_detected++;
598 
599             // header invalid: invoke callback
600             if (_q->callback != NULL) {
601                 // set framestats internals
602                 _q->framesyncstats.evm           = 0.0f; //20*log10f(sqrtf(_q->framesyncstats.evm / 600));
603                 _q->framesyncstats.rssi          = 20*log10f(_q->gamma_hat);
604                 _q->framesyncstats.cfo           = nco_crcf_get_frequency(_q->mixer);
605                 _q->framesyncstats.framesyms     = NULL;
606                 _q->framesyncstats.num_framesyms = 0;
607                 _q->framesyncstats.mod_scheme    = LIQUID_MODEM_UNKNOWN;
608                 _q->framesyncstats.mod_bps       = 0;
609                 _q->framesyncstats.check         = LIQUID_CRC_UNKNOWN;
610                 _q->framesyncstats.fec0          = LIQUID_FEC_UNKNOWN;
611                 _q->framesyncstats.fec1          = LIQUID_FEC_UNKNOWN;
612 
613                 // invoke callback method
614                 _q->callback(_q->header_dec,
615                              _q->header_valid,
616                              NULL,  // payload
617                              0,     // payload length
618                              0,     // payload valid,
619                              _q->framesyncstats,
620                              _q->userdata);
621             }
622 
623             // reset frame synchronizer
624             flexframesync_reset(_q);
625             return;
626         }
627     }
628 }
629 
630 // decode header
flexframesync_decode_header(flexframesync _q)631 void flexframesync_decode_header(flexframesync _q)
632 {
633     // recover data symbols from pilots
634     qpilotsync_execute(_q->header_pilotsync, _q->header_sym, _q->header_mod);
635 
636     // decode payload
637     if (_q->header_soft) {
638         _q->header_valid = qpacketmodem_decode_soft(_q->header_decoder,
639                                                     _q->header_mod,
640                                                     _q->header_dec);
641     } else {
642         _q->header_valid = qpacketmodem_decode(_q->header_decoder,
643                                                _q->header_mod,
644                                                _q->header_dec);
645     }
646 
647     if (!_q->header_valid)
648         return;
649 
650     // set fine carrier frequency and phase
651     float dphi_hat = qpilotsync_get_dphi(_q->header_pilotsync);
652     float  phi_hat = qpilotsync_get_phi (_q->header_pilotsync);
653     //printf("residual offset: dphi=%12.8f, phi=%12.8f\n", dphi_hat, phi_hat);
654     nco_crcf_set_frequency(_q->pll, dphi_hat);
655     nco_crcf_set_phase    (_q->pll, phi_hat + dphi_hat * _q->header_sym_len);
656 
657     // first several bytes of header are user-defined
658     unsigned int n = _q->header_user_len;
659 
660     // first byte is for expansion/version validation
661     unsigned int protocol = _q->header_dec[n+0];
662     if (protocol != FLEXFRAME_PROTOCOL) {
663         fprintf(stderr,"warning: flexframesync_decode_header(), invalid framing protocol %u (expected %u)\n", protocol, FLEXFRAME_PROTOCOL);
664         _q->header_valid = 0;
665         return;
666     }
667 
668     // strip off payload length
669     unsigned int payload_dec_len = (_q->header_dec[n+1] << 8) | (_q->header_dec[n+2]);
670     _q->payload_dec_len = payload_dec_len;
671 
672     // strip off modulation scheme/depth
673     unsigned int mod_scheme = _q->header_dec[n+3];
674 
675     // strip off CRC, forward error-correction schemes
676     //  CRC     : most-significant 3 bits of [n+4]
677     //  fec0    : least-significant 5 bits of [n+4]
678     //  fec1    : least-significant 5 bits of [n+5]
679     unsigned int check = (_q->header_dec[n+4] >> 5 ) & 0x07;
680     unsigned int fec0  = (_q->header_dec[n+4]      ) & 0x1f;
681     unsigned int fec1  = (_q->header_dec[n+5]      ) & 0x1f;
682 
683     // validate properties
684     if (mod_scheme == 0 || mod_scheme >= LIQUID_MODEM_NUM_SCHEMES) {
685         fprintf(stderr,"warning: flexframesync_decode_header(), invalid modulation scheme\n");
686         _q->header_valid = 0;
687         return;
688     } else if (check == LIQUID_CRC_UNKNOWN || check >= LIQUID_CRC_NUM_SCHEMES) {
689         fprintf(stderr,"warning: flexframesync_decode_header(), decoded CRC exceeds available\n");
690         _q->header_valid = 0;
691         return;
692     } else if (fec0 == LIQUID_FEC_UNKNOWN || fec0 >= LIQUID_FEC_NUM_SCHEMES) {
693         fprintf(stderr,"warning: flexframesync_decode_header(), decoded FEC (inner) exceeds available\n");
694         _q->header_valid = 0;
695         return;
696     } else if (fec1 == LIQUID_FEC_UNKNOWN || fec1 >= LIQUID_FEC_NUM_SCHEMES) {
697         fprintf(stderr,"warning: flexframesync_decode_header(), decoded FEC (outer) exceeds available\n");
698         _q->header_valid = 0;
699         return;
700     }
701 
702     // re-create payload demodulator for phase-locked loop
703     _q->payload_demod = modem_recreate(_q->payload_demod, mod_scheme);
704 
705     // reconfigure payload demodulator/decoder
706     qpacketmodem_configure(_q->payload_decoder,
707                            payload_dec_len, check, fec0, fec1, mod_scheme);
708 
709     // set length appropriately
710     _q->payload_sym_len = qpacketmodem_get_frame_len(_q->payload_decoder);
711 
712     // re-allocate buffers accordingly
713     _q->payload_sym = (float complex*) realloc(_q->payload_sym, (_q->payload_sym_len)*sizeof(float complex));
714     _q->payload_dec = (unsigned char*) realloc(_q->payload_dec, (_q->payload_dec_len)*sizeof(unsigned char));
715 
716     if (_q->payload_sym == NULL || _q->payload_dec == NULL) {
717         fprintf(stderr,"error: flexframesync_decode_header(), could not re-allocate payload arrays\n");
718         _q->header_valid = 0;
719         return;
720     }
721 
722 #if DEBUG_FLEXFRAMESYNC_PRINT
723     // print results
724     printf("flexframesync_decode_header():\n");
725     printf("    header crc      : %s\n", _q->header_valid ? "pass" : "FAIL");
726     printf("    check           : %s\n", crc_scheme_str[check][1]);
727     printf("    fec (inner)     : %s\n", fec_scheme_str[fec0][1]);
728     printf("    fec (outer)     : %s\n", fec_scheme_str[fec1][1]);
729     printf("    mod scheme      : %s\n", modulation_types[mod_scheme].name);
730     printf("    payload sym len : %u\n", _q->payload_sym_len);
731     printf("    payload dec len : %u\n", _q->payload_dec_len);
732     printf("    user data       :");
733     unsigned int i;
734     for (i=0; i<_q->header_user_len; i++)
735         printf(" %.2x", _q->header_dec[i]);
736     printf("\n");
737 #endif
738 }
739 
740 
741 // execute synchronizer, receiving payload
742 //  _q      :   frame synchronizer object
743 //  _x      :   input sample
744 //  _sym    :   demodulated symbol
flexframesync_execute_rxpayload(flexframesync _q,float complex _x)745 void flexframesync_execute_rxpayload(flexframesync _q,
746                                      float complex _x)
747 {
748     // step synchronizer
749     float complex mf_out = 0.0f;
750     int sample_available = flexframesync_step(_q, _x, &mf_out);
751 
752     // compute output if timeout
753     if (sample_available) {
754         // TODO: clean this up
755         // mix down with fine-tuned oscillator
756         nco_crcf_mix_down(_q->pll, mf_out, &mf_out);
757         // track phase, accumulate error-vector magnitude
758         unsigned int sym;
759         modem_demodulate(_q->payload_demod, mf_out, &sym);
760         float phase_error = modem_get_demodulator_phase_error(_q->payload_demod);
761         float evm         = modem_get_demodulator_evm        (_q->payload_demod);
762         nco_crcf_pll_step(_q->pll, phase_error);
763         nco_crcf_step(_q->pll);
764         _q->framesyncstats.evm += evm*evm;
765 
766         // save payload symbols (modem input/output)
767         _q->payload_sym[_q->symbol_counter] = mf_out;
768 
769         // increment counter
770         _q->symbol_counter++;
771 
772         if (_q->symbol_counter == _q->payload_sym_len) {
773             // decode payload
774             if (_q->payload_soft) {
775                 _q->payload_valid = qpacketmodem_decode_soft(_q->payload_decoder,
776                                                              _q->payload_sym,
777                                                              _q->payload_dec);
778             } else {
779                 _q->payload_valid = qpacketmodem_decode(_q->payload_decoder,
780                                                         _q->payload_sym,
781                                                         _q->payload_dec);
782             }
783 
784             // update statistics
785             _q->framedatastats.num_frames_detected++;
786             _q->framedatastats.num_headers_valid++;
787             _q->framedatastats.num_payloads_valid += _q->payload_valid;
788             _q->framedatastats.num_bytes_received += _q->payload_dec_len;
789 
790             // invoke callback
791             if (_q->callback != NULL) {
792                 // set framestats internals
793                 int ms = qpacketmodem_get_modscheme(_q->payload_decoder);
794                 _q->framesyncstats.evm           = 10*log10f(_q->framesyncstats.evm / (float)_q->payload_sym_len);
795                 _q->framesyncstats.rssi          = 20*log10f(_q->gamma_hat);
796                 _q->framesyncstats.cfo           = nco_crcf_get_frequency(_q->mixer);
797                 _q->framesyncstats.framesyms     = _q->payload_sym;
798                 _q->framesyncstats.num_framesyms = _q->payload_sym_len;
799                 _q->framesyncstats.mod_scheme    = ms;
800                 _q->framesyncstats.mod_bps       = modulation_types[ms].bps;
801                 _q->framesyncstats.check         = qpacketmodem_get_crc(_q->payload_decoder);
802                 _q->framesyncstats.fec0          = qpacketmodem_get_fec0(_q->payload_decoder);
803                 _q->framesyncstats.fec1          = qpacketmodem_get_fec1(_q->payload_decoder);
804 
805                 // invoke callback method
806                 _q->callback(_q->header_dec,
807                              _q->header_valid,
808                              _q->payload_dec,
809                              _q->payload_dec_len,
810                              _q->payload_valid,
811                              _q->framesyncstats,
812                              _q->userdata);
813             }
814 
815             // reset frame synchronizer
816             flexframesync_reset(_q);
817             return;
818         }
819     }
820 }
821 
822 // reset frame data statistics
flexframesync_reset_framedatastats(flexframesync _q)823 void flexframesync_reset_framedatastats(flexframesync _q)
824 {
825     framedatastats_reset(&_q->framedatastats);
826 }
827 
828 // retrieve frame data statistics
flexframesync_get_framedatastats(flexframesync _q)829 framedatastats_s flexframesync_get_framedatastats(flexframesync _q)
830 {
831     return _q->framedatastats;
832 }
833 
834 // enable debugging
flexframesync_debug_enable(flexframesync _q)835 void flexframesync_debug_enable(flexframesync _q)
836 {
837     // create debugging objects if necessary
838 #if DEBUG_FLEXFRAMESYNC
839     if (_q->debug_objects_created)
840         return;
841 
842     // create debugging objects
843     _q->debug_x = windowcf_create(DEBUG_BUFFER_LEN);
844 
845     // set debugging flags
846     _q->debug_enabled = 1;
847     _q->debug_objects_created = 1;
848 #else
849     fprintf(stderr,"flexframesync_debug_enable(): compile-time debugging disabled\n");
850 #endif
851 }
852 
853 // disable debugging
flexframesync_debug_disable(flexframesync _q)854 void flexframesync_debug_disable(flexframesync _q)
855 {
856     // disable debugging
857 #if DEBUG_FLEXFRAMESYNC
858     _q->debug_enabled = 0;
859 #else
860     fprintf(stderr,"flexframesync_debug_enable(): compile-time debugging disabled\n");
861 #endif
862 }
863 
864 // print debugging information
flexframesync_debug_print(flexframesync _q,const char * _filename)865 void flexframesync_debug_print(flexframesync _q,
866                                const char *  _filename)
867 {
868 #if DEBUG_FLEXFRAMESYNC
869     if (!_q->debug_objects_created) {
870         fprintf(stderr,"error: flexframesync_debug_print(), debugging objects don't exist; enable debugging first\n");
871         return;
872     }
873     unsigned int i;
874     float complex * rc;
875     FILE* fid = fopen(_filename,"w");
876     fprintf(fid,"%% %s: auto-generated file", _filename);
877     fprintf(fid,"\n\n");
878     fprintf(fid,"clear all;\n");
879     fprintf(fid,"close all;\n\n");
880     fprintf(fid,"n = %u;\n", DEBUG_BUFFER_LEN);
881 
882     // main figure
883     fprintf(fid,"figure('Color','white','position',[100 100 800 600]);\n");
884 
885     // write x
886     fprintf(fid,"x = zeros(1,n);\n");
887     windowcf_read(_q->debug_x, &rc);
888     for (i=0; i<DEBUG_BUFFER_LEN; i++)
889         fprintf(fid,"x(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(rc[i]), cimagf(rc[i]));
890     fprintf(fid,"\n\n");
891     fprintf(fid,"subplot(3,2,1:2);\n");
892     fprintf(fid,"plot(1:length(x),real(x), 1:length(x),imag(x));\n");
893     fprintf(fid,"grid on;\n");
894     fprintf(fid,"xlabel('sample index');\n");
895     fprintf(fid,"ylabel('received signal, x');\n");
896 
897     // write p/n sequence
898     fprintf(fid,"preamble_pn = zeros(1,64);\n");
899     rc = _q->preamble_pn;
900     for (i=0; i<64; i++)
901         fprintf(fid,"preamble_pn(%4u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(rc[i]), cimagf(rc[i]));
902 
903     // write p/n symbols
904     fprintf(fid,"preamble_rx = zeros(1,64);\n");
905     rc = _q->preamble_rx;
906     for (i=0; i<64; i++)
907         fprintf(fid,"preamble_rx(%4u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(rc[i]), cimagf(rc[i]));
908 
909     // write recovered header symbols (after qpilotsync)
910     fprintf(fid,"header_mod = zeros(1,%u);\n", _q->header_mod_len);
911     rc = _q->header_mod;
912     for (i=0; i<_q->header_mod_len; i++)
913         fprintf(fid,"header_mod(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(rc[i]), cimagf(rc[i]));
914 
915     // write raw payload symbols
916     fprintf(fid,"payload_sym = zeros(1,%u);\n", _q->payload_sym_len);
917     rc = _q->payload_sym;
918     for (i=0; i<_q->payload_sym_len; i++)
919         fprintf(fid,"payload_sym(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(rc[i]), cimagf(rc[i]));
920 
921     fprintf(fid,"subplot(3,2,[3 5]);\n");
922     fprintf(fid,"plot(real(header_mod),imag(header_mod),'o');\n");
923     fprintf(fid,"xlabel('in-phase');\n");
924     fprintf(fid,"ylabel('quadrature phase');\n");
925     fprintf(fid,"grid on;\n");
926     fprintf(fid,"axis([-1 1 -1 1]*1.5);\n");
927     fprintf(fid,"axis square;\n");
928     fprintf(fid,"title('Received Header Symbols');\n");
929 
930     fprintf(fid,"subplot(3,2,[4 6]);\n");
931     fprintf(fid,"plot(real(payload_sym),imag(payload_sym),'+');\n");
932     fprintf(fid,"xlabel('in-phase');\n");
933     fprintf(fid,"ylabel('quadrature phase');\n");
934     fprintf(fid,"grid on;\n");
935     fprintf(fid,"axis([-1 1 -1 1]*1.5);\n");
936     fprintf(fid,"axis square;\n");
937     fprintf(fid,"title('Received Payload Symbols');\n");
938 
939     fprintf(fid,"\n\n");
940     fclose(fid);
941 
942     printf("flexframesync/debug: results written to %s\n", _filename);
943 #else
944     fprintf(stderr,"flexframesync_debug_print(): compile-time debugging disabled\n");
945 #endif
946 }
947 
948