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