From 5191a9b7d4e30a5875d118adba24ca68090baf32 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Oct 2022 10:08:16 -0400 Subject: [PATCH 001/186] qdsync: adding new object to support both detection and tracking --- examples/qdsync_cccf_example.c | 86 +++++++++++++++++++ include/liquid.h | 46 ++++++++++ makefile.in | 2 + src/framing/src/qdsync_cccf.c | 150 +++++++++++++++++++++++++++++++++ 4 files changed, 284 insertions(+) create mode 100644 examples/qdsync_cccf_example.c create mode 100644 src/framing/src/qdsync_cccf.c diff --git a/examples/qdsync_cccf_example.c b/examples/qdsync_cccf_example.c new file mode 100644 index 000000000..12db10063 --- /dev/null +++ b/examples/qdsync_cccf_example.c @@ -0,0 +1,86 @@ +// This example demonstrates the functionality of the qdsync object to +// detect and synchronize an arbitrary signal in time in the presence of noise, +// carrier frequency/phase offsets, and fractional-sample timing offsets. +// offsets. +#include +#include +#include +#include +#include +#include +#include "liquid.h" + +#define OUTPUT_FILENAME "qdsync_cccf_example.m" + +// synchronization callback, return 0:continue, 1:reset +int callback(float complex * _buf, + unsigned int _buf_len, + void * _context) +{ printf("callback got %u samples\n", _buf_len); return 0; } + +int main(int argc, char*argv[]) +{ + // options + unsigned int sequence_len = 80; // number of sync symbols + unsigned int k = 2; // samples/symbol + unsigned int m = 7; // filter delay [symbols] + float beta = 0.3f; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + float nstd = 0.1f; + + unsigned int i; + + // generate synchronization sequence (QPSK symbols) + unsigned int seq_len = k*(sequence_len + 2*m); + float complex seq[seq_len]; + firinterp_crcf interp = firinterp_crcf_create_prototype(ftype,k,m,beta,0); + for (i=0; i +#include +#include +#include +#include + +#include "liquid.internal.h" + +// main object definition +struct qdsync_cccf_s { + qdsync_callback callback; // + void * context; // + qdetector_cccf detector; // detector + // resampler +}; + +// create detector with generic sequence +// _s : sample sequence +// _s_len : length of sample sequence +qdsync_cccf qdsync_cccf_create(float complex * _s, + unsigned int _s_len, + qdsync_callback _callback, + void * _context) +{ + // validate input + if (_s_len == 0) + return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); + + // allocate memory for main object and set internal properties + qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); + + // create detector + q->detector = qdetector_cccf_create(_s, _s_len); + + // set callback and context values + qdsync_cccf_set_callback(q, _callback); + qdsync_cccf_set_context (q, _context ); + + // reset and return object + qdsync_cccf_reset(q); + return q; +} + +// copy object +qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); + + // create new object and copy base parameters + qdsync_cccf q_copy = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); + memmove(q_copy, q_orig, sizeof(struct qdsync_cccf_s)); + + // copy sub-objects + q_copy->detector = qdetector_cccf_copy(q_orig->detector); + + // return new object + return q_copy; +} + +int qdsync_cccf_destroy(qdsync_cccf _q) +{ + // destroy internal objects + qdetector_cccf_destroy(_q->detector); + + // free main object memory + free(_q); + return LIQUID_OK; +} + +int qdsync_cccf_print(qdsync_cccf _q) +{ + printf("\n"); + return LIQUID_OK; +} + +int qdsync_cccf_reset(qdsync_cccf _q) +{ + return LIQUID_OK; +} + +int qdsync_cccf_execute(qdsync_cccf _q, + liquid_float_complex * _buf, + unsigned int _buf_len) +{ + // TODO: switch based on state + unsigned int i; + for (i=0; i<_buf_len; i++) { + void * p = qdetector_cccf_execute(_q->detector, _buf[i]); + if (p != NULL) + _q->callback(NULL, 0, _q->context); + } + return LIQUID_OK; +} + +// get detection threshold +float qdsync_cccf_get_threshold(qdsync_cccf _q) +{ + return qdetector_cccf_get_threshold(_q->detector); +} + +// set detection threshold +int qdsync_cccf_set_threshold(qdsync_cccf _q, + float _threshold) +{ + return qdetector_cccf_set_threshold(_q->detector, _threshold); +} + +// set callback method +int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback) +{ + _q->callback = _callback; + return LIQUID_OK; +} + +// set context value +int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) +{ + _q->context = _context; + return LIQUID_OK; +} + From 3777df4bdb3f9880341a8476353199eb2346b8af Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Oct 2022 11:33:01 -0400 Subject: [PATCH 002/186] qdsync: putting basic hooks to pass information back to caller --- examples/qdsync_cccf_example.c | 27 +++++++++++++++------------ src/framing/src/qdsync_cccf.c | 28 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/examples/qdsync_cccf_example.c b/examples/qdsync_cccf_example.c index 12db10063..5c98ac5b5 100644 --- a/examples/qdsync_cccf_example.c +++ b/examples/qdsync_cccf_example.c @@ -16,7 +16,13 @@ int callback(float complex * _buf, unsigned int _buf_len, void * _context) -{ printf("callback got %u samples\n", _buf_len); return 0; } +{ + printf("callback got %u samples\n", _buf_len); + unsigned int i; + for (i=0; i<_buf_len; i++) + fprintf((FILE*)_context, "y(end+1) = %12.8f + %12.8fj;\n", crealf(_buf[i]), cimagf(_buf[i])); + return 0; +} int main(int argc, char*argv[]) { @@ -26,7 +32,7 @@ int main(int argc, char*argv[]) unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; - float nstd = 0.1f; + float nstd = 0.01f; unsigned int i; @@ -41,13 +47,18 @@ int main(int argc, char*argv[]) } firinterp_crcf_destroy(interp); + // open file for storing results + FILE * fid = fopen(OUTPUT_FILENAME,"w"); + fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); + fprintf(fid,"clear all; close all; y=[];\n"); + // create sync object - qdsync_cccf q = qdsync_cccf_create(seq, seq_len, callback, NULL); + qdsync_cccf q = qdsync_cccf_create(seq, seq_len, callback, fid); qdsync_cccf_print(q); // float complex buf[seq_len]; - for (i=0; i<200; i++) { + for (i=0; i<20; i++) { if (i==10) memmove(buf, seq, seq_len*sizeof(float complex)); else memset (buf, 0x00, seq_len*sizeof(float complex)); @@ -59,17 +70,9 @@ int main(int argc, char*argv[]) // run through synchronizer qdsync_cccf_execute(q, buf, seq_len); } - qdsync_cccf_destroy(q); // export results - FILE * fid = fopen(OUTPUT_FILENAME,"w"); - fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); - fprintf(fid,"clear all\n"); - fprintf(fid,"close all\n"); - //fprintf(fid,"sequence_len= %u;\n", sequence_len); - //fprintf(fid,"num_samples = %u;\n", num_samples); - fprintf(fid,"s = [];\n"); for (i=0; istate = QDSYNC_STATE_DETECT; return LIQUID_OK; } @@ -113,10 +120,25 @@ int qdsync_cccf_execute(qdsync_cccf _q, { // TODO: switch based on state unsigned int i; + void * p = NULL; for (i=0; i<_buf_len; i++) { - void * p = qdetector_cccf_execute(_q->detector, _buf[i]); - if (p != NULL) - _q->callback(NULL, 0, _q->context); + switch (_q->state) { + case QDSYNC_STATE_DETECT: + p = qdetector_cccf_execute(_q->detector, _buf[i]); + if (p != NULL) { + if (_q->callback != NULL) + _q->callback(NULL, 0, _q->context); + _q->state = QDSYNC_STATE_SYNC; + } + break; + case QDSYNC_STATE_SYNC: + if (_q->callback != NULL) { + _q->callback(_buf, _buf_len, _q->context); + return LIQUID_OK; + } + break; + default:; + } } return LIQUID_OK; } From bb2cef7a782ff3dc125b9611984075ff5c9316cb Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Oct 2022 15:57:35 -0400 Subject: [PATCH 003/186] qdsync: refactoring to work with linear modulated signals --- examples/qdsync_cccf_example.c | 56 ++++---- include/liquid.h | 12 +- src/framing/src/framesync64.c | 2 +- src/framing/src/qdsync_cccf.c | 226 +++++++++++++++++++++++++++++---- 4 files changed, 237 insertions(+), 59 deletions(-) diff --git a/examples/qdsync_cccf_example.c b/examples/qdsync_cccf_example.c index 5c98ac5b5..3b04f02e1 100644 --- a/examples/qdsync_cccf_example.c +++ b/examples/qdsync_cccf_example.c @@ -27,25 +27,20 @@ int callback(float complex * _buf, int main(int argc, char*argv[]) { // options - unsigned int sequence_len = 80; // number of sync symbols + unsigned int seq_len = 80; // number of sync symbols unsigned int k = 2; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float nstd = 0.01f; - unsigned int i; - // generate synchronization sequence (QPSK symbols) - unsigned int seq_len = k*(sequence_len + 2*m); float complex seq[seq_len]; - firinterp_crcf interp = firinterp_crcf_create_prototype(ftype,k,m,beta,0); - for (i=0; imixer = nco_crcf_create(LIQUID_NCO); - + // create payload demodulator/decoder object int check = LIQUID_CRC_24; int fec0 = LIQUID_FEC_NONE; diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 3013a5f3c..ad6c8668b 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -33,37 +33,94 @@ #include "liquid.internal.h" +// push samples through detection stage +int qdsync_cccf_execute_detect(qdsync_cccf _q, + float complex _x); + +// step receiver mixer, matched filter, decimator +// _q : frame synchronizer +// _x : input sample +int qdsync_cccf_step(qdsync_cccf _q, + float complex _x); + +// append sample to output buffer +int qdsync_cccf_buf_append(qdsync_cccf _q, + float complex _x); + // main object definition struct qdsync_cccf_s { + unsigned int seq_len; // preamble sequence length + int ftype; // + unsigned int k; // + unsigned int m; // + float beta; // + qdsync_callback callback; // void * context; // qdetector_cccf detector; // detector + // status variables enum { QDSYNC_STATE_DETECT=0, // detect frame - QDSYNC_STATE_SYNC, // synchronize samples - } state; - // resampler - // nco + QDSYNC_STATE_SYNC, // + } state; // frame synchronization state + unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence + + // estimated offsets + float tau_hat; // + float gamma_hat; // + float dphi_hat; // + float phi_hat; // + + nco_crcf mixer; // coarse carrier frequency recovery + + // timing recovery objects, states + firpfb_crcf mf; // matched filter decimator + unsigned int npfb; // number of filters in symsync + int mf_counter; // matched filter output timer + unsigned int pfb_index; // filterbank index + + // symbol buffer + unsigned int buf_out_len;// output buffer length + float complex * buf_out; // output buffer + unsigned int buf_out_counter; // output counter }; // create detector with generic sequence -// _s : sample sequence -// _s_len : length of sample sequence -qdsync_cccf qdsync_cccf_create(float complex * _s, - unsigned int _s_len, - qdsync_callback _callback, - void * _context) +qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, + unsigned int _seq_len, + int _ftype, + unsigned int _k, + unsigned int _m, + float _beta, + qdsync_callback _callback, + void * _context) { // validate input - if (_s_len == 0) + if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - + // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); + q->seq_len = _seq_len; + q->ftype = _ftype; + q->k = _k; + q->m = _m; + q->beta = _beta; // create detector - q->detector = qdetector_cccf_create(_s, _s_len); + q->detector = qdetector_cccf_create_linear(_seq, _seq_len, _ftype, _k, _m, _beta); + + // create down-coverters for carrier phase tracking + q->mixer = nco_crcf_create(LIQUID_NCO); + + // create symbol timing recovery filters + q->npfb = 256; // number of filters in the bank + q->mf = firpfb_crcf_create_rnyquist(q->ftype, q->npfb, q->k, q->m, q->beta); + + // allocate buffer for storing output samples + q->buf_out_len = 64; // TODO: make user-defined? + q->buf_out = (float complex*) malloc(q->buf_out_len*sizeof(float complex)); // set callback and context values qdsync_cccf_set_callback(q, _callback); @@ -77,6 +134,7 @@ qdsync_cccf qdsync_cccf_create(float complex * _s, // copy object qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) { +#if 0 // validate input if (q_orig == NULL) return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); @@ -90,12 +148,20 @@ qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) // return new object return q_copy; +#else + return liquid_error_config("qdsync_cccf_copy(), method not yet implemented"); +#endif } int qdsync_cccf_destroy(qdsync_cccf _q) { // destroy internal objects qdetector_cccf_destroy(_q->detector); + nco_crcf_destroy(_q->mixer); + firpfb_crcf_destroy(_q->mf); + + // free output buffer + free(_q->buf_out); // free main object memory free(_q); @@ -110,7 +176,11 @@ int qdsync_cccf_print(qdsync_cccf _q) int qdsync_cccf_reset(qdsync_cccf _q) { + qdetector_cccf_reset(_q->detector); _q->state = QDSYNC_STATE_DETECT; + _q->symbol_counter = 0; + _q->buf_out_counter = 0; + firpfb_crcf_reset(_q->mf); return LIQUID_OK; } @@ -118,26 +188,19 @@ int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, unsigned int _buf_len) { - // TODO: switch based on state unsigned int i; - void * p = NULL; for (i=0; i<_buf_len; i++) { switch (_q->state) { case QDSYNC_STATE_DETECT: - p = qdetector_cccf_execute(_q->detector, _buf[i]); - if (p != NULL) { - if (_q->callback != NULL) - _q->callback(NULL, 0, _q->context); - _q->state = QDSYNC_STATE_SYNC; - } + // detect frame (look for p/n sequence) + qdsync_cccf_execute_detect(_q, _buf[i]); break; case QDSYNC_STATE_SYNC: - if (_q->callback != NULL) { - _q->callback(_buf, _buf_len, _q->context); - return LIQUID_OK; - } + // receive preamble sequence symbols + qdsync_cccf_step(_q, _buf[i]); break; - default:; + default: + return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); } } return LIQUID_OK; @@ -170,3 +233,114 @@ int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) return LIQUID_OK; } +// +// internal methods +// + +// execute synchronizer, seeking preamble sequence +// _q : frame synchronizer object +// _x : input sample +// _sym : demodulated symbol +int qdsync_cccf_execute_detect(qdsync_cccf _q, + float complex _x) +{ + // push through pre-demod synchronizer + float complex * v = qdetector_cccf_execute(_q->detector, _x); + + // check if frame has been detected + if (v != NULL) { + // get estimates + _q->tau_hat = qdetector_cccf_get_tau (_q->detector); + _q->gamma_hat = qdetector_cccf_get_gamma(_q->detector); + _q->dphi_hat = qdetector_cccf_get_dphi (_q->detector); + _q->phi_hat = qdetector_cccf_get_phi (_q->detector); + //printf("***** frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", + // _q->tau_hat, _q->dphi_hat, 20*log10f(_q->gamma_hat)); + + // set appropriate filterbank index + if (_q->tau_hat > 0) { + _q->pfb_index = (unsigned int)( _q->tau_hat * _q->npfb) % _q->npfb; + _q->mf_counter = 0; + } else { + _q->pfb_index = (unsigned int)((1.0f+_q->tau_hat) * _q->npfb) % _q->npfb; + _q->mf_counter = 1; + } + + // output filter scale + firpfb_crcf_set_scale(_q->mf, 0.5f / _q->gamma_hat); + + // set frequency/phase of mixer + nco_crcf_set_frequency(_q->mixer, _q->dphi_hat); + nco_crcf_set_phase (_q->mixer, _q->phi_hat ); + + // update state + _q->state = QDSYNC_STATE_SYNC; + + // run buffered samples through synchronizer + unsigned int buf_len = qdetector_cccf_get_buf_len(_q->detector); + qdsync_cccf_execute(_q, v, buf_len); + } + return LIQUID_OK; +} + +// step receiver mixer, matched filter, decimator +// _q : frame synchronizer +// _x : input sample +// _y : output symbol +int qdsync_cccf_step(qdsync_cccf _q, + float complex _x) +{ + // mix sample down + float complex v; + nco_crcf_mix_down(_q->mixer, _x, &v); + nco_crcf_step (_q->mixer); + + // push sample into filterbank + firpfb_crcf_push (_q->mf, v); + firpfb_crcf_execute(_q->mf, _q->pfb_index, &v); + + // increment counter to determine if sample is available + _q->mf_counter++; + int sample_available = (_q->mf_counter >= 1) ? 1 : 0; + + // set output sample if available + if (sample_available) { + // decrement counter by k=2 samples/symbol + _q->mf_counter -= 2; + + // append to output + qdsync_cccf_buf_append(_q, v); + } + + // return flag + return LIQUID_OK; +} + +// append sample to output buffer +int qdsync_cccf_buf_append(qdsync_cccf _q, + float complex _x) +{ + // account for filter delay + _q->symbol_counter++; + if (_q->symbol_counter <= 2*_q->m) + return LIQUID_OK; + + // append sample to end of buffer + _q->buf_out[_q->buf_out_counter] = _x; + _q->buf_out_counter++; + + // check if buffer is full + if (_q->buf_out_counter == _q->buf_out_len) { + // reset counter + _q->buf_out_counter = 0; + + // invoke callback + if (_q->callback != NULL) { + int rc = _q->callback(_q->buf_out, _q->buf_out_len, _q->context); + if (rc) + return qdsync_cccf_reset(_q); + } + } + return LIQUID_OK; +} + From ab8de5f73d00850f23bc463e6c71ff9a1053e9a5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 16 Oct 2022 17:11:45 -0400 Subject: [PATCH 004/186] qdsync: adding method to set carrier offset search range --- include/liquid.h | 4 ++++ src/framing/src/qdsync_cccf.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/include/liquid.h b/include/liquid.h index 82c29670c..9695774b4 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6276,6 +6276,10 @@ float qdsync_cccf_get_threshold(qdsync_cccf _q); // set detection threshold int qdsync_cccf_set_threshold(qdsync_cccf _q, float _threshold); +// set carrier offset search range +int qdsync_cccf_set_range(qdsync_cccf _q, + float _dphi_max); + // set callback method int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index ad6c8668b..d76a45166 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -219,6 +219,13 @@ int qdsync_cccf_set_threshold(qdsync_cccf _q, return qdetector_cccf_set_threshold(_q->detector, _threshold); } +// set carrier offset search range +int qdsync_cccf_set_range(qdsync_cccf _q, + float _dphi_max) +{ + return qdetector_cccf_set_range(_q->detector, _dphi_max); +} + // set callback method int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback) { From 50df9388fa4744139b91c5207ba23b13a043bc1e Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 17 Oct 2022 08:24:24 -0400 Subject: [PATCH 005/186] qdsync: restricting samples per symbol to be just 2 for now --- src/framing/src/qdsync_cccf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index d76a45166..6af86c18f 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -99,6 +99,8 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // validate input if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); + if (_k != 2) + return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); From e96f991ed230de1707c5ee318fdb2c6e7648508f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 19 Oct 2022 07:32:34 -0400 Subject: [PATCH 006/186] qdsync: adding method to determine if object is 'open' (actively receiving) --- include/liquid.h | 3 +++ src/framing/src/qdsync_cccf.c | 49 +++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 9695774b4..527255b62 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6286,6 +6286,9 @@ int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); // set context value int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); +// is synchronizer actively running? +int qdsync_cccf_is_open(qdsync_cccf _q); + // execute block of samples int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 6af86c18f..cabe17f90 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -186,28 +186,6 @@ int qdsync_cccf_reset(qdsync_cccf _q) return LIQUID_OK; } -int qdsync_cccf_execute(qdsync_cccf _q, - liquid_float_complex * _buf, - unsigned int _buf_len) -{ - unsigned int i; - for (i=0; i<_buf_len; i++) { - switch (_q->state) { - case QDSYNC_STATE_DETECT: - // detect frame (look for p/n sequence) - qdsync_cccf_execute_detect(_q, _buf[i]); - break; - case QDSYNC_STATE_SYNC: - // receive preamble sequence symbols - qdsync_cccf_step(_q, _buf[i]); - break; - default: - return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); - } - } - return LIQUID_OK; -} - // get detection threshold float qdsync_cccf_get_threshold(qdsync_cccf _q) { @@ -242,6 +220,33 @@ int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) return LIQUID_OK; } +int qdsync_cccf_execute(qdsync_cccf _q, + liquid_float_complex * _buf, + unsigned int _buf_len) +{ + unsigned int i; + for (i=0; i<_buf_len; i++) { + switch (_q->state) { + case QDSYNC_STATE_DETECT: + // detect frame (look for p/n sequence) + qdsync_cccf_execute_detect(_q, _buf[i]); + break; + case QDSYNC_STATE_SYNC: + // receive preamble sequence symbols + qdsync_cccf_step(_q, _buf[i]); + break; + default: + return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); + } + } + return LIQUID_OK; +} + +int qdsync_cccf_is_open(qdsync_cccf _q) +{ + return _q->state == QDSYNC_STATE_DETECT ? 0 : 1; +} + // // internal methods // From 03164dabeb8a7702dbfb92e6c9eceb79d94f99a1 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 19 Oct 2022 07:42:33 -0400 Subject: [PATCH 007/186] qdsync: adding autotest for detection, payload recovery --- makefile.in | 1 + src/framing/tests/qdsync_cccf_autotest.c | 141 +++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 src/framing/tests/qdsync_cccf_autotest.c diff --git a/makefile.in b/makefile.in index ec6b1cbb5..4d46007c8 100644 --- a/makefile.in +++ b/makefile.in @@ -694,6 +694,7 @@ framing_autotests := \ src/framing/tests/ofdmflexframe_autotest.c \ src/framing/tests/qdetector_cccf_autotest.c \ src/framing/tests/qdetector_cccf_copy_autotest.c \ + src/framing/tests/qdsync_cccf_autotest.c \ src/framing/tests/qpacketmodem_autotest.c \ src/framing/tests/qpilotsync_autotest.c \ src/framing/tests/qsource_autotest.c \ diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c new file mode 100644 index 000000000..a5cbfcdbd --- /dev/null +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2007 - 2018 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include "autotest/autotest.h" +#include "liquid.h" + +// common structure for relaying information to/from callback +typedef struct { + unsigned int seq_len; + float complex * buf; + unsigned int buf_len; + unsigned int count; +} autotest_qdsync_s; + +// synchronization callback, return 0:continue, 1:reset +int autotest_qdsync_callback(float complex * _buf, + unsigned int _buf_len, + void * _context) +{ + // save samples to buffer as appropriate + autotest_qdsync_s * q = (autotest_qdsync_s *) _context; + unsigned int i; + for (i=0; i<_buf_len; i++) { + if (q->count < q->seq_len) { + // preamble sequence + } else if (q->count < q->seq_len + q->buf_len) { + // payload + q->buf[q->count - q->seq_len] = _buf[i]; + } if (q->count == q->seq_len + q->buf_len) { + // buffer full; reset synchronizer + return 1; + } + q->count++; + } + return 0; +} + +void autotest_qdsync() +{ + // options + unsigned int seq_len = 80; // number of sync symbols + unsigned int payload_len = 200; // number of payload symbols + unsigned int k = 2; // samples/symbol + unsigned int m = 7; // filter delay [symbols] + float beta = 0.3f; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + float nstd = 0.001f; + + // generate synchronization sequence (QPSK symbols) + float complex seq[seq_len]; + unsigned int i; + for (i=0; i Date: Thu, 20 Oct 2022 20:48:14 -0400 Subject: [PATCH 008/186] qdsync: adding methods to get metrics, offset estimates --- include/liquid.h | 13 ++++++++++--- src/framing/src/qdsync_cccf.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 527255b62..1af8a9c52 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6286,14 +6286,21 @@ int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); // set context value int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); -// is synchronizer actively running? -int qdsync_cccf_is_open(qdsync_cccf _q); - // execute block of samples int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, unsigned int _buf_len); +// is synchronizer actively running? +int qdsync_cccf_is_open(qdsync_cccf _q); + +// get detection metrics and offsets +float qdsync_cccf_get_rxy (qdsync_cccf _q); // correlator output +float qdsync_cccf_get_tau (qdsync_cccf _q); // fractional timing offset estimate +float qdsync_cccf_get_gamma(qdsync_cccf _q); // channel gain +float qdsync_cccf_get_dphi (qdsync_cccf _q); // carrier frequency offset estimate +float qdsync_cccf_get_phi (qdsync_cccf _q); // carrier phase offset estimate + // // Pre-demodulation detector // diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index cabe17f90..c9737ae11 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -247,6 +247,36 @@ int qdsync_cccf_is_open(qdsync_cccf _q) return _q->state == QDSYNC_STATE_DETECT ? 0 : 1; } +// correlator output +float qdsync_cccf_get_rxy(qdsync_cccf _q) +{ + return qdetector_cccf_get_rxy(_q->detector); +} + +// fractional timing offset estimate +float qdsync_cccf_get_tau(qdsync_cccf _q) +{ + return qdetector_cccf_get_tau(_q->detector); +} + +// channel gain +float qdsync_cccf_get_gamma(qdsync_cccf _q) +{ + return qdetector_cccf_get_gamma(_q->detector); +} + +// carrier frequency offset estimate +float qdsync_cccf_get_dphi(qdsync_cccf _q) +{ + return qdetector_cccf_get_dphi(_q->detector); +} + +// carrier phase offset estimate +float qdsync_cccf_get_phi(qdsync_cccf _q) +{ + return qdetector_cccf_get_phi(_q->detector); +} + // // internal methods // From a8b6415f5ae0c153036088b2a11b3e61f45534e0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 20 Oct 2022 20:52:39 -0400 Subject: [PATCH 009/186] qdsync: removing internal offsets; no need to store extra copy --- src/framing/src/qdsync_cccf.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index c9737ae11..fec0df56e 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -66,12 +66,6 @@ struct qdsync_cccf_s { } state; // frame synchronization state unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence - // estimated offsets - float tau_hat; // - float gamma_hat; // - float dphi_hat; // - float phi_hat; // - nco_crcf mixer; // coarse carrier frequency recovery // timing recovery objects, states @@ -294,28 +288,28 @@ int qdsync_cccf_execute_detect(qdsync_cccf _q, // check if frame has been detected if (v != NULL) { // get estimates - _q->tau_hat = qdetector_cccf_get_tau (_q->detector); - _q->gamma_hat = qdetector_cccf_get_gamma(_q->detector); - _q->dphi_hat = qdetector_cccf_get_dphi (_q->detector); - _q->phi_hat = qdetector_cccf_get_phi (_q->detector); - //printf("***** frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", - // _q->tau_hat, _q->dphi_hat, 20*log10f(_q->gamma_hat)); + float tau_hat = qdetector_cccf_get_tau (_q->detector); + float gamma_hat = qdetector_cccf_get_gamma(_q->detector); + float dphi_hat = qdetector_cccf_get_dphi (_q->detector); + float phi_hat = qdetector_cccf_get_phi (_q->detector); + //printf("*** qdsync frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", + // tau_hat, dphi_hat, 20*log10f(gamma_hat)); // set appropriate filterbank index - if (_q->tau_hat > 0) { - _q->pfb_index = (unsigned int)( _q->tau_hat * _q->npfb) % _q->npfb; + if (tau_hat > 0) { + _q->pfb_index = (unsigned int)( tau_hat * _q->npfb) % _q->npfb; _q->mf_counter = 0; } else { - _q->pfb_index = (unsigned int)((1.0f+_q->tau_hat) * _q->npfb) % _q->npfb; + _q->pfb_index = (unsigned int)((1.0f+tau_hat) * _q->npfb) % _q->npfb; _q->mf_counter = 1; } // output filter scale - firpfb_crcf_set_scale(_q->mf, 0.5f / _q->gamma_hat); + firpfb_crcf_set_scale(_q->mf, 0.5f / gamma_hat); // set frequency/phase of mixer - nco_crcf_set_frequency(_q->mixer, _q->dphi_hat); - nco_crcf_set_phase (_q->mixer, _q->phi_hat ); + nco_crcf_set_frequency(_q->mixer, dphi_hat); + nco_crcf_set_phase (_q->mixer, phi_hat ); // update state _q->state = QDSYNC_STATE_SYNC; From 74042120b97f763db0ac7785427d47ae2b99b098 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 16:26:26 -0400 Subject: [PATCH 010/186] qdsync: allowing other than 2 samples per symbol, extending test * needs further investigation for offset correction * works reasonably well for k in {2,4} but for k=3 occasionally fails * needs more testing of edge cases and resiliency --- src/framing/src/qdsync_cccf.c | 27 +++++++++-------- src/framing/tests/qdsync_cccf_autotest.c | 38 +++++++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index fec0df56e..ed19eef64 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -93,8 +93,8 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // validate input if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - if (_k != 2) - return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); + //if (_k != 2) + // return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); @@ -292,20 +292,21 @@ int qdsync_cccf_execute_detect(qdsync_cccf _q, float gamma_hat = qdetector_cccf_get_gamma(_q->detector); float dphi_hat = qdetector_cccf_get_dphi (_q->detector); float phi_hat = qdetector_cccf_get_phi (_q->detector); - //printf("*** qdsync frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", - // tau_hat, dphi_hat, 20*log10f(gamma_hat)); // set appropriate filterbank index - if (tau_hat > 0) { - _q->pfb_index = (unsigned int)( tau_hat * _q->npfb) % _q->npfb; - _q->mf_counter = 0; - } else { - _q->pfb_index = (unsigned int)((1.0f+tau_hat) * _q->npfb) % _q->npfb; - _q->mf_counter = 1; + _q->mf_counter = _q->k - 2; + _q->pfb_index = 0; + int index = (int)(tau_hat * _q->npfb); + if (index < 0) { + _q->mf_counter++; + index += _q->npfb; } + _q->pfb_index = index; + //printf("* qdsync detected! tau:%6.3f, dphi:%12.4e, phi:%6.3f, gamma:%6.2f dB, mf:%u, pfb idx:%u\n", + // tau_hat, dphi_hat, phi_hat, 20*log10f(gamma_hat), _q->mf_counter, _q->pfb_index); // output filter scale - firpfb_crcf_set_scale(_q->mf, 0.5f / gamma_hat); + firpfb_crcf_set_scale(_q->mf, 1.0f / (_q->k * gamma_hat)); // set frequency/phase of mixer nco_crcf_set_frequency(_q->mixer, dphi_hat); @@ -339,12 +340,12 @@ int qdsync_cccf_step(qdsync_cccf _q, // increment counter to determine if sample is available _q->mf_counter++; - int sample_available = (_q->mf_counter >= 1) ? 1 : 0; + int sample_available = (_q->mf_counter >= _q->k-1) ? 1 : 0; // set output sample if available if (sample_available) { // decrement counter by k=2 samples/symbol - _q->mf_counter -= 2; + _q->mf_counter -= _q->k; // append to output qdsync_cccf_buf_append(_q, v); diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index a5cbfcdbd..d7ab62e1c 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -38,6 +38,7 @@ int autotest_qdsync_callback(float complex * _buf, unsigned int _buf_len, void * _context) { + printf("qdsync callback got %u samples\n", _buf_len); // save samples to buffer as appropriate autotest_qdsync_s * q = (autotest_qdsync_s *) _context; unsigned int i; @@ -76,8 +77,8 @@ void autotest_qdsync() } // payload symbols - float complex payload_tx[payload_len]; - float complex payload_rx[payload_len]; + float complex payload_tx[payload_len]; // transmitted + float complex payload_rx[payload_len]; // received with initial correction for (i=0; i Date: Wed, 26 Oct 2022 16:51:22 -0400 Subject: [PATCH 011/186] qdsync: cleaning up text formatting and comments with object --- src/framing/src/qdsync_cccf.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index ed19eef64..047ab7a79 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -50,34 +50,34 @@ int qdsync_cccf_buf_append(qdsync_cccf _q, // main object definition struct qdsync_cccf_s { unsigned int seq_len; // preamble sequence length - int ftype; // - unsigned int k; // - unsigned int m; // - float beta; // + int ftype; // filter type + unsigned int k; // samples per symbol + unsigned int m; // filter semi-length + float beta; // excess bandwidth factor - qdsync_callback callback; // - void * context; // + qdsync_callback callback; // user-defined callback function + void * context; // user-defined context object qdetector_cccf detector; // detector // status variables enum { QDSYNC_STATE_DETECT=0, // detect frame - QDSYNC_STATE_SYNC, // + QDSYNC_STATE_SYNC, // apply carrier offset correction and matched filter } state; // frame synchronization state unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence - nco_crcf mixer; // coarse carrier frequency recovery + nco_crcf mixer; // coarse carrier frequency recovery // timing recovery objects, states - firpfb_crcf mf; // matched filter decimator - unsigned int npfb; // number of filters in symsync - int mf_counter; // matched filter output timer - unsigned int pfb_index; // filterbank index + firpfb_crcf mf; // matched filter/decimator + unsigned int npfb; // number of filters in symsync + int mf_counter; // matched filter output timer + unsigned int pfb_index; // filterbank index // symbol buffer - unsigned int buf_out_len;// output buffer length - float complex * buf_out; // output buffer - unsigned int buf_out_counter; // output counter + unsigned int buf_out_len;// output buffer length + float complex * buf_out; // output buffer + unsigned int buf_out_counter; // output counter }; // create detector with generic sequence From 4159366907eba40968c77539d5bca342b867d1fe Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:03:49 -0400 Subject: [PATCH 012/186] qdsync/autotest: extending sequence and payload for more stable testing --- src/framing/tests/qdsync_cccf_autotest.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index d7ab62e1c..9d155a3a6 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -60,8 +60,8 @@ int autotest_qdsync_callback(float complex * _buf, void autotest_qdsync() { // options - unsigned int seq_len = 80; // number of sync symbols - unsigned int payload_len = 200; // number of payload symbols + unsigned int seq_len = 240; // number of sync symbols + unsigned int payload_len = 800; // number of payload symbols unsigned int k = 2; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor @@ -135,8 +135,8 @@ void autotest_qdsync() rmse = 10*log10f( rmse / (float)payload_len ); if (liquid_autotest_verbose) printf("qdsync: dphi: %12.4e, phi: %12.8f, rmse: %12.3f\n", dphi_hat, phi_hat, rmse); - CONTEND_LESS_THAN( rmse, -20.0f ) - CONTEND_LESS_THAN( fabsf(dphi_hat), 4e-3f ) + CONTEND_LESS_THAN( rmse, -30.0f ) + CONTEND_LESS_THAN( fabsf(dphi_hat), 1e-3f ) CONTEND_LESS_THAN( fabsf( phi_hat), 0.4f ) // clean up objects From 3ce538e67308bef2091c05adbe217846ffafa0a0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:05:56 -0400 Subject: [PATCH 013/186] qdsync: removing old (commented) error condition checking --- src/framing/src/qdsync_cccf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 047ab7a79..930e840bc 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -93,8 +93,6 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // validate input if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - //if (_k != 2) - // return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); From 7ab94836f2e492ea40df77d5b577587e951ef2ac Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:12:14 -0400 Subject: [PATCH 014/186] qdsync: extending tests to vary samples per symbol --- src/framing/tests/qdsync_cccf_autotest.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 9d155a3a6..39b252aa4 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -57,12 +57,12 @@ int autotest_qdsync_callback(float complex * _buf, return 0; } -void autotest_qdsync() +void testbench_qdsync(unsigned int _k) { // options unsigned int seq_len = 240; // number of sync symbols unsigned int payload_len = 800; // number of payload symbols - unsigned int k = 2; // samples/symbol + unsigned int k = _k; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; @@ -157,3 +157,8 @@ void autotest_qdsync() #endif } +// test specific configurations +void autotest_qdsync_k2() { testbench_qdsync(2); } +void autotest_qdsync_k3() { testbench_qdsync(3); } +void autotest_qdsync_k4() { testbench_qdsync(4); } + From bbc037692aa445492674ff59a6a8bb8c3ae18716 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:35:32 -0400 Subject: [PATCH 015/186] qdsync: adding method to resize output buffer in callback * needs testing * should handle all cases: increasing/decreasing, buffer empty/full * will invoke callback as many times as needed * really intended to run at initial instantiation, but can be run any time * useful for framing synchronization where we need buffers of particular sizes * can hold either the entire frame, or relevant pieces --- include/liquid.h | 4 ++++ src/framing/src/qdsync_cccf.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/liquid.h b/include/liquid.h index 1af8a9c52..c82f5807a 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6286,6 +6286,10 @@ int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); // set context value int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); +// Set callback buffer size (the number of symbol provided to the callback +// whenever it is invoked). +int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len); + // execute block of samples int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 930e840bc..70995eb7b 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -212,6 +212,41 @@ int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) return LIQUID_OK; } +// Set callback buffer size (the number of symbol provided to the callback +// whenever it is invoked). +int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) +{ + if (_buf_len == 0) + return liquid_error(LIQUID_EICONFIG,"qdsync_cccf_set_buf_len(), buffer length must be greater than 0"); + + // check current state + if (_q->buf_out_counter < _buf_len) { + // buffer might not be empty, but we aren't resizing within this space; + // ok to resize so long as old samples are copied + _q->buf_out_len = _buf_len; + _q->buf_out = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + } else { + // we are shrinking the buffer below the number of samples it currently + // holds; invoke the callback as many times as needed to reduce its size + unsigned int index = 0; + while (_q->buf_out_counter >= _buf_len) { + if (_q->callback != NULL) + _q->callback(_q->buf_out + index, _buf_len, _q->context); + + // adjust counters + index += _buf_len; + _q->buf_out_counter -= _buf_len; + } + // now perform the reallocation, but we cannot use 'realloc' here because + // we are not copying values at the front of the buffer + float complex * buf_new = (float complex*)malloc(_buf_len * sizeof(float complex)); + memmove(buf_new, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); + free(_q->buf_out); + _q->buf_out = buf_new; + } + return LIQUID_OK; +} + int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, unsigned int _buf_len) From 222017bec4fd3c80070838241f076cdb3ef79606 Mon Sep 17 00:00:00 2001 From: Sean Nowlan Date: Thu, 3 Nov 2022 09:53:08 -0400 Subject: [PATCH 016/186] headers: whitespace fixes and grammar tweaks --- include/liquid.h | 12 ++++++------ include/liquid.internal.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 40b0b8418..b39d72689 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -4568,7 +4568,7 @@ unsigned int RESAMP(_get_num_output)(RESAMP() _q, \ \ /* Execute arbitrary resampler on a single input sample and store the */ \ /* resulting samples in the output array. The number of output samples */ \ -/* is dependent upon the resampling rate but will be at most */ \ +/* depends upon the resampling rate but will be at most */ \ /* \( \lceil{ r \rceil} \) samples. */ \ /* _q : resamp object */ \ /* _x : single input sample */ \ @@ -4581,7 +4581,7 @@ int RESAMP(_execute)(RESAMP() _q, \ \ /* Execute arbitrary resampler on a block of input samples and store */ \ /* the resulting samples in the output array. The number of output */ \ -/* samples is dependent upon the resampling rate and the number of input */ \ +/* samples depends upon the resampling rate and the number of input */ \ /* samples but will be at most \( \lceil{ r n_x \rceil} \) samples. */ \ /* _q : resamp object */ \ /* _x : input buffer, [size: _nx x 1] */ \ @@ -4739,8 +4739,8 @@ unsigned int MSRESAMP(_get_num_output)(MSRESAMP() _q, \ unsigned int _num_input); \ \ /* Execute multi-stage resampler on one or more input samples. */ \ -/* The number of output samples is dependent upon the resampling rate */ \ -/* and the number of input samples. In general it is good practice to */ \ +/* The number of output samples depends upon the resampling rate and */ \ +/* the number of input samples. In general it is good practice to */ \ /* allocate at least \( \lceil{ 1 + 2 r n_x \rceil} \) samples in the */ \ /* output array to avoid overflows. */ \ /* _q : msresamp object */ \ @@ -6903,7 +6903,7 @@ float liquid_flattop(unsigned int _i, // Triangular window // _i : window index, _i in [0,_wlen-1] // _wlen : full window length -// _n : triangle length, _n in {_wlen-1, _wlen, _wlen+1} +// _n : triangle length, _n in {_wlen-1, _wlen, _wlen+1} float liquid_triangular(unsigned int _i, unsigned int _wlen, unsigned int _n); @@ -8345,7 +8345,7 @@ FIRPFBCH() FIRPFBCH(_create)(int _type, \ /* _type : type (LIQUID_ANALYZER | LIQUID_SYNTHESIZER) */ \ /* _M : number of channels */ \ /* _m : filter delay (symbols) */ \ -/* _As : stop-band attenuation [dB] */ \ +/* _As : stop-band attenuation [dB] */ \ FIRPFBCH() FIRPFBCH(_create_kaiser)(int _type, \ unsigned int _M, \ unsigned int _m, \ diff --git a/include/liquid.internal.h b/include/liquid.internal.h index 25f9b46df..037988509 100644 --- a/include/liquid.internal.h +++ b/include/liquid.internal.h @@ -1113,7 +1113,7 @@ int QSOURCE(_generate)(QSOURCE() _q, \ \ int QSOURCE(_generate_into)(QSOURCE() _q, \ TO * _buf); \ - + LIQUID_QSOURCE_DEFINE_API(LIQUID_QSOURCE_MANGLE_CFLOAT, liquid_float_complex) // From 4115f5adf68a0fc4ee1dae7aca8d343736586f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Wed, 5 Oct 2022 22:48:07 +0100 Subject: [PATCH 017/186] Add Sum Squares AVX version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 9 ++- library.json | 1 + makefile.in | 3 + src/dotprod/src/sumsq.avx.c | 113 ++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/dotprod/src/sumsq.avx.c diff --git a/configure.ac b/configure.ac index a41278434..63e316d1a 100644 --- a/configure.ac +++ b/configure.ac @@ -172,7 +172,14 @@ else # AVX : immintrin.h AX_EXT - if [ test "$ax_cv_have_sse41_ext" = yes && test "$ac_cv_header_smmintrin_h" = yes ]; then + if [ test "$ax_cv_have_avx_ext" = yes && test "$ac_cv_header_immintrin_h" = yes ]; then + # AVX extensions + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ + src/dotprod/src/dotprod_crcf.mmx.o \ + src/dotprod/src/dotprod_rrrf.sse4.o \ + src/dotprod/src/sumsq.avx.o" + ARCH_OPTION='-mavx' + elif [ test "$ax_cv_have_sse41_ext" = yes && test "$ac_cv_header_smmintrin_h" = yes ]; then # SSE4.1/2 extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ src/dotprod/src/dotprod_crcf.mmx.o \ diff --git a/library.json b/library.json index 1ee304823..fea268c46 100644 --- a/library.json +++ b/library.json @@ -42,6 +42,7 @@ "-<*/src/*.mmx.c>", "-<*/src/*.neon.c>", "-<*/src/*.sse4.c>", + "-<*/src/*.avx.c>", "-<*/src/*.x86.s>" ] }, diff --git a/makefile.in b/makefile.in index 63c105b28..596320379 100644 --- a/makefile.in +++ b/makefile.in @@ -220,6 +220,9 @@ src/dotprod/src/sumsq.mmx.o : %.o : %.c $(include_headers) # SSE4.1/2 src/dotprod/src/dotprod_rrrf.sse4.o : %.o : %.c $(include_headers) +# AVX +src/dotprod/src/sumsq.avx.o : %.o : %.c $(include_headers) + # ARM Neon src/dotprod/src/dotprod_rrrf.neon.o : %.o : %.c $(include_headers) src/dotprod/src/dotprod_crcf.neon.o : %.o : %.c $(include_headers) diff --git a/src/dotprod/src/sumsq.avx.c b/src/dotprod/src/sumsq.avx.c new file mode 100644 index 000000000..aea53fb01 --- /dev/null +++ b/src/dotprod/src/sumsq.avx.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2007 - 2015 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// sumsq.mmx.c : floating-point sum of squares (MMX) +// + +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#if HAVE_MMX && HAVE_MMINTRIN_H +#include // MMX +#endif + +#if HAVE_SSE && HAVE_XMMINTRIN_H +#include // SSE +#endif + +#if HAVE_SSE2 && HAVE_EMMINTRIN_H +#include // SSE2 +#endif + +#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#include // SSE3 +#endif + +#if HAVE_AVX && HAVE_IMMINTRIN_H +#include // AVX +#endif + +// sum squares, basic loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf(float * _v, + unsigned int _n) +{ + // first cut: ... + __m256 v; // input vector + __m256 s; // dot product + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/8)) + unsigned int t = (_n >> 3) << 3; + + // + unsigned int i; + for (i=0; i Date: Wed, 16 Nov 2022 22:54:55 +0000 Subject: [PATCH 018/186] Update ax_ext.m4 and dependencies to test for newer SIMD extensions Update SIMD implementations to exclude header checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 15 +- scripts/ax_ext.m4 | 436 ++++++++++++++++++++--------- scripts/ax_gcc_x86_avx_xgetbv.m4 | 79 ++++++ scripts/ax_gcc_x86_cpuid.m4 | 26 +- src/dotprod/src/dotprod_cccf.mmx.c | 14 +- src/dotprod/src/dotprod_rrrf.mmx.c | 12 +- src/dotprod/src/sumsq.avx.c | 10 +- src/dotprod/src/sumsq.mmx.c | 10 +- 8 files changed, 429 insertions(+), 173 deletions(-) create mode 100644 scripts/ax_gcc_x86_avx_xgetbv.m4 diff --git a/configure.ac b/configure.ac index 63e316d1a..3d2469f47 100644 --- a/configure.ac +++ b/configure.ac @@ -172,28 +172,35 @@ else # AVX : immintrin.h AX_EXT - if [ test "$ax_cv_have_avx_ext" = yes && test "$ac_cv_header_immintrin_h" = yes ]; then + if [ test "$ax_cv_have_avx2_ext" = yes ]; then + # AVX2 extensions + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ + src/dotprod/src/dotprod_crcf.mmx.o \ + src/dotprod/src/dotprod_rrrf.sse4.o \ + src/dotprod/src/sumsq.avx.o" + ARCH_OPTION='-mavx2' + elif [ test "$ax_cv_have_avx_ext" = yes ]; then # AVX extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ src/dotprod/src/dotprod_crcf.mmx.o \ src/dotprod/src/dotprod_rrrf.sse4.o \ src/dotprod/src/sumsq.avx.o" ARCH_OPTION='-mavx' - elif [ test "$ax_cv_have_sse41_ext" = yes && test "$ac_cv_header_smmintrin_h" = yes ]; then + elif [ test "$ax_cv_have_sse41_ext" = yes ]; then # SSE4.1/2 extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ src/dotprod/src/dotprod_crcf.mmx.o \ src/dotprod/src/dotprod_rrrf.sse4.o \ src/dotprod/src/sumsq.mmx.o" ARCH_OPTION='-msse4.1' - elif [ test "$ax_cv_have_sse3_ext" = yes && test "$ac_cv_header_pmmintrin_h" = yes ]; then + elif [ test "$ax_cv_have_sse3_ext" = yes ]; then # SSE3 extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ src/dotprod/src/dotprod_crcf.mmx.o \ src/dotprod/src/dotprod_rrrf.mmx.o \ src/dotprod/src/sumsq.mmx.o" ARCH_OPTION='-msse3' - elif [ test "$ax_cv_have_sse2_ext" = yes && test "$ac_cv_header_emmintrin_h" = yes ]; then + elif [ test "$ax_cv_have_sse2_ext" = yes ]; then # SSE2 extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ src/dotprod/src/dotprod_crcf.mmx.o \ diff --git a/scripts/ax_ext.m4 b/scripts/ax_ext.m4 index 0cb2c6e64..b95d75a2b 100644 --- a/scripts/ax_ext.m4 +++ b/scripts/ax_ext.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_ext.html +# https://www.gnu.org/software/autoconf-archive/ax_ext.html # =========================================================================== # # SYNOPSIS @@ -8,161 +8,321 @@ # # DESCRIPTION # -# Find supported SIMD extensions by requesting cpuid. When an SIMD -# extension is found, the -m"simdextensionname" is added to SIMD_FLAGS -# (only if compilator support it) (ie : if "sse2" is available "-msse2" is -# added to SIMD_FLAGS) +# Find supported SIMD extensions by requesting cpuid. When a SIMD +# extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if +# compiler supports it. For example, if "sse2" is available then "-msse2" +# is added to SIMD_FLAGS. +# +# Find other supported CPU extensions by requesting cpuid. When a +# processor extension is found, the -m"extensionname" is added to +# CPUEXT_FLAGS if compiler supports it. For example, if "bmi2" is +# available then "-mbmi2" is added to CPUEXT_FLAGS. # # This macro calls: # # AC_SUBST(SIMD_FLAGS) +# AC_SUBST(CPUEXT_FLAGS) # # And defines: # -# HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / -# HAVE_SSE41 / HAVE_SSE42 / HAVE_AVX +# HAVE_RDRND / HAVE_BMI1 / HAVE_BMI2 / HAVE_ADX / HAVE_MPX +# HAVE_PREFETCHWT1 / HAVE_ABM / HAVE_MMX / HAVE_SSE / HAVE_SSE2 +# HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4_1 / HAVE_SSE4_2 / HAVE_SSE4a +# HAVE_SHA / HAVE_AES / HAVE_AVX / HAVE_FMA3 / HAVE_FMA4 / HAVE_XOP +# HAVE_AVX2 / HAVE_AVX512_F / HAVE_AVX512_CD / HAVE_AVX512_PF +# HAVE_AVX512_ER / HAVE_AVX512_VL / HAVE_AVX512_BW / HAVE_AVX512_DQ +# HAVE_AVX512_IFMA / HAVE_AVX512_VBMI / HAVE_ALTIVEC / HAVE_VSX # # LICENSE # -# Copyright (c) 2008 Christophe Tournayre +# Copyright (c) 2007 Christophe Tournayre +# Copyright (c) 2013,2015 Michael Petch +# Copyright (c) 2017 Rafael de Lucena Valle # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. -# -# MODIFIED -# * March 23, 2012 Joseph D. Gaeddert -# Including tests for proper intrinsics header files in addition to cpu -# capabilities. -# MMX : mmintrin.h -# SSE : xmmintrin.h -# SSE2 : emmintrin.h -# SSE3 : pmmintrin.h -# SSSE3 : tmmintrin.h -# SSE4.1/4.2 : smmintrin.h -# AVX : immintrin.h - -#serial 9 + +#serial 18 AC_DEFUN([AX_EXT], [ - AC_REQUIRE([AX_GCC_X86_CPUID]) - - AX_GCC_X86_CPUID(0x00000001) - ecx=0 - edx=0 - if test "$ax_cv_gcc_x86_cpuid_0x00000001" != "unknown"; - then - ecx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 3` - edx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 4` - fi - - AC_CHECK_HEADERS(mmintrin.h xmmintrin.h emmintrin.h pmmintrin.h tmmintrin.h smmintrin.h immintrin.h) - - AC_CACHE_CHECK([whether mmx is supported], [ax_cv_have_mmx_ext], - [ - ax_cv_have_mmx_ext=no - if test "$((0x$edx>>23&0x01))" = 1; then - ax_cv_have_mmx_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether sse is supported], [ax_cv_have_sse_ext], - [ - ax_cv_have_sse_ext=no - if test "$((0x$edx>>25&0x01))" = 1; then - ax_cv_have_sse_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether sse2 is supported], [ax_cv_have_sse2_ext], - [ - ax_cv_have_sse2_ext=no - if test "$((0x$edx>>26&0x01))" = 1; then - ax_cv_have_sse2_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether sse3 is supported], [ax_cv_have_sse3_ext], - [ - ax_cv_have_sse3_ext=no - if test "$((0x$ecx&0x01))" = 1; then - ax_cv_have_sse3_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether ssse3 is supported], [ax_cv_have_ssse3_ext], - [ - ax_cv_have_ssse3_ext=no - if test "$((0x$ecx>>9&0x01))" = 1; then - ax_cv_have_ssse3_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether sse4.1 is supported], [ax_cv_have_sse41_ext], - [ - ax_cv_have_sse41_ext=no - if test "$((0x$ecx>>19&0x01))" = 1; then - ax_cv_have_sse41_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether sse4.2 is supported], [ax_cv_have_sse42_ext], - [ - ax_cv_have_sse42_ext=no - if test "$((0x$ecx>>20&0x01))" = 1; then - ax_cv_have_sse42_ext=yes - fi - ]) - - AC_CACHE_CHECK([whether avx is supported], [ax_cv_have_avx_ext], - [ - ax_cv_have_avx_ext=no - if test "$((0x$ecx>>28&0x01))" = 1; then - ax_cv_have_avx_ext=yes - fi - ]) - - if [ test "$ax_cv_have_mmx_ext" = yes && test "$ac_cv_header_mmintrin_h" = yes ]; then - AC_DEFINE(HAVE_MMX,1, [Support MMX instructions]) - AX_CHECK_COMPILE_FLAG(-mmmx, SIMD_FLAGS="$SIMD_FLAGS -mmmx", []) - fi - - if [ test "$ax_cv_have_sse_ext" = yes && test "$ac_cv_header_xmmintrin_h" = yes ]; then - AC_DEFINE(HAVE_SSE,1, [Support SSE (Streaming SIMD Extensions) instructions]) - AX_CHECK_COMPILE_FLAG(-msse, SIMD_FLAGS="$SIMD_FLAGS -msse", []) - fi - - if [ test "$ax_cv_have_sse2_ext" = yes && test "$ac_cv_header_emmintrin_h" = yes ]; then - AC_DEFINE(HAVE_SSE2,1, [Support SSE2 (Streaming SIMD Extensions 2) instructions]) - AX_CHECK_COMPILE_FLAG(-msse2, SIMD_FLAGS="$SIMD_FLAGS -msse2", []) - fi - - if [ test "$ax_cv_have_sse3_ext" = yes && test "$ac_cv_header_pmmintrin_h" = yes ]; then - AC_DEFINE(HAVE_SSE3,1, [Support SSE3 (Streaming SIMD Extensions 3) instructions]) - AX_CHECK_COMPILE_FLAG(-msse3, SIMD_FLAGS="$SIMD_FLAGS -msse3", []) - fi - - if [ test "$ax_cv_have_ssse3_ext" = yes && test "$ac_cv_header_tmmintrin_h" = yes ]; then - AC_DEFINE(HAVE_SSSE3,1, [Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions]) - AX_CHECK_COMPILE_FLAG(-mssse3, SIMD_FLAGS="$SIMD_FLAGS -mssse3", []) - fi - - if [ test "$ax_cv_have_sse41_ext" = yes && test "$ac_cv_header_smmintrin_h" = yes ]; then - AC_DEFINE(HAVE_SSE41,1, [Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions]) - AX_CHECK_COMPILE_FLAG(-msse4.1, SIMD_FLAGS="$SIMD_FLAGS -msse4.1", []) - fi - - if [ test "$ax_cv_have_sse42_ext" = yes && test "$ac_cv_header_smmintrin_h" = yes ]; then - AC_DEFINE(HAVE_SSE42, 1,[Support SSE4.2 (Streaming SIMD Extensions 4.2) instructions]) - AX_CHECK_COMPILE_FLAG(-msse4.2, SIMD_FLAGS="$SIMD_FLAGS -msse4.2", []) - fi - - if [ test "$ax_cv_have_avx_ext" = yes && test "$ac_cv_header_immintrin_h" = yes ]; then - AC_DEFINE(HAVE_AVX,1,[Support AVX (Advanced Vector Extensions) instructions]) - AX_CHECK_COMPILE_FLAG(-mavx, SIMD_FLAGS="$SIMD_FLAGS -mavx", []) - fi + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_PROG_CC]) + + CPUEXT_FLAGS="" + SIMD_FLAGS="" + + case $host_cpu in + powerpc*) + AC_CACHE_CHECK([whether altivec is supported for old distros], [ax_cv_have_altivec_old_ext], + [ + if test `/usr/sbin/sysctl -a 2>/dev/null| grep -c hw.optional.altivec` != 0; then + if test `/usr/sbin/sysctl -n hw.optional.altivec` = 1; then + ax_cv_have_altivec_old_ext=yes + fi + fi + ]) + + if test "$ax_cv_have_altivec_old_ext" = yes; then + AC_DEFINE(HAVE_ALTIVEC,,[Support Altivec instructions]) + AX_CHECK_COMPILE_FLAG(-faltivec, SIMD_FLAGS="$SIMD_FLAGS -faltivec", []) + fi + + AC_CACHE_CHECK([whether altivec is supported], [ax_cv_have_altivec_ext], + [ + if test `LD_SHOW_AUXV=1 /bin/true 2>/dev/null|grep -c altivec` != 0; then + ax_cv_have_altivec_ext=yes + fi + ]) + + if test "$ax_cv_have_altivec_ext" = yes; then + AC_DEFINE(HAVE_ALTIVEC,,[Support Altivec instructions]) + AX_CHECK_COMPILE_FLAG(-maltivec, SIMD_FLAGS="$SIMD_FLAGS -maltivec", []) + fi + + AC_CACHE_CHECK([whether vsx is supported], [ax_cv_have_vsx_ext], + [ + if test `LD_SHOW_AUXV=1 /bin/true 2>/dev/null|grep -c vsx` != 0; then + ax_cv_have_vsx_ext=yes + fi + ]) + + if test "$ax_cv_have_vsx_ext" = yes; then + AC_DEFINE(HAVE_VSX,,[Support VSX instructions]) + AX_CHECK_COMPILE_FLAG(-mvsx, SIMD_FLAGS="$SIMD_FLAGS -mvsx", []) + fi + ;; + + i[[3456]]86*|x86_64*|amd64*) + + AC_REQUIRE([AX_GCC_X86_CPUID]) + AC_REQUIRE([AX_GCC_X86_CPUID_COUNT]) + AC_REQUIRE([AX_GCC_X86_AVX_XGETBV]) + + eax_cpuid0=0 + AX_GCC_X86_CPUID(0x00000000) + if test "$ax_cv_gcc_x86_cpuid_0x00000000" != "unknown"; + then + eax_cpuid0=`echo $ax_cv_gcc_x86_cpuid_0x00000000 | cut -d ":" -f 1` + fi + + eax_cpuid80000000=0 + AX_GCC_X86_CPUID(0x80000000) + if test "$ax_cv_gcc_x86_cpuid_0x80000000" != "unknown"; + then + eax_cpuid80000000=`echo $ax_cv_gcc_x86_cpuid_0x80000000 | cut -d ":" -f 1` + fi + + ecx_cpuid1=0 + edx_cpuid1=0 + if test "$((0x$eax_cpuid0))" -ge 1 ; then + AX_GCC_X86_CPUID(0x00000001) + if test "$ax_cv_gcc_x86_cpuid_0x00000001" != "unknown"; + then + ecx_cpuid1=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 3` + edx_cpuid1=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 4` + fi + fi + + ebx_cpuid7=0 + ecx_cpuid7=0 + if test "$((0x$eax_cpuid0))" -ge 7 ; then + AX_GCC_X86_CPUID_COUNT(0x00000007, 0x00) + if test "$ax_cv_gcc_x86_cpuid_0x00000007" != "unknown"; + then + ebx_cpuid7=`echo $ax_cv_gcc_x86_cpuid_0x00000007 | cut -d ":" -f 2` + ecx_cpuid7=`echo $ax_cv_gcc_x86_cpuid_0x00000007 | cut -d ":" -f 3` + fi + fi + + ecx_cpuid80000001=0 + edx_cpuid80000001=0 + if test "$((0x$eax_cpuid80000000))" -ge "$((0x80000001))" ; then + AX_GCC_X86_CPUID(0x80000001) + if test "$ax_cv_gcc_x86_cpuid_0x80000001" != "unknown"; + then + ecx_cpuid80000001=`echo $ax_cv_gcc_x86_cpuid_0x80000001 | cut -d ":" -f 3` + edx_cpuid80000001=`echo $ax_cv_gcc_x86_cpuid_0x80000001 | cut -d ":" -f 4` + fi + fi + + AC_CACHE_VAL([ax_cv_have_mmx_os_support_ext], + [ + ax_cv_have_mmx_os_support_ext=yes + ]) + + ax_cv_have_none_os_support_ext=yes + + AC_CACHE_VAL([ax_cv_have_sse_os_support_ext], + [ + ax_cv_have_sse_os_support_ext=no, + if test "$((0x$edx_cpuid1>>25&0x01))" = 1; then + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include + /* No way at ring1 to ring3 in protected mode to check the CR0 and CR4 + control registers directly. Execute an SSE instruction. + If it raises SIGILL then OS doesn't support SSE based instructions */ + void sig_handler(int signum){ exit(1); } + int main(){ + signal(SIGILL, sig_handler); + /* SSE instruction xorps %xmm0,%xmm0 */ + __asm__ __volatile__ (".byte 0x0f, 0x57, 0xc0"); + return 0; + }]])], + [ax_cv_have_sse_os_support_ext=yes], + [ax_cv_have_sse_os_support_ext=no], + [ax_cv_have_sse_os_support_ext=no]) + AC_LANG_POP([C]) + fi + ]) + + xgetbv_eax=0 + if test "$((0x$ecx_cpuid1>>28&0x01))" = 1; then + AX_GCC_X86_AVX_XGETBV(0x00000000) + + if test x"$ax_cv_gcc_x86_avx_xgetbv_0x00000000" != x"unknown"; then + xgetbv_eax=`echo $ax_cv_gcc_x86_avx_xgetbv_0x00000000 | cut -d ":" -f 1` + fi + + AC_CACHE_VAL([ax_cv_have_avx_os_support_ext], + [ + ax_cv_have_avx_os_support_ext=no + if test "$((0x$ecx_cpuid1>>27&0x01))" = 1; then + if test "$((0x$xgetbv_eax&0x6))" = 6; then + ax_cv_have_avx_os_support_ext=yes + fi + fi + ]) + fi + + AC_CACHE_VAL([ax_cv_have_avx512_os_support_ext], + [ + ax_cv_have_avx512_os_support_ext=no + if test "$ax_cv_have_avx_os_support_ext" = yes; then + if test "$((0x$xgetbv_eax&0xe6))" = "$((0xe6))"; then + ax_cv_have_avx512_os_support_ext=yes + fi + fi + ]) + + for ac_instr_info dnl + in "none;rdrnd;RDRND;ecx_cpuid1,30;-mrdrnd;HAVE_RDRND;CPUEXT_FLAGS" dnl + "none;bmi1;BMI1;ebx_cpuid7,3;-mbmi;HAVE_BMI1;CPUEXT_FLAGS" dnl + "none;bmi2;BMI2;ebx_cpuid7,8;-mbmi2;HAVE_BMI2;CPUEXT_FLAGS" dnl + "none;adx;ADX;ebx_cpuid7,19;-madx;HAVE_ADX;CPUEXT_FLAGS" dnl + "none;mpx;MPX;ebx_cpuid7,14;-mmpx;HAVE_MPX;CPUEXT_FLAGS" dnl + "none;prefetchwt1;PREFETCHWT1;ecx_cpuid7,0;-mprefetchwt1;HAVE_PREFETCHWT1;CPUEXT_FLAGS" dnl + "none;abm;ABM;ecx_cpuid80000001,5;-mabm;HAVE_ABM;CPUEXT_FLAGS" dnl + "mmx;mmx;MMX;edx_cpuid1,23;-mmmx;HAVE_MMX;SIMD_FLAGS" dnl + "sse;sse;SSE;edx_cpuid1,25;-msse;HAVE_SSE;SIMD_FLAGS" dnl + "sse;sse2;SSE2;edx_cpuid1,26;-msse2;HAVE_SSE2;SIMD_FLAGS" dnl + "sse;sse3;SSE3;ecx_cpuid1,1;-msse3;HAVE_SSE3;SIMD_FLAGS" dnl + "sse;ssse3;SSSE3;ecx_cpuid1,9;-mssse3;HAVE_SSSE3;SIMD_FLAGS" dnl + "sse;sse41;SSE4.1;ecx_cpuid1,19;-msse4.1;HAVE_SSE4_1;SIMD_FLAGS" dnl + "sse;sse42;SSE4.2;ecx_cpuid1,20;-msse4.2;HAVE_SSE4_2;SIMD_FLAGS" dnl + "sse;sse4a;SSE4a;ecx_cpuid80000001,6;-msse4a;HAVE_SSE4a;SIMD_FLAGS" dnl + "sse;sha;SHA;ebx_cpuid7,29;-msha;HAVE_SHA;SIMD_FLAGS" dnl + "sse;aes;AES;ecx_cpuid1,25;-maes;HAVE_AES;SIMD_FLAGS" dnl + "avx;avx;AVX;ecx_cpuid1,28;-mavx;HAVE_AVX;SIMD_FLAGS" dnl + "avx;fma3;FMA3;ecx_cpuid1,12;-mfma;HAVE_FMA3;SIMD_FLAGS" dnl + "avx;fma4;FMA4;ecx_cpuid80000001,16;-mfma4;HAVE_FMA4;SIMD_FLAGS" dnl + "avx;xop;XOP;ecx_cpuid80000001,11;-mxop;HAVE_XOP;SIMD_FLAGS" dnl + "avx;avx2;AVX2;ebx_cpuid7,5;-mavx2;HAVE_AVX2;SIMD_FLAGS" dnl + "avx512;avx512f;AVX512-F;ebx_cpuid7,16;-mavx512f;HAVE_AVX512_F;SIMD_FLAGS" dnl + "avx512;avx512cd;AVX512-CD;ebx_cpuid7,28;-mavx512cd;HAVE_AVX512_CD;SIMD_FLAGS" dnl + "avx512;avx512pf;AVX512-PF;ebx_cpuid7,26;-mavx512pf;HAVE_AVX512_PF;SIMD_FLAGS" dnl + "avx512;avx512er;AVX512-ER;ebx_cpuid7,27;-mavx512er;HAVE_AVX512_ER;SIMD_FLAGS" dnl + "avx512;avx512vl;AVX512-VL;ebx_cpuid7,31;-mavx512vl;HAVE_AVX512_VL;SIMD_FLAGS" dnl + "avx512;avx512bw;AVX512-BW;ebx_cpuid7,30;-mavx512bw;HAVE_AVX512_BW;SIMD_FLAGS" dnl + "avx512;avx512dq;AVX512-DQ;ebx_cpuid7,17;-mavx512dq;HAVE_AVX512_DQ;SIMD_FLAGS" dnl + "avx512;avx512ifma;AVX512-IFMA;ebx_cpuid7,21;-mavx512ifma;HAVE_AVX512_IFMA;SIMD_FLAGS" dnl + "avx512;avx512vbmi;AVX512-VBMI;ecx_cpuid7,1;-mavx512vbmi;HAVE_AVX512_VBMI;SIMD_FLAGS" dnl + # + do ac_instr_os_support=$(eval echo \$ax_cv_have_$(echo $ac_instr_info | cut -d ";" -f 1)_os_support_ext) + ac_instr_acvar=$(echo $ac_instr_info | cut -d ";" -f 2) + ac_instr_shortname=$(echo $ac_instr_info | cut -d ";" -f 3) + ac_instr_chk_loc=$(echo $ac_instr_info | cut -d ";" -f 4) + ac_instr_chk_reg=0x$(eval echo \$$(echo $ac_instr_chk_loc | cut -d "," -f 1)) + ac_instr_chk_bit=$(echo $ac_instr_chk_loc | cut -d "," -f 2) + ac_instr_compiler_flags=$(echo $ac_instr_info | cut -d ";" -f 5) + ac_instr_have_define=$(echo $ac_instr_info | cut -d ";" -f 6) + ac_instr_flag_type=$(echo $ac_instr_info | cut -d ";" -f 7) + + AC_CACHE_CHECK([whether ${ac_instr_shortname} is supported by the processor], [ax_cv_have_${ac_instr_acvar}_cpu_ext], + [ + eval ax_cv_have_${ac_instr_acvar}_cpu_ext=no + if test "$((${ac_instr_chk_reg}>>${ac_instr_chk_bit}&0x01))" = 1 ; then + eval ax_cv_have_${ac_instr_acvar}_cpu_ext=yes + fi + ]) + + if test x"$(eval echo \$ax_cv_have_${ac_instr_acvar}_cpu_ext)" = x"yes"; then + AC_CACHE_CHECK([whether ${ac_instr_shortname} is supported by the processor and OS], [ax_cv_have_${ac_instr_acvar}_ext], + [ + eval ax_cv_have_${ac_instr_acvar}_ext=no + if test x"${ac_instr_os_support}" = x"yes"; then + eval ax_cv_have_${ac_instr_acvar}_ext=yes + fi + ]) + + if test "$(eval echo \$ax_cv_have_${ac_instr_acvar}_ext)" = yes; then + AX_CHECK_COMPILE_FLAG(${ac_instr_compiler_flags}, eval ax_cv_support_${ac_instr_acvar}_ext=yes, + eval ax_cv_support_${ac_instr_acvar}_ext=no) + if test x"$(eval echo \$ax_cv_support_${ac_instr_acvar}_ext)" = x"yes"; then + eval ${ac_instr_flag_type}=\"\$${ac_instr_flag_type} ${ac_instr_compiler_flags}\" + AC_DEFINE_UNQUOTED([${ac_instr_have_define}]) + else + AC_MSG_WARN([Your processor and OS supports ${ac_instr_shortname} instructions but not your compiler, can you try another compiler?]) + fi + else + if test x"${ac_instr_os_support}" = x"no"; then + AC_CACHE_VAL(ax_cv_support_${ac_instr_acvar}_ext, eval ax_cv_support_${ac_instr_acvar}_ext=no) + AC_MSG_WARN([Your processor supports ${ac_instr_shortname}, but your OS doesn't]) + fi + fi + else + AC_CACHE_VAL(ax_cv_have_${ac_instr_acvar}_ext, eval ax_cv_have_${ac_instr_acvar}_ext=no) + AC_CACHE_VAL(ax_cv_support_${ac_instr_acvar}_ext, eval ax_cv_support_${ac_instr_acvar}_ext=no) + fi + done + ;; + esac + AH_TEMPLATE([HAVE_RDRND],[Define to 1 to support Digital Random Number Generator]) + AH_TEMPLATE([HAVE_BMI1],[Define to 1 to support Bit Manipulation Instruction Set 1]) + AH_TEMPLATE([HAVE_BMI2],[Define to 1 to support Bit Manipulation Instruction Set 2]) + AH_TEMPLATE([HAVE_ADX],[Define to 1 to support Multi-Precision Add-Carry Instruction Extensions]) + AH_TEMPLATE([HAVE_MPX],[Define to 1 to support Memory Protection Extensions]) + AH_TEMPLATE([HAVE_PREFETCHWT1],[Define to 1 to support Prefetch Vector Data Into Caches WT1]) + AH_TEMPLATE([HAVE_ABM],[Define to 1 to support Advanced Bit Manipulation]) + AH_TEMPLATE([HAVE_MMX],[Define to 1 to support Multimedia Extensions]) + AH_TEMPLATE([HAVE_SSE],[Define to 1 to support Streaming SIMD Extensions]) + AH_TEMPLATE([HAVE_SSE2],[Define to 1 to support Streaming SIMD Extensions]) + AH_TEMPLATE([HAVE_SSE3],[Define to 1 to support Streaming SIMD Extensions 3]) + AH_TEMPLATE([HAVE_SSSE3],[Define to 1 to support Supplemental Streaming SIMD Extensions 3]) + AH_TEMPLATE([HAVE_SSE4_1],[Define to 1 to support Streaming SIMD Extensions 4.1]) + AH_TEMPLATE([HAVE_SSE4_2],[Define to 1 to support Streaming SIMD Extensions 4.2]) + AH_TEMPLATE([HAVE_SSE4a],[Define to 1 to support AMD Streaming SIMD Extensions 4a]) + AH_TEMPLATE([HAVE_SHA],[Define to 1 to support Secure Hash Algorithm Extension]) + AH_TEMPLATE([HAVE_AES],[Define to 1 to support Advanced Encryption Standard New Instruction Set (AES-NI)]) + AH_TEMPLATE([HAVE_AVX],[Define to 1 to support Advanced Vector Extensions]) + AH_TEMPLATE([HAVE_FMA3],[Define to 1 to support Fused Multiply-Add Extensions 3]) + AH_TEMPLATE([HAVE_FMA4],[Define to 1 to support Fused Multiply-Add Extensions 4]) + AH_TEMPLATE([HAVE_XOP],[Define to 1 to support eXtended Operations Extensions]) + AH_TEMPLATE([HAVE_AVX2],[Define to 1 to support Advanced Vector Extensions 2]) + AH_TEMPLATE([HAVE_AVX512_F],[Define to 1 to support AVX-512 Foundation Extensions]) + AH_TEMPLATE([HAVE_AVX512_CD],[Define to 1 to support AVX-512 Conflict Detection Instructions]) + AH_TEMPLATE([HAVE_AVX512_PF],[Define to 1 to support AVX-512 Conflict Prefetch Instructions]) + AH_TEMPLATE([HAVE_AVX512_ER],[Define to 1 to support AVX-512 Exponential & Reciprocal Instructions]) + AH_TEMPLATE([HAVE_AVX512_VL],[Define to 1 to support AVX-512 Vector Length Extensions]) + AH_TEMPLATE([HAVE_AVX512_BW],[Define to 1 to support AVX-512 Byte and Word Instructions]) + AH_TEMPLATE([HAVE_AVX512_DQ],[Define to 1 to support AVX-512 Doubleword and Quadword Instructions]) + AH_TEMPLATE([HAVE_AVX512_IFMA],[Define to 1 to support AVX-512 Integer Fused Multiply Add Instructions]) + AH_TEMPLATE([HAVE_AVX512_VBMI],[Define to 1 to support AVX-512 Vector Byte Manipulation Instructions]) AC_SUBST(SIMD_FLAGS) + AC_SUBST(CPUEXT_FLAGS) ]) diff --git a/scripts/ax_gcc_x86_avx_xgetbv.m4 b/scripts/ax_gcc_x86_avx_xgetbv.m4 new file mode 100644 index 000000000..a57fc5eeb --- /dev/null +++ b/scripts/ax_gcc_x86_avx_xgetbv.m4 @@ -0,0 +1,79 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_X86_AVX_XGETBV +# +# DESCRIPTION +# +# On later x86 processors with AVX SIMD support, with gcc or a compiler +# that has a compatible syntax for inline assembly instructions, run a +# small program that executes the xgetbv instruction with input OP. This +# can be used to detect if the OS supports AVX instruction usage. +# +# On output, the values of the eax and edx registers are stored as +# hexadecimal strings as "eax:edx" in the cache variable +# ax_cv_gcc_x86_avx_xgetbv. +# +# If the xgetbv instruction fails (because you are running a +# cross-compiler, or because you are not using gcc, or because you are on +# a processor that doesn't have this instruction), +# ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown". +# +# This macro mainly exists to be used in AX_EXT. +# +# LICENSE +# +# Copyright (c) 2013 Michael Petch +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 3 + +AC_DEFUN([AX_GCC_X86_AVX_XGETBV], +[AC_REQUIRE([AC_PROG_CC]) +AC_LANG_PUSH([C]) +AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1, + [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], [ + int op = $1, eax, edx; + FILE *f; + /* Opcodes for xgetbv */ + __asm__ __volatile__ (".byte 0x0f, 0x01, 0xd0" + : "=a" (eax), "=d" (edx) + : "c" (op)); + f = fopen("conftest_xgetbv", "w"); if (!f) return 1; + fprintf(f, "%x:%x\n", eax, edx); + fclose(f); + return 0; +])], + [ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv], + [ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv], + [ax_cv_gcc_x86_avx_xgetbv_$1=unknown])]) +AC_LANG_POP([C]) +]) diff --git a/scripts/ax_gcc_x86_cpuid.m4 b/scripts/ax_gcc_x86_cpuid.m4 index 7d46fee02..df954658e 100644 --- a/scripts/ax_gcc_x86_cpuid.m4 +++ b/scripts/ax_gcc_x86_cpuid.m4 @@ -1,17 +1,19 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html +# https://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html # =========================================================================== # # SYNOPSIS # # AX_GCC_X86_CPUID(OP) +# AX_GCC_X86_CPUID_COUNT(OP, COUNT) # # DESCRIPTION # # On Pentium and later x86 processors, with gcc or a compiler that has a # compatible syntax for inline assembly instructions, run a small program # that executes the cpuid instruction with input OP. This can be used to -# detect the CPU type. +# detect the CPU type. AX_GCC_X86_CPUID_COUNT takes an additional COUNT +# parameter that gets passed into register ECX before calling cpuid. # # On output, the values of the eax, ebx, ecx, and edx registers are stored # as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable @@ -28,6 +30,7 @@ # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2008 Matteo Frigo +# Copyright (c) 2015 Michael Petch # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -40,7 +43,7 @@ # Public License for more details. # # You should have received a copy of the GNU General Public License along -# with this program. If not, see . +# with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure @@ -55,18 +58,25 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 7 +#serial 10 AC_DEFUN([AX_GCC_X86_CPUID], +[AX_GCC_X86_CPUID_COUNT($1, 0) +]) + +AC_DEFUN([AX_GCC_X86_CPUID_COUNT], [AC_REQUIRE([AC_PROG_CC]) AC_LANG_PUSH([C]) AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1, [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], [ - int op = $1, eax, ebx, ecx, edx; + int op = $1, level = $2, eax, ebx, ecx, edx; FILE *f; - __asm__("cpuid" - : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) - : "a" (op)); + __asm__ __volatile__ ("xchg %%ebx, %1\n" + "cpuid\n" + "xchg %%ebx, %1\n" + : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op), "2" (level)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); fclose(f); diff --git a/src/dotprod/src/dotprod_cccf.mmx.c b/src/dotprod/src/dotprod_cccf.mmx.c index 36cf10ae0..6794bfcfe 100644 --- a/src/dotprod/src/dotprod_cccf.mmx.c +++ b/src/dotprod/src/dotprod_cccf.mmx.c @@ -33,19 +33,19 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX && HAVE_MMINTRIN_H +#if HAVE_MMX #include // MMX #endif -#if HAVE_SSE && HAVE_XMMINTRIN_H +#if HAVE_SSE #include // SSE #endif -#if HAVE_SSE2 && HAVE_EMMINTRIN_H +#if HAVE_SSE2 #include // SSE2 #endif -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 #include // SSE3 #endif @@ -268,7 +268,7 @@ int dotprod_cccf_execute_mmx(dotprod_cccf _q, // aligned output array float w[4] __attribute__((aligned(16))) = {0,0,0,0}; -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 // SSE3 __m128 s; // dot product __m128 sum = _mm_setzero_ps(); // load zeros into sum register @@ -299,7 +299,7 @@ int dotprod_cccf_execute_mmx(dotprod_cccf _q, // shuffle values cq = _mm_shuffle_ps( cq, cq, _MM_SHUFFLE(2,3,0,1) ); -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 // SSE3: combine using addsub_ps() s = _mm_addsub_ps( ci, cq ); @@ -320,7 +320,7 @@ int dotprod_cccf_execute_mmx(dotprod_cccf _q, #endif } -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 // unload packed array _mm_store_ps(w, sum); #endif diff --git a/src/dotprod/src/dotprod_rrrf.mmx.c b/src/dotprod/src/dotprod_rrrf.mmx.c index 22a9e5fed..613eb707a 100644 --- a/src/dotprod/src/dotprod_rrrf.mmx.c +++ b/src/dotprod/src/dotprod_rrrf.mmx.c @@ -34,19 +34,19 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX && HAVE_MMINTRIN_H +#if HAVE_MMX #include // MMX #endif -#if HAVE_SSE && HAVE_XMMINTRIN_H +#if HAVE_SSE #include // SSE #endif -#if HAVE_SSE2 && HAVE_EMMINTRIN_H +#if HAVE_SSE2 #include // SSE2 #endif -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 #include // SSE3 #endif @@ -243,7 +243,7 @@ int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, // aligned output array float w[4] __attribute__((aligned(16))); -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 // fold down into single value __m128 z = _mm_setzero_ps(); sum = _mm_hadd_ps(sum, z); @@ -322,7 +322,7 @@ int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, // aligned output array float w[4] __attribute__((aligned(16))); -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 // SSE3: fold down to single value using _mm_hadd_ps() __m128 z = _mm_setzero_ps(); sum0 = _mm_hadd_ps(sum0, z); diff --git a/src/dotprod/src/sumsq.avx.c b/src/dotprod/src/sumsq.avx.c index aea53fb01..76e3c59de 100644 --- a/src/dotprod/src/sumsq.avx.c +++ b/src/dotprod/src/sumsq.avx.c @@ -33,23 +33,23 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX && HAVE_MMINTRIN_H +#if HAVE_MMX #include // MMX #endif -#if HAVE_SSE && HAVE_XMMINTRIN_H +#if HAVE_SSE #include // SSE #endif -#if HAVE_SSE2 && HAVE_EMMINTRIN_H +#if HAVE_SSE2 #include // SSE2 #endif -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 #include // SSE3 #endif -#if HAVE_AVX && HAVE_IMMINTRIN_H +#if HAVE_AVX #include // AVX #endif diff --git a/src/dotprod/src/sumsq.mmx.c b/src/dotprod/src/sumsq.mmx.c index 056ba75bb..b6fb51ab0 100644 --- a/src/dotprod/src/sumsq.mmx.c +++ b/src/dotprod/src/sumsq.mmx.c @@ -33,19 +33,19 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX && HAVE_MMINTRIN_H +#if HAVE_MMX #include // MMX #endif -#if HAVE_SSE && HAVE_XMMINTRIN_H +#if HAVE_SSE #include // SSE #endif -#if HAVE_SSE2 && HAVE_EMMINTRIN_H +#if HAVE_SSE2 #include // SSE2 #endif -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 #include // SSE3 #endif @@ -79,7 +79,7 @@ float liquid_sumsqf(float * _v, // aligned output array float w[4] __attribute__((aligned(16))); -#if HAVE_SSE3 && HAVE_PMMINTRIN_H +#if HAVE_SSE3 // fold down into single value __m128 z = _mm_setzero_ps(); sum = _mm_hadd_ps(sum, z); From 268fee41ec69731d7eaaa53cfdb8756f2b1e9f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Thu, 17 Nov 2022 01:51:20 +0000 Subject: [PATCH 019/186] Add dotprod_rrrf AVX implementation Add dotprod_crcf AVX implementation Add dotprod_cccf AVX implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 12 +- src/dotprod/src/dotprod_cccf.avx.c | 404 +++++++++++++++++++++++++++++ src/dotprod/src/dotprod_crcf.avx.c | 336 ++++++++++++++++++++++++ src/dotprod/src/dotprod_rrrf.avx.c | 301 +++++++++++++++++++++ 4 files changed, 1047 insertions(+), 6 deletions(-) create mode 100644 src/dotprod/src/dotprod_cccf.avx.c create mode 100644 src/dotprod/src/dotprod_crcf.avx.c create mode 100644 src/dotprod/src/dotprod_rrrf.avx.c diff --git a/configure.ac b/configure.ac index 3d2469f47..c2442842a 100644 --- a/configure.ac +++ b/configure.ac @@ -174,16 +174,16 @@ else if [ test "$ax_cv_have_avx2_ext" = yes ]; then # AVX2 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.sse4.o \ + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ + src/dotprod/src/dotprod_crcf.avx.o \ + src/dotprod/src/dotprod_rrrf.avx.o \ src/dotprod/src/sumsq.avx.o" ARCH_OPTION='-mavx2' elif [ test "$ax_cv_have_avx_ext" = yes ]; then # AVX extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.sse4.o \ + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ + src/dotprod/src/dotprod_crcf.avx.o \ + src/dotprod/src/dotprod_rrrf.avx.o \ src/dotprod/src/sumsq.avx.o" ARCH_OPTION='-mavx' elif [ test "$ax_cv_have_sse41_ext" = yes ]; then diff --git a/src/dotprod/src/dotprod_cccf.avx.c b/src/dotprod/src/dotprod_cccf.avx.c new file mode 100644 index 000000000..80782786a --- /dev/null +++ b/src/dotprod/src/dotprod_cccf.avx.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX) +// + +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#include // AVX + +#define DEBUG_DOTPROD_CCCF_AVX 0 + +// forward declaration of internal methods +int dotprod_cccf_execute_avx(dotprod_cccf _q, + float complex * _x, + float complex * _y); + +int dotprod_cccf_execute_avx4(dotprod_cccf _q, + float complex * _x, + float complex * _y); + +// basic dot product (ordinal calculation) +int dotprod_cccf_run(float complex * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_cccf_run4(float complex * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 32-byte aligned + q->hi = (float*) _mm_malloc( 2*q->n*sizeof(float), 32 ); + q->hq = (float*) _mm_malloc( 2*q->n*sizeof(float), 32 ); + + // set coefficients, repeated + // hi = { crealf(_h[0]), crealf(_h[0]), ... crealf(_h[n-1]), crealf(_h[n-1])} + // hq = { cimagf(_h[0]), cimagf(_h[0]), ... cimagf(_h[n-1]), cimagf(_h[n-1])} + unsigned int i; + for (i=0; in; i++) { + unsigned int k = _rev ? q->n-i-1 : i; + q->hi[2*i+0] = crealf(_h[k]); + q->hi[2*i+1] = crealf(_h[k]); + + q->hq[2*i+0] = cimagf(_h[k]); + q->hq[2*i+1] = cimagf(_h[k]); + } + + // return object + return q; +} + +dotprod_cccf dotprod_cccf_create(float complex * _h, + unsigned int _n) +{ + return dotprod_cccf_create_opt(_h, _n, 0); +} + +dotprod_cccf dotprod_cccf_create_rev(float complex * _h, + unsigned int _n) +{ + return dotprod_cccf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_cccf dotprod_cccf_recreate(dotprod_cccf _q, + float complex * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_cccf_destroy(_q); + return dotprod_cccf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_cccf dotprod_cccf_recreate_rev(dotprod_cccf _q, + float complex * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_cccf_destroy(_q); + return dotprod_cccf_create_rev(_h,_n); +} + +dotprod_cccf dotprod_cccf_copy(dotprod_cccf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_cccf_copy().avx, object cannot be NULL"); + + dotprod_cccf q_copy = (dotprod_cccf)malloc(sizeof(struct dotprod_cccf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 32-byte aligned (repeated) + q_copy->hi = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 32 ); + q_copy->hq = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 32 ); + + // copy coefficients array (repeated) + // hi = { crealf(_h[0]), crealf(_h[0]), ... crealf(_h[n-1]), crealf(_h[n-1])} + // hq = { cimagf(_h[0]), cimagf(_h[0]), ... cimagf(_h[n-1]), cimagf(_h[n-1])} + memmove(q_copy->hi, q_orig->hi, 2*q_orig->n*sizeof(float)); + memmove(q_copy->hq, q_orig->hq, 2*q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + +int dotprod_cccf_destroy(dotprod_cccf _q) +{ + _mm_free(_q->hi); + _mm_free(_q->hq); + free(_q); + return LIQUID_OK; +} + +int dotprod_cccf_print(dotprod_cccf _q) +{ + printf("dotprod_cccf [avx, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf(" %3u : %12.9f +j%12.9f\n", i, _q->hi[i], _q->hq[i]); + return LIQUID_OK; +} + +// execute structured dot product +// _q : dotprod object +// _x : input array +// _y : output sample +int dotprod_cccf_execute(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // switch based on size + if (_q->n < 64) { + return dotprod_cccf_execute_avx(_q, _x, _y); + } + return dotprod_cccf_execute_avx4(_q, _x, _y); +} + +// use AVX extensions +// +// (a + jb)(c + jd) = (ac - bd) + j(ad + bc) +// +// mm_x = { x[0].real, x[0].imag, x[1].real, x[1].imag, x[2].real, x[2].imag, x[3].real, x[3].imag } +// mm_hi = { h[0].real, h[0].real, h[1].real, h[1].real, h[2].real, h[2].real, h[3].real, h[3].real } +// mm_hq = { h[0].imag, h[0].imag, h[1].imag, h[1].imag, h[2].imag, h[2].imag, h[3].imag, h[3].imag } +// +// mm_y0 = mm_x * mm_hi +// = { x[0].real * h[0].real, +// x[0].imag * h[0].real, +// x[1].real * h[1].real, +// x[1].imag * h[1].real, +// x[2].real * h[2].real, +// x[2].imag * h[2].real, +// x[3].real * h[3].real, +// x[3].imag * h[3].real }; +// +// mm_y1 = mm_x * mm_hq +// = { x[0].real * h[0].imag, +// x[0].imag * h[0].imag, +// x[1].real * h[1].imag, +// x[1].imag * h[1].imag, +// x[2].real * h[2].imag, +// x[2].imag * h[2].imag, +// x[3].real * h[3].imag, +// x[3].imag * h[3].imag }; +// +int dotprod_cccf_execute_avx(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // temporary buffers + __m256 v; // input vector + __m256 hi; // coefficients vector (real) + __m256 hq; // coefficients vector (imag) + __m256 ci; // output multiplication (v * hi) + __m256 cq; // output multiplication (v * hq) + + // aligned output array + float w[8] __attribute__((aligned(32))) = {0,0,0,0,0,0,0,0}; + + __m256 s; // dot product + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/8)) + unsigned int t = (n >> 3) << 3; + + // + unsigned int i; + for (i=0; ihi[i]); + hq = _mm256_load_ps(&_q->hq[i]); + + // compute parallel multiplications + ci = _mm256_mul_ps(v, hi); + cq = _mm256_mul_ps(v, hq); + + // shuffle values + cq = _mm256_shuffle_ps( cq, cq, _MM_SHUFFLE(2,3,0,1) ); + + // combine using addsub_ps() + s = _mm256_addsub_ps( ci, cq ); + + // accumulate + sum = _mm256_add_ps(sum, s); + } + + // unload packed array + _mm256_store_ps(w, sum); + + // add in-phase and quadrature components + w[0] += w[2] + w[4] + w[6]; // I + w[1] += w[3] + w[5] + w[7]; // Q + + //float complex total = *((float complex*)w); + float complex total = w[0] + w[1] * _Complex_I; + + // cleanup + for (i=t/2; i<_q->n; i++) + total += _x[i] * ( _q->hi[2*i] + _q->hq[2*i]*_Complex_I ); + + // set return value + *_y = total; + return LIQUID_OK; +} + +// use AVX extensions +int dotprod_cccf_execute_avx4(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m256 v0, v1, v2, v3; // input vectors + __m256 hi0, hi1, hi2, hi3; // coefficients vectors (real) + __m256 hq0, hq1, hq2, hq3; // coefficients vectors (imag) + __m256 ci0, ci1, ci2, ci3; // output multiplications (v * hi) + __m256 cq0, cq1, cq2, cq3; // output multiplications (v * hq) + + // load zeros into sum registers + __m256 sumi = _mm256_setzero_ps(); + __m256 sumq = _mm256_setzero_ps(); + + // r = 8*floor(n/32) + unsigned int r = (n >> 5) << 3; + + // + unsigned int i; + for (i=0; ihi[4*i+0]); + hi1 = _mm256_load_ps(&_q->hi[4*i+8]); + hi2 = _mm256_load_ps(&_q->hi[4*i+16]); + hi3 = _mm256_load_ps(&_q->hi[4*i+24]); + + // load real coefficients into registers (aligned) + hq0 = _mm256_load_ps(&_q->hq[4*i+0]); + hq1 = _mm256_load_ps(&_q->hq[4*i+8]); + hq2 = _mm256_load_ps(&_q->hq[4*i+16]); + hq3 = _mm256_load_ps(&_q->hq[4*i+24]); + + // compute parallel multiplications (real) + ci0 = _mm256_mul_ps(v0, hi0); + ci1 = _mm256_mul_ps(v1, hi1); + ci2 = _mm256_mul_ps(v2, hi2); + ci3 = _mm256_mul_ps(v3, hi3); + + // compute parallel multiplications (imag) + cq0 = _mm256_mul_ps(v0, hq0); + cq1 = _mm256_mul_ps(v1, hq1); + cq2 = _mm256_mul_ps(v2, hq2); + cq3 = _mm256_mul_ps(v3, hq3); + + // accumulate + sumi = _mm256_add_ps(sumi, ci0); sumq = _mm256_add_ps(sumq, cq0); + sumi = _mm256_add_ps(sumi, ci1); sumq = _mm256_add_ps(sumq, cq1); + sumi = _mm256_add_ps(sumi, ci2); sumq = _mm256_add_ps(sumq, cq2); + sumi = _mm256_add_ps(sumi, ci3); sumq = _mm256_add_ps(sumq, cq3); + } + + // shuffle values + sumq = _mm256_shuffle_ps( sumq, sumq, _MM_SHUFFLE(2,3,0,1) ); + + // unload + float wi[8] __attribute__((aligned(32))); + float wq[8] __attribute__((aligned(32))); + _mm256_store_ps(wi, sumi); + _mm256_store_ps(wq, sumq); + + // fold down (add/sub) + float complex total = + ((wi[0] - wq[0]) + (wi[2] - wq[2]) + (wi[4] - wq[4]) + (wi[6] - wq[6])) + + ((wi[1] + wq[1]) + (wi[3] + wq[3]) + (wi[5] + wq[5]) + (wi[7] + wq[7])) * _Complex_I; + + // cleanup (note: n _must_ be even) + // TODO : clean this method up + for (i=2*r; i<_q->n; i++) { + total += _x[i] * ( _q->hi[2*i] + _q->hq[2*i]*_Complex_I ); + } + + // set return value + *_y = total; + return LIQUID_OK; +} + diff --git a/src/dotprod/src/dotprod_crcf.avx.c b/src/dotprod/src/dotprod_crcf.avx.c new file mode 100644 index 000000000..6e14af3ad --- /dev/null +++ b/src/dotprod/src/dotprod_crcf.avx.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX) +// + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +#define DEBUG_DOTPROD_CRCF_AVX 0 + +// forward declaration of internal methods +int dotprod_crcf_execute_avx(dotprod_crcf _q, + float complex * _x, + float complex * _y); +int dotprod_crcf_execute_avx4(dotprod_crcf _q, + float complex * _x, + float complex * _y); + +// basic dot product (ordinal calculation) +int dotprod_crcf_run(float * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_crcf_run4(float * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 32-byte aligned + q->h = (float*) _mm_malloc( 2*q->n*sizeof(float), 32 ); + + // set coefficients, repeated + // h = { _h[0], _h[0], _h[1], _h[1], ... _h[n-1], _h[n-1]} + unsigned int i; + for (i=0; in; i++) { + unsigned int k = _rev ? q->n-i-1 : i; + q->h[2*i+0] = _h[k]; + q->h[2*i+1] = _h[k]; + } + + // return object + return q; +} + +dotprod_crcf dotprod_crcf_create(float * _h, + unsigned int _n) +{ + return dotprod_crcf_create_opt(_h, _n, 0); +} + +dotprod_crcf dotprod_crcf_create_rev(float * _h, + unsigned int _n) +{ + return dotprod_crcf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_crcf dotprod_crcf_recreate(dotprod_crcf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_crcf_destroy(_q); + return dotprod_crcf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_crcf dotprod_crcf_recreate_rev(dotprod_crcf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_crcf_destroy(_q); + return dotprod_crcf_create_rev(_h,_n); +} + +dotprod_crcf dotprod_crcf_copy(dotprod_crcf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_crcf_copy().avx, object cannot be NULL"); + + dotprod_crcf q_copy = (dotprod_crcf)malloc(sizeof(struct dotprod_crcf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 32-byte aligned (repeated) + q_copy->h = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 32 ); + + // copy coefficients array (repeated) + // h = { _h[0], _h[0], _h[1], _h[1], ... _h[n-1], _h[n-1]} + memmove(q_copy->h, q_orig->h, 2*q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + + +int dotprod_crcf_destroy(dotprod_crcf _q) +{ + _mm_free(_q->h); + free(_q); + return LIQUID_OK; +} + +int dotprod_crcf_print(dotprod_crcf _q) +{ + // print coefficients to screen, skipping odd entries (due + // to repeated coefficients) + printf("dotprod_crcf [avx, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf(" %3u : %12.9f\n", i, _q->h[2*i]); + return LIQUID_OK; +} + +// +int dotprod_crcf_execute(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // switch based on size + if (_q->n < 64) { + return dotprod_crcf_execute_avx(_q, _x, _y); + } + return dotprod_crcf_execute_avx4(_q, _x, _y); +} + +// use AVX extensions +int dotprod_crcf_execute_avx(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m256 v; // input vector + __m256 h; // coefficients vector + __m256 s; // dot product + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/8)) + unsigned int t = (n >> 3) << 3; + + // + unsigned int i; + for (i=0; ih[i]); + + // compute multiplication + s = _mm256_mul_ps(v, h); + + // accumulate + sum = _mm256_add_ps(sum, s); + } + + // aligned output array + float w[8] __attribute__((aligned(32))); + + // unload packed array + _mm256_store_ps(w, sum); + + // add in-phase and quadrature components + w[0] += w[2] + w[4] + w[6]; + w[1] += w[3] + w[5] + w[7]; + + // cleanup (note: n _must_ be even) + for (; ih[i ]; + w[1] += x[i+1] * _q->h[i+1]; + } + + // set return value + *_y = w[0] + _Complex_I*w[1]; + return LIQUID_OK; +} + +// use AVX extensions +int dotprod_crcf_execute_avx4(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m256 v0, v1, v2, v3; // input vectors + __m256 h0, h1, h2, h3; // coefficients vectors + __m256 s0, s1, s2, s3; // dot products [re, im, re, im] + + // load zeros into sum registers + __m256 sum0 = _mm256_setzero_ps(); + __m256 sum1 = _mm256_setzero_ps(); + __m256 sum2 = _mm256_setzero_ps(); + __m256 sum3 = _mm256_setzero_ps(); + + // r = 8*floor(n/32) + unsigned int r = (n >> 5) << 3; + + // + unsigned int i; + for (i=0; ih[4*i+0]); + h1 = _mm256_load_ps(&_q->h[4*i+8]); + h2 = _mm256_load_ps(&_q->h[4*i+16]); + h3 = _mm256_load_ps(&_q->h[4*i+24]); + + // compute multiplication + s0 = _mm256_mul_ps(v0, h0); + s1 = _mm256_mul_ps(v1, h1); + s2 = _mm256_mul_ps(v2, h2); + s3 = _mm256_mul_ps(v3, h3); + + // parallel addition + sum0 = _mm256_add_ps( sum0, s0 ); + sum1 = _mm256_add_ps( sum1, s1 ); + sum2 = _mm256_add_ps( sum2, s2 ); + sum3 = _mm256_add_ps( sum3, s3 ); + } + + // fold down + sum0 = _mm256_add_ps( sum0, sum1 ); + sum2 = _mm256_add_ps( sum2, sum3 ); + sum0 = _mm256_add_ps( sum0, sum2 ); + + // aligned output array + float w[8] __attribute__((aligned(32))); + + // unload packed array and perform manual sum + _mm256_store_ps(w, sum0); + w[0] += w[2] + w[4] + w[6]; + w[1] += w[3] + w[5] + w[7]; + + // cleanup (note: n _must_ be even) + for (i=4*r; ih[i ]; + w[1] += x[i+1] * _q->h[i+1]; + } + + // set return value + *_y = w[0] + w[1]*_Complex_I; + return LIQUID_OK; +} + diff --git a/src/dotprod/src/dotprod_rrrf.avx.c b/src/dotprod/src/dotprod_rrrf.avx.c new file mode 100644 index 000000000..2af478375 --- /dev/null +++ b/src/dotprod/src/dotprod_rrrf.avx.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX) +// + +#include +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#include // AVX + +#define DEBUG_DOTPROD_RRRF_AVX 0 + +// internal methods +int dotprod_rrrf_execute_avx(dotprod_rrrf _q, + float * _x, + float * _y); +int dotprod_rrrf_execute_avxu(dotprod_rrrf _q, + float * _x, + float * _y); + +// basic dot product (ordinal calculation) +int dotprod_rrrf_run(float * _h, + float * _x, + unsigned int _n, + float * _y) +{ + float r=0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_rrrf_run4(float * _h, + float * _x, + unsigned int _n, + float * _y) +{ + float r=0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 32-byte aligned + q->h = (float*) _mm_malloc( q->n*sizeof(float), 32); + + // set coefficients + unsigned int i; + for (i=0; in; i++) + q->h[i] = _h[_rev ? q->n-i-1 : i]; + + // return object + return q; +} + +dotprod_rrrf dotprod_rrrf_create(float * _h, + unsigned int _n) +{ + return dotprod_rrrf_create_opt(_h, _n, 0); +} + +dotprod_rrrf dotprod_rrrf_create_rev(float * _h, + unsigned int _n) +{ + return dotprod_rrrf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_rrrf dotprod_rrrf_recreate(dotprod_rrrf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_rrrf_destroy(_q); + return dotprod_rrrf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_rrrf dotprod_rrrf_recreate_rev(dotprod_rrrf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_rrrf_destroy(_q); + return dotprod_rrrf_create_rev(_h,_n); +} + +dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_rrrf_copy().avx, object cannot be NULL"); + + dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 32-byte aligned + q_copy->h = (float*) _mm_malloc( q_copy->n*sizeof(float), 32 ); + + // copy coefficients array + memmove(q_copy->h, q_orig->h, q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + +int dotprod_rrrf_destroy(dotprod_rrrf _q) +{ + _mm_free(_q->h); + free(_q); + return LIQUID_OK; +} + +int dotprod_rrrf_print(dotprod_rrrf _q) +{ + printf("dotprod_rrrf [avx, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf("%3u : %12.9f\n", i, _q->h[i]); + return LIQUID_OK; +} + +// +int dotprod_rrrf_execute(dotprod_rrrf _q, + float * _x, + float * _y) +{ + // switch based on size + if (_q->n < 32) { + return dotprod_rrrf_execute_avx(_q, _x, _y); + } + return dotprod_rrrf_execute_avxu(_q, _x, _y); +} + +// use AVX extensions +int dotprod_rrrf_execute_avx(dotprod_rrrf _q, + float * _x, + float * _y) +{ + __m256 v; // input vector + __m256 h; // coefficients vector + __m256 s; // dot product + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/8)) + unsigned int t = (_q->n >> 3) << 3; + + // + unsigned int i; + for (i=0; ih[i]); + + // compute dot product + s = _mm256_dp_ps(v, h, 0xff); + + // parallel addition + sum = _mm256_add_ps( sum, s ); + } + + // aligned output array + float w[8] __attribute__((aligned(32))); + + // unload packed array + _mm256_store_ps(w, sum); + float total = w[0] + w[4]; + + // cleanup + for (; i<_q->n; i++) + total += _x[i] * _q->h[i]; + + // set return value + *_y = total; + return LIQUID_OK; +} + +// use AVX extensions (unrolled) +int dotprod_rrrf_execute_avxu(dotprod_rrrf _q, + float * _x, + float * _y) +{ + __m256 v0, v1, v2, v3; + __m256 h0, h1, h2, h3; + __m256 s0, s1, s2, s3; + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/32)) + unsigned int r = (_q->n >> 5) << 3; + + // + unsigned int i; + for (i=0; ih[4*i+ 0]); + h1 = _mm256_load_ps(&_q->h[4*i+ 8]); + h2 = _mm256_load_ps(&_q->h[4*i+16]); + h3 = _mm256_load_ps(&_q->h[4*i+24]); + + // compute dot products + s0 = _mm256_dp_ps(v0, h0, 0xff); + s1 = _mm256_dp_ps(v1, h1, 0xff); + s2 = _mm256_dp_ps(v2, h2, 0xff); + s3 = _mm256_dp_ps(v3, h3, 0xff); + + // parallel addition + // FIXME: these additions are by far the limiting factor + sum = _mm256_add_ps( sum, s0 ); + sum = _mm256_add_ps( sum, s1 ); + sum = _mm256_add_ps( sum, s2 ); + sum = _mm256_add_ps( sum, s3 ); + } + + // aligned output array + float w[8] __attribute__((aligned(32))); + + // unload packed array + _mm256_store_ps(w, sum); + float total = w[0] + w[4]; + + // cleanup + for (i=4*r; i<_q->n; i++) + total += _x[i] * _q->h[i]; + + // set return value + *_y = total; + return LIQUID_OK; +} + From 2ae3fd73a76cc650cbde8f6f275ece391e1c4f67 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 19 Nov 2022 07:27:57 -0500 Subject: [PATCH 020/186] benchmark: using python-based comparison script in favor of C version --- makefile.in | 5 - scripts/benchmark_compare.c | 339 ----------------------------------- scripts/benchmark_compare.py | 31 ++++ 3 files changed, 31 insertions(+), 344 deletions(-) delete mode 100644 scripts/benchmark_compare.c create mode 100755 scripts/benchmark_compare.py diff --git a/makefile.in b/makefile.in index 596320379..954b00068 100644 --- a/makefile.in +++ b/makefile.in @@ -1472,10 +1472,6 @@ $(bench_prog): $(bench_prog).o $(benchmark_obj) $(benchmark_extra_obj) ${ARCHIVE bench: $(bench_prog) ./$(bench_prog) -o benchmark.json -# benchmark compare script -scripts/benchmark_compare : % : %.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) - # fftbench program bench/fftbench.o : %.o : %.c $(CC) $(BENCH_CPPFLAGS) $(BENCH_CFLAGS) $< -c -o $@ @@ -1486,7 +1482,6 @@ bench/fftbench : % : %.o ${ARCHIVE_LIB} # clean up the generated files clean-bench: $(RM) benchmark_include.h $(bench_prog).o $(bench_prog) - $(RM) scripts/benchmark_compare $(RM) $(benchmark_obj) $(RM) $(benchmark_extra_obj) $(RM) bench/fftbench.o diff --git a/scripts/benchmark_compare.c b/scripts/benchmark_compare.c deleted file mode 100644 index dbb138aef..000000000 --- a/scripts/benchmark_compare.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (c) 2007 - 2015 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// benchmark_compare.c -// -// compare benchmark runs -// - -#include -#include -#include -#include - -// print usage/help message -void usage() -{ - printf("benchmark_compare [old_benchmark] [new_benchmark]\n"); -} - -// define benchmark_t -struct benchmark_t { - //unsigned int id; - char name[64]; - //unsigned int name_len; - //unsigned int num_trials; - //float extime; - //float rate; - float cycles_per_trial; - - // link to other benchmark - struct benchmark_t * link; -}; - -struct benchlist_s { - struct benchmark_t * benchmarks; - unsigned int num_benchmarks; -}; - -typedef struct benchlist_s * benchlist; - -// -benchlist benchlist_create(); -void benchlist_destroy(benchlist _q); -void benchlist_print(benchlist _q); -void benchlist_append(benchlist _q, - char * _name, - float _cycles_per_trial); - -void benchlist_link(benchlist _q0, - benchlist _q1); - -// is line a comment? -int parse_file(const char * _filename, - benchlist _benchmarks); - -// read line from file -// _fid : input file -// _buffer : output buffer -// _n : buffer length -void readline(FILE * _fid, - char * _buffer, - unsigned int _n); - -// is line a comment? -int line_is_comment(char * _buffer); - -int main(int argc, char*argv[]) -{ - if (argc != 3) { - usage(); - exit(1); - } - - // parse old benchmarks - benchlist benchmarks_old = benchlist_create(); - parse_file(argv[1], benchmarks_old); - //benchlist_print(benchmarks_old); - - // parse new benchmarks - benchlist benchmarks_new = benchlist_create(); - parse_file(argv[2], benchmarks_new); - //benchlist_print(benchmarks_new); - - // link benchmarks and print results - benchlist_link(benchmarks_old, benchmarks_new); - benchlist_print(benchmarks_old); - - // destroy benchmark lists - benchlist_destroy(benchmarks_old); - benchlist_destroy(benchmarks_new); - - printf("done.\n"); - return 0; -} - -// -benchlist benchlist_create() -{ - benchlist q = (benchlist) malloc(sizeof(struct benchlist_s)); - - // initialize internals - q->benchmarks = NULL; - q->num_benchmarks = 0; - - // return main object - return q; -} - -void benchlist_destroy(benchlist _q) -{ - // free internals - if (_q->benchmarks != NULL) - free(_q->benchmarks); - - // free main object memory - free(_q); -} - -#if 0 -void benchlist_print(benchlist _q) -{ - unsigned int i; - printf("benchlist [%u]:\n", _q->num_benchmarks); - for (i=0; i<_q->num_benchmarks; i++) { - printf(" - %-32s : %14.2f", _q->benchmarks[i].name, - _q->benchmarks[i].cycles_per_trial); - if (_q->benchmarks[i].link == NULL) { - printf("\n"); - } else { - // print delta - float cycles_old = _q->benchmarks[i].cycles_per_trial; - float cycles_new = _q->benchmarks[i].link->cycles_per_trial; - float delta = (cycles_new - cycles_old)/cycles_old; - - // print static value - printf(" : %12.3f %8.2f %%\n", cycles_new, 100*delta); - } - - } -} -#else -void benchlist_print(benchlist _q) -{ - unsigned int i; - printf("benchlist [%u]:\n", _q->num_benchmarks); - for (i=0; i<_q->num_benchmarks; i++) { - if (_q->benchmarks[i].link == NULL) - continue; - - printf(" - %-28s ", _q->benchmarks[i].name); - // print delta - float cycles_old = _q->benchmarks[i].cycles_per_trial; - float cycles_new = _q->benchmarks[i].link->cycles_per_trial; - float speedup = (cycles_old + 1e-6f) / (cycles_new + 1e-6f); - - // print bar graph style response - unsigned int num_spaces = 20; - unsigned int num_hashes = (unsigned int) (0.4*fabsf(log2f(speedup)) * num_spaces); - if (num_hashes > num_spaces) num_hashes = num_spaces; - unsigned int j; - if (speedup > 1.0) { - // newer is better - for (j=0; jnum_benchmarks; i++) { - if ( strncmp(_q->benchmarks[i].name, _name, 64) == 0) { - fprintf(stderr,"warning: cannot append benchmark '%s'; benchmark already exists\n", _name); - return; - } - } - - // re-allocate memory array - _q->num_benchmarks++; - _q->benchmarks = (struct benchmark_t*)realloc(_q->benchmarks, (_q->num_benchmarks)*sizeof(struct benchmark_t)); - - //printf("appending '%s' (%f)\n", _name, _cycles_per_trial); - - // - // append new element - // - - // copy name - strncpy(_q->benchmarks[_q->num_benchmarks-1].name, _name, 64); - - // copy properties - _q->benchmarks[_q->num_benchmarks-1].cycles_per_trial = _cycles_per_trial; - - // set link to NULL - _q->benchmarks[_q->num_benchmarks-1].link = NULL; -} - -void benchlist_link(benchlist _q0, - benchlist _q1) -{ - // link nodes - unsigned int i; - unsigned int j; - for (i=0; i<_q0->num_benchmarks; i++) { - for (j=0; j<_q1->num_benchmarks; j++) { - if ( strncmp(_q0->benchmarks[i].name, _q1->benchmarks[j].name, 64)==0 ) { - // match found; link nodes - _q0->benchmarks[i].link = &_q1->benchmarks[j]; - _q1->benchmarks[j].link = &_q0->benchmarks[i]; - } - } - } -} - - -// is line a comment? -int parse_file(const char * _filename, - benchlist _benchmarks) -{ - // - FILE * fid = fopen(_filename,"r"); - if (!fid) { - fprintf(stderr,"error: parse_file(), could not open '%s' for reading\n", _filename); - exit(1); - } - - printf("parsing '%s'...\n", _filename); - char buffer[256]; // line buffer - - int id; - char name[64]; - unsigned long int num_trials; - float execution_time; - float rate; - float cycles_per_trial; - - do { - // read line into buffer - readline(fid, buffer, 256); - - // skip comment lines - if (line_is_comment(buffer)) - continue; - - // scan line for results - int results = sscanf(buffer,"%d %s %lu %f %f %f\n", - &id, - name, - &num_trials, - &execution_time, - &rate, - &cycles_per_trial); - if (results < 6) { - //fprintf(stderr,"warning: skipping line '%s'\n", buffer); - continue; - } - - // append... - benchlist_append(_benchmarks, name, cycles_per_trial); - - } while (!feof(fid)); - - // close the file - fclose(fid); - - // return - return 0; -} - - -// read line from file -// _fid : input file -// _buffer : output buffer -// _n : buffer length -void readline(FILE * _fid, - char * _buffer, - unsigned int _n) -{ - unsigned int i=0; - char c; - while ( (c = fgetc(_fid)) != EOF && c != '\n' && i < _n-1) { - _buffer[i] = c; - i++; - } - _buffer[i] = '\0'; -} - -// is line a comment? -int line_is_comment(char * _buffer) -{ - // read first non-whitespace character - unsigned int i=0; - char c; - do { - c = _buffer[i]; - i++; - } while (c == ' ' || c == '\t'); - - if (c == '#' || c == '\0') - return 1; - - return 0; -} - diff --git a/scripts/benchmark_compare.py b/scripts/benchmark_compare.py new file mode 100755 index 000000000..328e134c1 --- /dev/null +++ b/scripts/benchmark_compare.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +'''Compare benchmark results''' +import argparse, json, numpy as np + +# parse command-line arguments +p = argparse.ArgumentParser(description=__doc__) +p.add_argument('old', help='old benchmark file (.json)') +p.add_argument('new', help='new benchmark file (.json)') +p.add_argument('-thresh', default=1.5, help='threshold for displaying deltas') +args = p.parse_args() + +# load json files +v0 = json.load(open(args.old,'r')) +v1 = json.load(open(args.new,'r')) + +# convert to dictionary and get set of common names +b0 = {v['name']:v for v in v0['benchmarks']} +b1 = {v['name']:v for v in v1['benchmarks']} +set_common = set(b0.keys()).intersection(set(b1.keys())) + +# iterate over common benchmarks and print results +for key in set_common: + r0,r1 = b0[key], b1[key] + if 0 in (r0['trials'],r1['trials']): + continue + rate = r1['rate'] / r0['rate'] + if np.exp(np.abs(np.log(rate))) < args.thresh: + continue + + print(' %32s, rate = %12.8f' % (key, rate)) + From 73d2be4988a06522916df5f9a3d3366314df88a9 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 20 Nov 2022 06:53:32 -0500 Subject: [PATCH 021/186] build: bumping versions of config.guess, config.sub --- scripts/config.guess | 42 +++++++++++++++++++++++++++++++----------- scripts/config.sub | 29 +++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/scripts/config.guess b/scripts/config.guess index 7f76b6228..980b02083 100644 --- a/scripts/config.guess +++ b/scripts/config.guess @@ -4,7 +4,7 @@ # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2022-01-09' +timestamp='2022-09-17' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -966,6 +966,12 @@ EOF GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; @@ -1036,7 +1042,7 @@ EOF k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; - loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + loongarch32:Linux:*:* | loongarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) @@ -1151,16 +1157,27 @@ EOF ;; x86_64:Linux:*:*) set_cc_for_build + CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_X32 >/dev/null - then - LIBCABI=${LIBC}x32 - fi + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac fi - GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI + GUESS=$CPU-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC @@ -1367,8 +1384,11 @@ EOF BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; - x86_64:Haiku:*:*) - GUESS=x86_64-unknown-haiku + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE diff --git a/scripts/config.sub b/scripts/config.sub index dba16e84c..baf1512b3 100644 --- a/scripts/config.sub +++ b/scripts/config.sub @@ -4,7 +4,7 @@ # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2022-01-03' +timestamp='2022-09-17' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -145,7 +145,7 @@ case $1 in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ - | storm-chaos* | os2-emx* | rtmk-nova*) + | storm-chaos* | os2-emx* | rtmk-nova* | managarm-*) basic_machine=$field1 basic_os=$maybe_os ;; @@ -1207,7 +1207,7 @@ case $cpu-$vendor in | k1om \ | le32 | le64 \ | lm32 \ - | loongarch32 | loongarch64 | loongarchx32 \ + | loongarch32 | loongarch64 \ | m32c | m32r | m32rle \ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ @@ -1341,6 +1341,10 @@ EOF kernel=linux os=`echo "$basic_os" | sed -e 's|linux|gnu|'` ;; + managarm*) + kernel=managarm + os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'` + ;; *) kernel= os=$basic_os @@ -1754,7 +1758,7 @@ case $os in | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ - | fiwix* ) + | fiwix* | mlibc* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) @@ -1762,6 +1766,9 @@ case $os in ;; none) ;; + kernel* ) + # Restricted further below + ;; *) echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 exit 1 @@ -1772,16 +1779,26 @@ esac # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ - | linux-musl* | linux-relibc* | linux-uclibc* ) + | linux-musl* | linux-relibc* | linux-uclibc* | linux-mlibc* ) ;; uclinux-uclibc* ) ;; - -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) + managarm-mlibc* | managarm-kernel* ) + ;; + -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* | -mlibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; + -kernel* ) + echo "Invalid configuration \`$1': \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + *-kernel* ) + echo "Invalid configuration \`$1': \`$kernel' does not support \`$os'." 1>&2 + exit 1 + ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; vxworks-simlinux | vxworks-simwindows | vxworks-spe) From a6d89a5909276d712a3c1934312d3a795e53288c Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 6 Oct 2022 08:05:38 -0400 Subject: [PATCH 022/186] build: bumping version number in preparation for release --- HISTORY | 11 +++++++++++ configure.ac | 2 +- include/liquid.h | 4 ++-- library.json | 2 +- scripts/config.h | 4 ++-- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/HISTORY b/HISTORY index 881d18ca9..4305e432e 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,17 @@ ## Latest improvements ## +## Improvements for v1.5.0 ## + + * autotest + - increased coverage testing (81% across entire project) + * build + - added support for PlatformIO (https://platformio.org) for embeedded + development + - incorporated recursive copy() methods to objects to facilitate c++ copy + constructors for bindings + - improved support for error codes + ## Improvements for v1.4.0 ## * autotest diff --git a/configure.ac b/configure.ac index c2442842a..12d4b7cd8 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Process with autoconf to generate configure script # -AC_INIT([liquid-dsp],[1.4],[joseph@liquidsdr.org]) +AC_INIT([liquid-dsp],[1.5],[joseph@liquidsdr.org]) AC_CONFIG_SRCDIR([src/libliquid.c]) AC_CONFIG_MACRO_DIR([scripts]) diff --git a/include/liquid.h b/include/liquid.h index 7d8f388d1..d19dc02ec 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -51,8 +51,8 @@ extern "C" { // LIQUID_VERSION = "X.Y.Z" // LIQUID_VERSION_NUMBER = (X*1000000 + Y*1000 + Z) // -#define LIQUID_VERSION "1.4.0" -#define LIQUID_VERSION_NUMBER 1004000 +#define LIQUID_VERSION "1.5.0" +#define LIQUID_VERSION_NUMBER 1005000 // // Run-time library version numbers diff --git a/library.json b/library.json index fea268c46..6f14be837 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "liquid-dsp", - "version": "1.4.0", + "version": "1.5.0", "description": "Software-defined radio digital signal processing library", "homepage": "https://liquidsdr.org", "keywords": diff --git a/scripts/config.h b/scripts/config.h index d3c717e33..3b81cd5e4 100644 --- a/scripts/config.h +++ b/scripts/config.h @@ -169,7 +169,7 @@ #define PACKAGE_NAME "liquid-dsp" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "liquid-dsp 1.4.0" +#define PACKAGE_STRING "liquid-dsp 1.5.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "liquid-dsp" @@ -178,7 +178,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.4.0" +#define PACKAGE_VERSION "1.5.0" /* The size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 From e9155f513b25d24527f26bb1d49df2fb317e85f7 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 20 Nov 2022 08:11:10 -0500 Subject: [PATCH 023/186] build: updating HISTORY with notes/updates since last release --- HISTORY | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/HISTORY b/HISTORY index 4305e432e..d1d7b6993 100644 --- a/HISTORY +++ b/HISTORY @@ -3,14 +3,62 @@ ## Improvements for v1.5.0 ## - * autotest - - increased coverage testing (81% across entire project) * build - added support for PlatformIO (https://platformio.org) for embeedded - development + development (thanks, @jcw!) - incorporated recursive copy() methods to objects to facilitate c++ copy - constructors for bindings - - improved support for error codes + constructors for bindings; now all objects can be deep copied to a new + object with their entire memory and state preserved + - added convenience method to malloc and copy memory arrays + - improved support for error codes across objects and methods + - cleaned up spelling errors across project (thanks, @nowls!) + - scrubbed function argument variable names to avoid underscore followed + by a capital letter, causing trouble with pre-compiler processing + - added basic test to check linking to installed library, incorporating + into CI/CD pipelines + - added more example programs + * autotest + - increased coverage testing (81% across entire project) + - added the ability to "hammer" a particular test by running repeatedly on + incremental seeds to assess specific edge cases (e.g. with random data) + - added timer to show execution time for each test and identify areas for + speed improvements + - added methods for testing spectral response of various fields + - added special directory for storing output logs: autotest/logs/ + * benchmark + - replacing old C-based benchmark comparison with simpler python version + * dotprod + - added support for AVX SIMD in vector dot products (thanks, @vankxr!) + * fft + - adding macro to allow for aligned memory allocation if FFTW is used + (thanks, @nowls!) + * filter + - added new halfband filter design using Parks-McClellan algorithm and + qs1dsearch method to provide as exact a specification as possible + - added method to retrieve filter response from coefficients array + - dds: adding methods to get/set scale + - firhilb, iirhilb: added block processing method + - msresamp, resamp: adding method to provide the exact number of output + samples with provided input size + - msresamp2, resamp2: using better halfband filter design for exact user + specifications + - resamp: adding methods to get/set scale, fixing filter bank resolution + (was hard-coded, now respects user configuration) + * framing + - framesync64: added methods to set callback and userdata (context) + fields, adding support for exporting debugging files for post-analysis + as well as python script for processing, adding better estimate of + error vector magnitude + - msource: added convenience method to recall number of samples generated + by each source + - ofdmflexframesync: added methods to set callback and userdata (context) + fields + - qpacketmodem: returning much better estimate of error vector magnitude + - qsource: fixed issue with carrier frequency adjustment + * optim + - added qs1dsearch object to perform quad-section 1-dimensional search: + similar to bisection search, but to find potentially non-continuous + minimum/maximum of function ## Improvements for v1.4.0 ## From c3e0de7e89cdcc0ec5da9ccaeb5738496263d4c0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 20 Nov 2022 15:31:24 -0500 Subject: [PATCH 024/186] build: explicitly printing version numbers in linker test --- scripts/liquid_linker_test.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/liquid_linker_test.c b/scripts/liquid_linker_test.c index a63d3f9d1..79a0b153b 100644 --- a/scripts/liquid_linker_test.c +++ b/scripts/liquid_linker_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2022 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,14 @@ int main() { - // test liquid string version + // test liquid version number + printf("checking installed liquid version numbers...\n"); + printf(" header : %d\n", LIQUID_VERSION_NUMBER); + printf(" library : %d\n", liquid_libversion_number()); LIQUID_VALIDATE_LIBVERSION; // create object, print and return + printf("creating test object...\n"); resamp_crcf q = resamp_crcf_create(0.12345f, 12, 0.25f, 60.0f, 256); resamp_crcf_print(q); resamp_crcf_destroy(q); From 930356b336bbcb9ecffb444d0b58f210aa9718c4 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 14 Jan 2023 08:19:22 -0500 Subject: [PATCH 025/186] dds: prepending macro with LIQUID_ to allow for API parsing --- include/liquid.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index d19dc02ec..454ca0ac6 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -4791,7 +4791,7 @@ LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_CCCF, // Direct digital [up/down] synthesizer // -#define DDS_MANGLE_CCCF(name) LIQUID_CONCAT(dds_cccf,name) +#define LIQUID_DDS_MANGLE_CCCF(name) LIQUID_CONCAT(dds_cccf,name) #define LIQUID_DDS_DEFINE_API(DDS,TO,TC,TI) \ \ @@ -4857,7 +4857,7 @@ int DDS(_interp_execute)(DDS() _q, \ TI _x, \ TO * _y); \ -LIQUID_DDS_DEFINE_API(DDS_MANGLE_CCCF, +LIQUID_DDS_DEFINE_API(LIQUID_DDS_MANGLE_CCCF, liquid_float_complex, liquid_float_complex, liquid_float_complex) From d78f5c1aa91643d64863d97005db57e36458c4ab Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 8 Feb 2023 23:03:03 -0500 Subject: [PATCH 026/186] gcd: fixing logic for stopping loop, adding more test cases --- src/math/src/modular_arithmetic.c | 2 +- src/math/tests/gcd_autotest.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/math/src/modular_arithmetic.c b/src/math/src/modular_arithmetic.c index 01d4b8a39..5146d73b5 100644 --- a/src/math/src/modular_arithmetic.c +++ b/src/math/src/modular_arithmetic.c @@ -131,7 +131,7 @@ unsigned int liquid_gcd(unsigned int _p, // dumb, slow method unsigned int gcd = 1; unsigned int r = 2; // root - while ( r*r <= _p ) { + while ( r <= _q ) { while ((_p % r)==0 && (_q % r) == 0) { _p /= r; _q /= r; diff --git a/src/math/tests/gcd_autotest.c b/src/math/tests/gcd_autotest.c index fd1fcb81f..2dfa81338 100644 --- a/src/math/tests/gcd_autotest.c +++ b/src/math/tests/gcd_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2018 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -63,5 +63,9 @@ void autotest_gcd_base() testbench_gcd( 2*2*3*5*7, 2*3, 17); testbench_gcd( 2*2*3*5*7, 2*3, 17*17); testbench_gcd( 2*2*3*5*7, 2*3, 17*17*17); + testbench_gcd( 11, 3, 1); + testbench_gcd( 3, 127, 131); + testbench_gcd( 131, 127, 3); + testbench_gcd( 127, 131, 3); } From f86127ade747da3228923a18c8c15ea3ba9d265b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 22 Feb 2023 11:44:50 -0500 Subject: [PATCH 027/186] qdsync/autotest: making testbench specific to linear --- src/framing/tests/qdsync_cccf_autotest.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 39b252aa4..826fbf74d 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -57,14 +57,16 @@ int autotest_qdsync_callback(float complex * _buf, return 0; } -void testbench_qdsync(unsigned int _k) +void testbench_qdsync_linear(unsigned int _k, + unsigned int _m, + float _beta) { // options unsigned int seq_len = 240; // number of sync symbols unsigned int payload_len = 800; // number of payload symbols unsigned int k = _k; // samples/symbol - unsigned int m = 7; // filter delay [symbols] - float beta = 0.3f; // excess bandwidth factor + unsigned int m = _m; // filter delay [symbols] + float beta = _beta; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float nstd = 0.001f; @@ -158,7 +160,7 @@ void testbench_qdsync(unsigned int _k) } // test specific configurations -void autotest_qdsync_k2() { testbench_qdsync(2); } -void autotest_qdsync_k3() { testbench_qdsync(3); } -void autotest_qdsync_k4() { testbench_qdsync(4); } +void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } +void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } +void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } From 0aa0e87eea1fce7d0c7c0a36741c8e0695631ee0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 22 Feb 2023 12:21:16 -0500 Subject: [PATCH 028/186] qdsync/autotest: simplifying test to use entire sequence --- src/framing/tests/qdsync_cccf_autotest.c | 72 +++++++++--------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 826fbf74d..a3d8e0841 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -27,7 +27,6 @@ // common structure for relaying information to/from callback typedef struct { - unsigned int seq_len; float complex * buf; unsigned int buf_len; unsigned int count; @@ -43,16 +42,11 @@ int autotest_qdsync_callback(float complex * _buf, autotest_qdsync_s * q = (autotest_qdsync_s *) _context; unsigned int i; for (i=0; i<_buf_len; i++) { - if (q->count < q->seq_len) { - // preamble sequence - } else if (q->count < q->seq_len + q->buf_len) { - // payload - q->buf[q->count - q->seq_len] = _buf[i]; - } if (q->count == q->seq_len + q->buf_len) { - // buffer full; reset synchronizer - return 1; - } - q->count++; + if (q->count == q->buf_len) + return 1; // buffer full; reset synchronizer + + // save payload + q->buf[q->count++] = _buf[i]; } return 0; } @@ -62,8 +56,7 @@ void testbench_qdsync_linear(unsigned int _k, float _beta) { // options - unsigned int seq_len = 240; // number of sync symbols - unsigned int payload_len = 800; // number of payload symbols + unsigned int seq_len = 1200; // total number of sync symbols unsigned int k = _k; // samples/symbol unsigned int m = _m; // filter delay [symbols] float beta = _beta; // excess bandwidth factor @@ -71,24 +64,17 @@ void testbench_qdsync_linear(unsigned int _k, float nstd = 0.001f; // generate synchronization sequence (QPSK symbols) - float complex seq[seq_len]; + float complex seq_tx[seq_len]; // transmitted + float complex seq_rx[seq_len]; // received with initial correction unsigned int i; for (i=0; i Date: Wed, 22 Feb 2023 13:06:21 -0500 Subject: [PATCH 029/186] qdsync: adding copy() method, autotest --- src/framing/src/qdsync_cccf.c | 15 ++-- src/framing/tests/qdsync_cccf_autotest.c | 90 +++++++++++++++++++++++- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 70995eb7b..1469b4f9c 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -113,7 +113,7 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, q->mf = firpfb_crcf_create_rnyquist(q->ftype, q->npfb, q->k, q->m, q->beta); // allocate buffer for storing output samples - q->buf_out_len = 64; // TODO: make user-defined? + q->buf_out_len = 64; // user can re-size this later q->buf_out = (float complex*) malloc(q->buf_out_len*sizeof(float complex)); // set callback and context values @@ -128,7 +128,6 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // copy object qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) { -#if 0 // validate input if (q_orig == NULL) return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); @@ -137,14 +136,20 @@ qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) qdsync_cccf q_copy = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); memmove(q_copy, q_orig, sizeof(struct qdsync_cccf_s)); + // set callback and userdata fields + q_copy->callback = q_orig->callback; + q_copy->context = q_orig->context; + // copy sub-objects q_copy->detector = qdetector_cccf_copy(q_orig->detector); + q_copy->mixer = nco_crcf_copy (q_orig->mixer); + q_copy->mf = firpfb_crcf_copy (q_orig->mf); + + // copy memory in new allocation + q_copy->buf_out = (float complex*)liquid_malloc_copy(q_orig->buf_out, q_orig->buf_out_len, sizeof(float complex)); // return new object return q_copy; -#else - return liquid_error_config("qdsync_cccf_copy(), method not yet implemented"); -#endif } int qdsync_cccf_destroy(qdsync_cccf _q) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index a3d8e0841..d8e0a78ad 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -22,11 +22,13 @@ #include #include +#include #include "autotest/autotest.h" #include "liquid.h" // common structure for relaying information to/from callback typedef struct { + int id; float complex * buf; unsigned int buf_len; unsigned int count; @@ -37,9 +39,9 @@ int autotest_qdsync_callback(float complex * _buf, unsigned int _buf_len, void * _context) { - printf("qdsync callback got %u samples\n", _buf_len); // save samples to buffer as appropriate autotest_qdsync_s * q = (autotest_qdsync_s *) _context; + printf("[%d] qdsync callback got %u samples\n", q->id, _buf_len); unsigned int i; for (i=0; i<_buf_len; i++) { if (q->count == q->buf_len) @@ -73,7 +75,7 @@ void testbench_qdsync_linear(unsigned int _k, } // create sync object, only using first few symbols - autotest_qdsync_s obj = {.buf=seq_rx, .buf_len=seq_len, .count=0}; + autotest_qdsync_s obj = {.id=0, .buf=seq_rx, .buf_len=seq_len, .count=0}; qdsync_cccf q = qdsync_cccf_create_linear(seq_tx, 240, ftype, k, m, beta, autotest_qdsync_callback, (void*)&obj); qdsync_cccf_set_range(q, 0.001f); @@ -148,3 +150,87 @@ void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } +// test copying from one object to another +void autotest_qdsync_cccf_copy() +{ + // options + unsigned int seq_len= 2400; // total number of symbols in sequence + unsigned int split = 1033; // cut-off point where object gets copied + unsigned int k = 2; // samples/symbol + unsigned int m = 12; // filter delay [symbols] + float beta = 0.25; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + float nstd = 0.001f; + unsigned int i; + + // generate random frame sequence + float complex seq_tx [seq_len]; // transmitted + float complex seq_rx_orig[seq_len]; // received with initial correction (original) + float complex seq_rx_copy[seq_len]; // received with initial correction (original) + for (i=0; i Date: Wed, 22 Feb 2023 13:14:22 -0500 Subject: [PATCH 030/186] qdsync/autotest: cleaning up autotest, respecting verbosity flag --- src/framing/tests/qdsync_cccf_autotest.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index d8e0a78ad..c01555d9c 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2018 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,8 @@ int autotest_qdsync_callback(float complex * _buf, { // save samples to buffer as appropriate autotest_qdsync_s * q = (autotest_qdsync_s *) _context; - printf("[%d] qdsync callback got %u samples\n", q->id, _buf_len); + if (liquid_autotest_verbose) + printf("[%d] qdsync callback got %u samples\n", q->id, _buf_len); unsigned int i; for (i=0; i<_buf_len; i++) { if (q->count == q->buf_len) @@ -220,13 +221,12 @@ void autotest_qdsync_cccf_copy() CONTEND_EQUALITY(c_orig.count, seq_len); CONTEND_EQUALITY(c_copy.count, seq_len); // print values for visual comparison - /* - for (i=0; i Date: Sat, 15 Oct 2022 10:08:16 -0400 Subject: [PATCH 031/186] qdsync: adding new object to support both detection and tracking --- examples/qdsync_cccf_example.c | 86 +++++++++++++++++++ include/liquid.h | 46 ++++++++++ makefile.in | 2 + src/framing/src/qdsync_cccf.c | 150 +++++++++++++++++++++++++++++++++ 4 files changed, 284 insertions(+) create mode 100644 examples/qdsync_cccf_example.c create mode 100644 src/framing/src/qdsync_cccf.c diff --git a/examples/qdsync_cccf_example.c b/examples/qdsync_cccf_example.c new file mode 100644 index 000000000..12db10063 --- /dev/null +++ b/examples/qdsync_cccf_example.c @@ -0,0 +1,86 @@ +// This example demonstrates the functionality of the qdsync object to +// detect and synchronize an arbitrary signal in time in the presence of noise, +// carrier frequency/phase offsets, and fractional-sample timing offsets. +// offsets. +#include +#include +#include +#include +#include +#include +#include "liquid.h" + +#define OUTPUT_FILENAME "qdsync_cccf_example.m" + +// synchronization callback, return 0:continue, 1:reset +int callback(float complex * _buf, + unsigned int _buf_len, + void * _context) +{ printf("callback got %u samples\n", _buf_len); return 0; } + +int main(int argc, char*argv[]) +{ + // options + unsigned int sequence_len = 80; // number of sync symbols + unsigned int k = 2; // samples/symbol + unsigned int m = 7; // filter delay [symbols] + float beta = 0.3f; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + float nstd = 0.1f; + + unsigned int i; + + // generate synchronization sequence (QPSK symbols) + unsigned int seq_len = k*(sequence_len + 2*m); + float complex seq[seq_len]; + firinterp_crcf interp = firinterp_crcf_create_prototype(ftype,k,m,beta,0); + for (i=0; i +#include +#include +#include +#include + +#include "liquid.internal.h" + +// main object definition +struct qdsync_cccf_s { + qdsync_callback callback; // + void * context; // + qdetector_cccf detector; // detector + // resampler +}; + +// create detector with generic sequence +// _s : sample sequence +// _s_len : length of sample sequence +qdsync_cccf qdsync_cccf_create(float complex * _s, + unsigned int _s_len, + qdsync_callback _callback, + void * _context) +{ + // validate input + if (_s_len == 0) + return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); + + // allocate memory for main object and set internal properties + qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); + + // create detector + q->detector = qdetector_cccf_create(_s, _s_len); + + // set callback and context values + qdsync_cccf_set_callback(q, _callback); + qdsync_cccf_set_context (q, _context ); + + // reset and return object + qdsync_cccf_reset(q); + return q; +} + +// copy object +qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); + + // create new object and copy base parameters + qdsync_cccf q_copy = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); + memmove(q_copy, q_orig, sizeof(struct qdsync_cccf_s)); + + // copy sub-objects + q_copy->detector = qdetector_cccf_copy(q_orig->detector); + + // return new object + return q_copy; +} + +int qdsync_cccf_destroy(qdsync_cccf _q) +{ + // destroy internal objects + qdetector_cccf_destroy(_q->detector); + + // free main object memory + free(_q); + return LIQUID_OK; +} + +int qdsync_cccf_print(qdsync_cccf _q) +{ + printf("\n"); + return LIQUID_OK; +} + +int qdsync_cccf_reset(qdsync_cccf _q) +{ + return LIQUID_OK; +} + +int qdsync_cccf_execute(qdsync_cccf _q, + liquid_float_complex * _buf, + unsigned int _buf_len) +{ + // TODO: switch based on state + unsigned int i; + for (i=0; i<_buf_len; i++) { + void * p = qdetector_cccf_execute(_q->detector, _buf[i]); + if (p != NULL) + _q->callback(NULL, 0, _q->context); + } + return LIQUID_OK; +} + +// get detection threshold +float qdsync_cccf_get_threshold(qdsync_cccf _q) +{ + return qdetector_cccf_get_threshold(_q->detector); +} + +// set detection threshold +int qdsync_cccf_set_threshold(qdsync_cccf _q, + float _threshold) +{ + return qdetector_cccf_set_threshold(_q->detector, _threshold); +} + +// set callback method +int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback) +{ + _q->callback = _callback; + return LIQUID_OK; +} + +// set context value +int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) +{ + _q->context = _context; + return LIQUID_OK; +} + From 556a0f4dc610bf9a17ec00592fe70a70723e41f3 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Oct 2022 11:33:01 -0400 Subject: [PATCH 032/186] qdsync: putting basic hooks to pass information back to caller --- examples/qdsync_cccf_example.c | 27 +++++++++++++++------------ src/framing/src/qdsync_cccf.c | 28 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/examples/qdsync_cccf_example.c b/examples/qdsync_cccf_example.c index 12db10063..5c98ac5b5 100644 --- a/examples/qdsync_cccf_example.c +++ b/examples/qdsync_cccf_example.c @@ -16,7 +16,13 @@ int callback(float complex * _buf, unsigned int _buf_len, void * _context) -{ printf("callback got %u samples\n", _buf_len); return 0; } +{ + printf("callback got %u samples\n", _buf_len); + unsigned int i; + for (i=0; i<_buf_len; i++) + fprintf((FILE*)_context, "y(end+1) = %12.8f + %12.8fj;\n", crealf(_buf[i]), cimagf(_buf[i])); + return 0; +} int main(int argc, char*argv[]) { @@ -26,7 +32,7 @@ int main(int argc, char*argv[]) unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; - float nstd = 0.1f; + float nstd = 0.01f; unsigned int i; @@ -41,13 +47,18 @@ int main(int argc, char*argv[]) } firinterp_crcf_destroy(interp); + // open file for storing results + FILE * fid = fopen(OUTPUT_FILENAME,"w"); + fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); + fprintf(fid,"clear all; close all; y=[];\n"); + // create sync object - qdsync_cccf q = qdsync_cccf_create(seq, seq_len, callback, NULL); + qdsync_cccf q = qdsync_cccf_create(seq, seq_len, callback, fid); qdsync_cccf_print(q); // float complex buf[seq_len]; - for (i=0; i<200; i++) { + for (i=0; i<20; i++) { if (i==10) memmove(buf, seq, seq_len*sizeof(float complex)); else memset (buf, 0x00, seq_len*sizeof(float complex)); @@ -59,17 +70,9 @@ int main(int argc, char*argv[]) // run through synchronizer qdsync_cccf_execute(q, buf, seq_len); } - qdsync_cccf_destroy(q); // export results - FILE * fid = fopen(OUTPUT_FILENAME,"w"); - fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); - fprintf(fid,"clear all\n"); - fprintf(fid,"close all\n"); - //fprintf(fid,"sequence_len= %u;\n", sequence_len); - //fprintf(fid,"num_samples = %u;\n", num_samples); - fprintf(fid,"s = [];\n"); for (i=0; istate = QDSYNC_STATE_DETECT; return LIQUID_OK; } @@ -113,10 +120,25 @@ int qdsync_cccf_execute(qdsync_cccf _q, { // TODO: switch based on state unsigned int i; + void * p = NULL; for (i=0; i<_buf_len; i++) { - void * p = qdetector_cccf_execute(_q->detector, _buf[i]); - if (p != NULL) - _q->callback(NULL, 0, _q->context); + switch (_q->state) { + case QDSYNC_STATE_DETECT: + p = qdetector_cccf_execute(_q->detector, _buf[i]); + if (p != NULL) { + if (_q->callback != NULL) + _q->callback(NULL, 0, _q->context); + _q->state = QDSYNC_STATE_SYNC; + } + break; + case QDSYNC_STATE_SYNC: + if (_q->callback != NULL) { + _q->callback(_buf, _buf_len, _q->context); + return LIQUID_OK; + } + break; + default:; + } } return LIQUID_OK; } From c80b2271b369281ed06ec439496b4553abfc0860 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Oct 2022 15:57:35 -0400 Subject: [PATCH 033/186] qdsync: refactoring to work with linear modulated signals --- examples/qdsync_cccf_example.c | 56 ++++---- include/liquid.h | 12 +- src/framing/src/framesync64.c | 2 +- src/framing/src/qdsync_cccf.c | 226 +++++++++++++++++++++++++++++---- 4 files changed, 237 insertions(+), 59 deletions(-) diff --git a/examples/qdsync_cccf_example.c b/examples/qdsync_cccf_example.c index 5c98ac5b5..3b04f02e1 100644 --- a/examples/qdsync_cccf_example.c +++ b/examples/qdsync_cccf_example.c @@ -27,25 +27,20 @@ int callback(float complex * _buf, int main(int argc, char*argv[]) { // options - unsigned int sequence_len = 80; // number of sync symbols + unsigned int seq_len = 80; // number of sync symbols unsigned int k = 2; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float nstd = 0.01f; - unsigned int i; - // generate synchronization sequence (QPSK symbols) - unsigned int seq_len = k*(sequence_len + 2*m); float complex seq[seq_len]; - firinterp_crcf interp = firinterp_crcf_create_prototype(ftype,k,m,beta,0); - for (i=0; imixer = nco_crcf_create(LIQUID_NCO); - + // create payload demodulator/decoder object int check = LIQUID_CRC_24; int fec0 = LIQUID_FEC_NONE; diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 3013a5f3c..ad6c8668b 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -33,37 +33,94 @@ #include "liquid.internal.h" +// push samples through detection stage +int qdsync_cccf_execute_detect(qdsync_cccf _q, + float complex _x); + +// step receiver mixer, matched filter, decimator +// _q : frame synchronizer +// _x : input sample +int qdsync_cccf_step(qdsync_cccf _q, + float complex _x); + +// append sample to output buffer +int qdsync_cccf_buf_append(qdsync_cccf _q, + float complex _x); + // main object definition struct qdsync_cccf_s { + unsigned int seq_len; // preamble sequence length + int ftype; // + unsigned int k; // + unsigned int m; // + float beta; // + qdsync_callback callback; // void * context; // qdetector_cccf detector; // detector + // status variables enum { QDSYNC_STATE_DETECT=0, // detect frame - QDSYNC_STATE_SYNC, // synchronize samples - } state; - // resampler - // nco + QDSYNC_STATE_SYNC, // + } state; // frame synchronization state + unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence + + // estimated offsets + float tau_hat; // + float gamma_hat; // + float dphi_hat; // + float phi_hat; // + + nco_crcf mixer; // coarse carrier frequency recovery + + // timing recovery objects, states + firpfb_crcf mf; // matched filter decimator + unsigned int npfb; // number of filters in symsync + int mf_counter; // matched filter output timer + unsigned int pfb_index; // filterbank index + + // symbol buffer + unsigned int buf_out_len;// output buffer length + float complex * buf_out; // output buffer + unsigned int buf_out_counter; // output counter }; // create detector with generic sequence -// _s : sample sequence -// _s_len : length of sample sequence -qdsync_cccf qdsync_cccf_create(float complex * _s, - unsigned int _s_len, - qdsync_callback _callback, - void * _context) +qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, + unsigned int _seq_len, + int _ftype, + unsigned int _k, + unsigned int _m, + float _beta, + qdsync_callback _callback, + void * _context) { // validate input - if (_s_len == 0) + if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - + // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); + q->seq_len = _seq_len; + q->ftype = _ftype; + q->k = _k; + q->m = _m; + q->beta = _beta; // create detector - q->detector = qdetector_cccf_create(_s, _s_len); + q->detector = qdetector_cccf_create_linear(_seq, _seq_len, _ftype, _k, _m, _beta); + + // create down-coverters for carrier phase tracking + q->mixer = nco_crcf_create(LIQUID_NCO); + + // create symbol timing recovery filters + q->npfb = 256; // number of filters in the bank + q->mf = firpfb_crcf_create_rnyquist(q->ftype, q->npfb, q->k, q->m, q->beta); + + // allocate buffer for storing output samples + q->buf_out_len = 64; // TODO: make user-defined? + q->buf_out = (float complex*) malloc(q->buf_out_len*sizeof(float complex)); // set callback and context values qdsync_cccf_set_callback(q, _callback); @@ -77,6 +134,7 @@ qdsync_cccf qdsync_cccf_create(float complex * _s, // copy object qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) { +#if 0 // validate input if (q_orig == NULL) return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); @@ -90,12 +148,20 @@ qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) // return new object return q_copy; +#else + return liquid_error_config("qdsync_cccf_copy(), method not yet implemented"); +#endif } int qdsync_cccf_destroy(qdsync_cccf _q) { // destroy internal objects qdetector_cccf_destroy(_q->detector); + nco_crcf_destroy(_q->mixer); + firpfb_crcf_destroy(_q->mf); + + // free output buffer + free(_q->buf_out); // free main object memory free(_q); @@ -110,7 +176,11 @@ int qdsync_cccf_print(qdsync_cccf _q) int qdsync_cccf_reset(qdsync_cccf _q) { + qdetector_cccf_reset(_q->detector); _q->state = QDSYNC_STATE_DETECT; + _q->symbol_counter = 0; + _q->buf_out_counter = 0; + firpfb_crcf_reset(_q->mf); return LIQUID_OK; } @@ -118,26 +188,19 @@ int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, unsigned int _buf_len) { - // TODO: switch based on state unsigned int i; - void * p = NULL; for (i=0; i<_buf_len; i++) { switch (_q->state) { case QDSYNC_STATE_DETECT: - p = qdetector_cccf_execute(_q->detector, _buf[i]); - if (p != NULL) { - if (_q->callback != NULL) - _q->callback(NULL, 0, _q->context); - _q->state = QDSYNC_STATE_SYNC; - } + // detect frame (look for p/n sequence) + qdsync_cccf_execute_detect(_q, _buf[i]); break; case QDSYNC_STATE_SYNC: - if (_q->callback != NULL) { - _q->callback(_buf, _buf_len, _q->context); - return LIQUID_OK; - } + // receive preamble sequence symbols + qdsync_cccf_step(_q, _buf[i]); break; - default:; + default: + return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); } } return LIQUID_OK; @@ -170,3 +233,114 @@ int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) return LIQUID_OK; } +// +// internal methods +// + +// execute synchronizer, seeking preamble sequence +// _q : frame synchronizer object +// _x : input sample +// _sym : demodulated symbol +int qdsync_cccf_execute_detect(qdsync_cccf _q, + float complex _x) +{ + // push through pre-demod synchronizer + float complex * v = qdetector_cccf_execute(_q->detector, _x); + + // check if frame has been detected + if (v != NULL) { + // get estimates + _q->tau_hat = qdetector_cccf_get_tau (_q->detector); + _q->gamma_hat = qdetector_cccf_get_gamma(_q->detector); + _q->dphi_hat = qdetector_cccf_get_dphi (_q->detector); + _q->phi_hat = qdetector_cccf_get_phi (_q->detector); + //printf("***** frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", + // _q->tau_hat, _q->dphi_hat, 20*log10f(_q->gamma_hat)); + + // set appropriate filterbank index + if (_q->tau_hat > 0) { + _q->pfb_index = (unsigned int)( _q->tau_hat * _q->npfb) % _q->npfb; + _q->mf_counter = 0; + } else { + _q->pfb_index = (unsigned int)((1.0f+_q->tau_hat) * _q->npfb) % _q->npfb; + _q->mf_counter = 1; + } + + // output filter scale + firpfb_crcf_set_scale(_q->mf, 0.5f / _q->gamma_hat); + + // set frequency/phase of mixer + nco_crcf_set_frequency(_q->mixer, _q->dphi_hat); + nco_crcf_set_phase (_q->mixer, _q->phi_hat ); + + // update state + _q->state = QDSYNC_STATE_SYNC; + + // run buffered samples through synchronizer + unsigned int buf_len = qdetector_cccf_get_buf_len(_q->detector); + qdsync_cccf_execute(_q, v, buf_len); + } + return LIQUID_OK; +} + +// step receiver mixer, matched filter, decimator +// _q : frame synchronizer +// _x : input sample +// _y : output symbol +int qdsync_cccf_step(qdsync_cccf _q, + float complex _x) +{ + // mix sample down + float complex v; + nco_crcf_mix_down(_q->mixer, _x, &v); + nco_crcf_step (_q->mixer); + + // push sample into filterbank + firpfb_crcf_push (_q->mf, v); + firpfb_crcf_execute(_q->mf, _q->pfb_index, &v); + + // increment counter to determine if sample is available + _q->mf_counter++; + int sample_available = (_q->mf_counter >= 1) ? 1 : 0; + + // set output sample if available + if (sample_available) { + // decrement counter by k=2 samples/symbol + _q->mf_counter -= 2; + + // append to output + qdsync_cccf_buf_append(_q, v); + } + + // return flag + return LIQUID_OK; +} + +// append sample to output buffer +int qdsync_cccf_buf_append(qdsync_cccf _q, + float complex _x) +{ + // account for filter delay + _q->symbol_counter++; + if (_q->symbol_counter <= 2*_q->m) + return LIQUID_OK; + + // append sample to end of buffer + _q->buf_out[_q->buf_out_counter] = _x; + _q->buf_out_counter++; + + // check if buffer is full + if (_q->buf_out_counter == _q->buf_out_len) { + // reset counter + _q->buf_out_counter = 0; + + // invoke callback + if (_q->callback != NULL) { + int rc = _q->callback(_q->buf_out, _q->buf_out_len, _q->context); + if (rc) + return qdsync_cccf_reset(_q); + } + } + return LIQUID_OK; +} + From e618e8b43db279d907aea83cf0d2669964ef63e5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 16 Oct 2022 17:11:45 -0400 Subject: [PATCH 034/186] qdsync: adding method to set carrier offset search range --- include/liquid.h | 4 ++++ src/framing/src/qdsync_cccf.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/include/liquid.h b/include/liquid.h index f5cfc603b..ca3fbe5c3 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6299,6 +6299,10 @@ float qdsync_cccf_get_threshold(qdsync_cccf _q); // set detection threshold int qdsync_cccf_set_threshold(qdsync_cccf _q, float _threshold); +// set carrier offset search range +int qdsync_cccf_set_range(qdsync_cccf _q, + float _dphi_max); + // set callback method int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index ad6c8668b..d76a45166 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -219,6 +219,13 @@ int qdsync_cccf_set_threshold(qdsync_cccf _q, return qdetector_cccf_set_threshold(_q->detector, _threshold); } +// set carrier offset search range +int qdsync_cccf_set_range(qdsync_cccf _q, + float _dphi_max) +{ + return qdetector_cccf_set_range(_q->detector, _dphi_max); +} + // set callback method int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback) { From 2e29e1e991090d46fbddce4dcdfe89f632c3b69b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 17 Oct 2022 08:24:24 -0400 Subject: [PATCH 035/186] qdsync: restricting samples per symbol to be just 2 for now --- src/framing/src/qdsync_cccf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index d76a45166..6af86c18f 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -99,6 +99,8 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // validate input if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); + if (_k != 2) + return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); From 3e1e4c45f2fa8030e3e5fac31b0b5ff9fdb4650f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 19 Oct 2022 07:32:34 -0400 Subject: [PATCH 036/186] qdsync: adding method to determine if object is 'open' (actively receiving) --- include/liquid.h | 3 +++ src/framing/src/qdsync_cccf.c | 49 +++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index ca3fbe5c3..1c580fcb4 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6309,6 +6309,9 @@ int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); // set context value int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); +// is synchronizer actively running? +int qdsync_cccf_is_open(qdsync_cccf _q); + // execute block of samples int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 6af86c18f..cabe17f90 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -186,28 +186,6 @@ int qdsync_cccf_reset(qdsync_cccf _q) return LIQUID_OK; } -int qdsync_cccf_execute(qdsync_cccf _q, - liquid_float_complex * _buf, - unsigned int _buf_len) -{ - unsigned int i; - for (i=0; i<_buf_len; i++) { - switch (_q->state) { - case QDSYNC_STATE_DETECT: - // detect frame (look for p/n sequence) - qdsync_cccf_execute_detect(_q, _buf[i]); - break; - case QDSYNC_STATE_SYNC: - // receive preamble sequence symbols - qdsync_cccf_step(_q, _buf[i]); - break; - default: - return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); - } - } - return LIQUID_OK; -} - // get detection threshold float qdsync_cccf_get_threshold(qdsync_cccf _q) { @@ -242,6 +220,33 @@ int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) return LIQUID_OK; } +int qdsync_cccf_execute(qdsync_cccf _q, + liquid_float_complex * _buf, + unsigned int _buf_len) +{ + unsigned int i; + for (i=0; i<_buf_len; i++) { + switch (_q->state) { + case QDSYNC_STATE_DETECT: + // detect frame (look for p/n sequence) + qdsync_cccf_execute_detect(_q, _buf[i]); + break; + case QDSYNC_STATE_SYNC: + // receive preamble sequence symbols + qdsync_cccf_step(_q, _buf[i]); + break; + default: + return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); + } + } + return LIQUID_OK; +} + +int qdsync_cccf_is_open(qdsync_cccf _q) +{ + return _q->state == QDSYNC_STATE_DETECT ? 0 : 1; +} + // // internal methods // From cf7bc59f75421eadc2ffb2dcb551eaacf2bc2af5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 19 Oct 2022 07:42:33 -0400 Subject: [PATCH 037/186] qdsync: adding autotest for detection, payload recovery --- makefile.in | 1 + src/framing/tests/qdsync_cccf_autotest.c | 141 +++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 src/framing/tests/qdsync_cccf_autotest.c diff --git a/makefile.in b/makefile.in index ac4daeceb..889e1ff34 100644 --- a/makefile.in +++ b/makefile.in @@ -699,6 +699,7 @@ framing_autotests := \ src/framing/tests/ofdmflexframe_autotest.c \ src/framing/tests/qdetector_cccf_autotest.c \ src/framing/tests/qdetector_cccf_copy_autotest.c \ + src/framing/tests/qdsync_cccf_autotest.c \ src/framing/tests/qpacketmodem_autotest.c \ src/framing/tests/qpilotsync_autotest.c \ src/framing/tests/qsource_autotest.c \ diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c new file mode 100644 index 000000000..a5cbfcdbd --- /dev/null +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2007 - 2018 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include "autotest/autotest.h" +#include "liquid.h" + +// common structure for relaying information to/from callback +typedef struct { + unsigned int seq_len; + float complex * buf; + unsigned int buf_len; + unsigned int count; +} autotest_qdsync_s; + +// synchronization callback, return 0:continue, 1:reset +int autotest_qdsync_callback(float complex * _buf, + unsigned int _buf_len, + void * _context) +{ + // save samples to buffer as appropriate + autotest_qdsync_s * q = (autotest_qdsync_s *) _context; + unsigned int i; + for (i=0; i<_buf_len; i++) { + if (q->count < q->seq_len) { + // preamble sequence + } else if (q->count < q->seq_len + q->buf_len) { + // payload + q->buf[q->count - q->seq_len] = _buf[i]; + } if (q->count == q->seq_len + q->buf_len) { + // buffer full; reset synchronizer + return 1; + } + q->count++; + } + return 0; +} + +void autotest_qdsync() +{ + // options + unsigned int seq_len = 80; // number of sync symbols + unsigned int payload_len = 200; // number of payload symbols + unsigned int k = 2; // samples/symbol + unsigned int m = 7; // filter delay [symbols] + float beta = 0.3f; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + float nstd = 0.001f; + + // generate synchronization sequence (QPSK symbols) + float complex seq[seq_len]; + unsigned int i; + for (i=0; i Date: Thu, 20 Oct 2022 20:48:14 -0400 Subject: [PATCH 038/186] qdsync: adding methods to get metrics, offset estimates --- include/liquid.h | 13 ++++++++++--- src/framing/src/qdsync_cccf.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 1c580fcb4..50850c399 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6309,14 +6309,21 @@ int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); // set context value int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); -// is synchronizer actively running? -int qdsync_cccf_is_open(qdsync_cccf _q); - // execute block of samples int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, unsigned int _buf_len); +// is synchronizer actively running? +int qdsync_cccf_is_open(qdsync_cccf _q); + +// get detection metrics and offsets +float qdsync_cccf_get_rxy (qdsync_cccf _q); // correlator output +float qdsync_cccf_get_tau (qdsync_cccf _q); // fractional timing offset estimate +float qdsync_cccf_get_gamma(qdsync_cccf _q); // channel gain +float qdsync_cccf_get_dphi (qdsync_cccf _q); // carrier frequency offset estimate +float qdsync_cccf_get_phi (qdsync_cccf _q); // carrier phase offset estimate + // // Pre-demodulation detector // diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index cabe17f90..c9737ae11 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -247,6 +247,36 @@ int qdsync_cccf_is_open(qdsync_cccf _q) return _q->state == QDSYNC_STATE_DETECT ? 0 : 1; } +// correlator output +float qdsync_cccf_get_rxy(qdsync_cccf _q) +{ + return qdetector_cccf_get_rxy(_q->detector); +} + +// fractional timing offset estimate +float qdsync_cccf_get_tau(qdsync_cccf _q) +{ + return qdetector_cccf_get_tau(_q->detector); +} + +// channel gain +float qdsync_cccf_get_gamma(qdsync_cccf _q) +{ + return qdetector_cccf_get_gamma(_q->detector); +} + +// carrier frequency offset estimate +float qdsync_cccf_get_dphi(qdsync_cccf _q) +{ + return qdetector_cccf_get_dphi(_q->detector); +} + +// carrier phase offset estimate +float qdsync_cccf_get_phi(qdsync_cccf _q) +{ + return qdetector_cccf_get_phi(_q->detector); +} + // // internal methods // From 932880e70cc38297b1e3942fd9d06f50a24c34bb Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 20 Oct 2022 20:52:39 -0400 Subject: [PATCH 039/186] qdsync: removing internal offsets; no need to store extra copy --- src/framing/src/qdsync_cccf.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index c9737ae11..fec0df56e 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -66,12 +66,6 @@ struct qdsync_cccf_s { } state; // frame synchronization state unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence - // estimated offsets - float tau_hat; // - float gamma_hat; // - float dphi_hat; // - float phi_hat; // - nco_crcf mixer; // coarse carrier frequency recovery // timing recovery objects, states @@ -294,28 +288,28 @@ int qdsync_cccf_execute_detect(qdsync_cccf _q, // check if frame has been detected if (v != NULL) { // get estimates - _q->tau_hat = qdetector_cccf_get_tau (_q->detector); - _q->gamma_hat = qdetector_cccf_get_gamma(_q->detector); - _q->dphi_hat = qdetector_cccf_get_dphi (_q->detector); - _q->phi_hat = qdetector_cccf_get_phi (_q->detector); - //printf("***** frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", - // _q->tau_hat, _q->dphi_hat, 20*log10f(_q->gamma_hat)); + float tau_hat = qdetector_cccf_get_tau (_q->detector); + float gamma_hat = qdetector_cccf_get_gamma(_q->detector); + float dphi_hat = qdetector_cccf_get_dphi (_q->detector); + float phi_hat = qdetector_cccf_get_phi (_q->detector); + //printf("*** qdsync frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", + // tau_hat, dphi_hat, 20*log10f(gamma_hat)); // set appropriate filterbank index - if (_q->tau_hat > 0) { - _q->pfb_index = (unsigned int)( _q->tau_hat * _q->npfb) % _q->npfb; + if (tau_hat > 0) { + _q->pfb_index = (unsigned int)( tau_hat * _q->npfb) % _q->npfb; _q->mf_counter = 0; } else { - _q->pfb_index = (unsigned int)((1.0f+_q->tau_hat) * _q->npfb) % _q->npfb; + _q->pfb_index = (unsigned int)((1.0f+tau_hat) * _q->npfb) % _q->npfb; _q->mf_counter = 1; } // output filter scale - firpfb_crcf_set_scale(_q->mf, 0.5f / _q->gamma_hat); + firpfb_crcf_set_scale(_q->mf, 0.5f / gamma_hat); // set frequency/phase of mixer - nco_crcf_set_frequency(_q->mixer, _q->dphi_hat); - nco_crcf_set_phase (_q->mixer, _q->phi_hat ); + nco_crcf_set_frequency(_q->mixer, dphi_hat); + nco_crcf_set_phase (_q->mixer, phi_hat ); // update state _q->state = QDSYNC_STATE_SYNC; From 5b97e0fff1766dcd567e76f1e299e8174e7f8582 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 16:26:26 -0400 Subject: [PATCH 040/186] qdsync: allowing other than 2 samples per symbol, extending test * needs further investigation for offset correction * works reasonably well for k in {2,4} but for k=3 occasionally fails * needs more testing of edge cases and resiliency --- src/framing/src/qdsync_cccf.c | 27 +++++++++-------- src/framing/tests/qdsync_cccf_autotest.c | 38 +++++++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index fec0df56e..ed19eef64 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -93,8 +93,8 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // validate input if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - if (_k != 2) - return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); + //if (_k != 2) + // return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); @@ -292,20 +292,21 @@ int qdsync_cccf_execute_detect(qdsync_cccf _q, float gamma_hat = qdetector_cccf_get_gamma(_q->detector); float dphi_hat = qdetector_cccf_get_dphi (_q->detector); float phi_hat = qdetector_cccf_get_phi (_q->detector); - //printf("*** qdsync frame detected! tau-hat:%8.4f, dphi-hat:%8.4f, gamma:%8.2f dB\n", - // tau_hat, dphi_hat, 20*log10f(gamma_hat)); // set appropriate filterbank index - if (tau_hat > 0) { - _q->pfb_index = (unsigned int)( tau_hat * _q->npfb) % _q->npfb; - _q->mf_counter = 0; - } else { - _q->pfb_index = (unsigned int)((1.0f+tau_hat) * _q->npfb) % _q->npfb; - _q->mf_counter = 1; + _q->mf_counter = _q->k - 2; + _q->pfb_index = 0; + int index = (int)(tau_hat * _q->npfb); + if (index < 0) { + _q->mf_counter++; + index += _q->npfb; } + _q->pfb_index = index; + //printf("* qdsync detected! tau:%6.3f, dphi:%12.4e, phi:%6.3f, gamma:%6.2f dB, mf:%u, pfb idx:%u\n", + // tau_hat, dphi_hat, phi_hat, 20*log10f(gamma_hat), _q->mf_counter, _q->pfb_index); // output filter scale - firpfb_crcf_set_scale(_q->mf, 0.5f / gamma_hat); + firpfb_crcf_set_scale(_q->mf, 1.0f / (_q->k * gamma_hat)); // set frequency/phase of mixer nco_crcf_set_frequency(_q->mixer, dphi_hat); @@ -339,12 +340,12 @@ int qdsync_cccf_step(qdsync_cccf _q, // increment counter to determine if sample is available _q->mf_counter++; - int sample_available = (_q->mf_counter >= 1) ? 1 : 0; + int sample_available = (_q->mf_counter >= _q->k-1) ? 1 : 0; // set output sample if available if (sample_available) { // decrement counter by k=2 samples/symbol - _q->mf_counter -= 2; + _q->mf_counter -= _q->k; // append to output qdsync_cccf_buf_append(_q, v); diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index a5cbfcdbd..d7ab62e1c 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -38,6 +38,7 @@ int autotest_qdsync_callback(float complex * _buf, unsigned int _buf_len, void * _context) { + printf("qdsync callback got %u samples\n", _buf_len); // save samples to buffer as appropriate autotest_qdsync_s * q = (autotest_qdsync_s *) _context; unsigned int i; @@ -76,8 +77,8 @@ void autotest_qdsync() } // payload symbols - float complex payload_tx[payload_len]; - float complex payload_rx[payload_len]; + float complex payload_tx[payload_len]; // transmitted + float complex payload_rx[payload_len]; // received with initial correction for (i=0; i Date: Wed, 26 Oct 2022 16:51:22 -0400 Subject: [PATCH 041/186] qdsync: cleaning up text formatting and comments with object --- src/framing/src/qdsync_cccf.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index ed19eef64..047ab7a79 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -50,34 +50,34 @@ int qdsync_cccf_buf_append(qdsync_cccf _q, // main object definition struct qdsync_cccf_s { unsigned int seq_len; // preamble sequence length - int ftype; // - unsigned int k; // - unsigned int m; // - float beta; // + int ftype; // filter type + unsigned int k; // samples per symbol + unsigned int m; // filter semi-length + float beta; // excess bandwidth factor - qdsync_callback callback; // - void * context; // + qdsync_callback callback; // user-defined callback function + void * context; // user-defined context object qdetector_cccf detector; // detector // status variables enum { QDSYNC_STATE_DETECT=0, // detect frame - QDSYNC_STATE_SYNC, // + QDSYNC_STATE_SYNC, // apply carrier offset correction and matched filter } state; // frame synchronization state unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence - nco_crcf mixer; // coarse carrier frequency recovery + nco_crcf mixer; // coarse carrier frequency recovery // timing recovery objects, states - firpfb_crcf mf; // matched filter decimator - unsigned int npfb; // number of filters in symsync - int mf_counter; // matched filter output timer - unsigned int pfb_index; // filterbank index + firpfb_crcf mf; // matched filter/decimator + unsigned int npfb; // number of filters in symsync + int mf_counter; // matched filter output timer + unsigned int pfb_index; // filterbank index // symbol buffer - unsigned int buf_out_len;// output buffer length - float complex * buf_out; // output buffer - unsigned int buf_out_counter; // output counter + unsigned int buf_out_len;// output buffer length + float complex * buf_out; // output buffer + unsigned int buf_out_counter; // output counter }; // create detector with generic sequence From 976cca79fdeb07ae794b19526ead2adf22d5818c Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:03:49 -0400 Subject: [PATCH 042/186] qdsync/autotest: extending sequence and payload for more stable testing --- src/framing/tests/qdsync_cccf_autotest.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index d7ab62e1c..9d155a3a6 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -60,8 +60,8 @@ int autotest_qdsync_callback(float complex * _buf, void autotest_qdsync() { // options - unsigned int seq_len = 80; // number of sync symbols - unsigned int payload_len = 200; // number of payload symbols + unsigned int seq_len = 240; // number of sync symbols + unsigned int payload_len = 800; // number of payload symbols unsigned int k = 2; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor @@ -135,8 +135,8 @@ void autotest_qdsync() rmse = 10*log10f( rmse / (float)payload_len ); if (liquid_autotest_verbose) printf("qdsync: dphi: %12.4e, phi: %12.8f, rmse: %12.3f\n", dphi_hat, phi_hat, rmse); - CONTEND_LESS_THAN( rmse, -20.0f ) - CONTEND_LESS_THAN( fabsf(dphi_hat), 4e-3f ) + CONTEND_LESS_THAN( rmse, -30.0f ) + CONTEND_LESS_THAN( fabsf(dphi_hat), 1e-3f ) CONTEND_LESS_THAN( fabsf( phi_hat), 0.4f ) // clean up objects From 13500c49fd5640001a83c7061ed621698c9e4c01 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:05:56 -0400 Subject: [PATCH 043/186] qdsync: removing old (commented) error condition checking --- src/framing/src/qdsync_cccf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 047ab7a79..930e840bc 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -93,8 +93,6 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // validate input if (_seq_len == 0) return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - //if (_k != 2) - // return liquid_error_config("qdsync_cccf_create(), samples per symbol fixed at 2 (temporarily)"); // allocate memory for main object and set internal properties qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); From b3984c52e65ab0affb5d5a42ecc2124e85f17e18 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:12:14 -0400 Subject: [PATCH 044/186] qdsync: extending tests to vary samples per symbol --- src/framing/tests/qdsync_cccf_autotest.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 9d155a3a6..39b252aa4 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -57,12 +57,12 @@ int autotest_qdsync_callback(float complex * _buf, return 0; } -void autotest_qdsync() +void testbench_qdsync(unsigned int _k) { // options unsigned int seq_len = 240; // number of sync symbols unsigned int payload_len = 800; // number of payload symbols - unsigned int k = 2; // samples/symbol + unsigned int k = _k; // samples/symbol unsigned int m = 7; // filter delay [symbols] float beta = 0.3f; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; @@ -157,3 +157,8 @@ void autotest_qdsync() #endif } +// test specific configurations +void autotest_qdsync_k2() { testbench_qdsync(2); } +void autotest_qdsync_k3() { testbench_qdsync(3); } +void autotest_qdsync_k4() { testbench_qdsync(4); } + From c40c5638b50e42af9345c1554ddd3faf798e03df Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 26 Oct 2022 17:35:32 -0400 Subject: [PATCH 045/186] qdsync: adding method to resize output buffer in callback * needs testing * should handle all cases: increasing/decreasing, buffer empty/full * will invoke callback as many times as needed * really intended to run at initial instantiation, but can be run any time * useful for framing synchronization where we need buffers of particular sizes * can hold either the entire frame, or relevant pieces --- include/liquid.h | 4 ++++ src/framing/src/qdsync_cccf.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/liquid.h b/include/liquid.h index 50850c399..2773884f3 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6309,6 +6309,10 @@ int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); // set context value int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); +// Set callback buffer size (the number of symbol provided to the callback +// whenever it is invoked). +int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len); + // execute block of samples int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 930e840bc..70995eb7b 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -212,6 +212,41 @@ int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) return LIQUID_OK; } +// Set callback buffer size (the number of symbol provided to the callback +// whenever it is invoked). +int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) +{ + if (_buf_len == 0) + return liquid_error(LIQUID_EICONFIG,"qdsync_cccf_set_buf_len(), buffer length must be greater than 0"); + + // check current state + if (_q->buf_out_counter < _buf_len) { + // buffer might not be empty, but we aren't resizing within this space; + // ok to resize so long as old samples are copied + _q->buf_out_len = _buf_len; + _q->buf_out = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + } else { + // we are shrinking the buffer below the number of samples it currently + // holds; invoke the callback as many times as needed to reduce its size + unsigned int index = 0; + while (_q->buf_out_counter >= _buf_len) { + if (_q->callback != NULL) + _q->callback(_q->buf_out + index, _buf_len, _q->context); + + // adjust counters + index += _buf_len; + _q->buf_out_counter -= _buf_len; + } + // now perform the reallocation, but we cannot use 'realloc' here because + // we are not copying values at the front of the buffer + float complex * buf_new = (float complex*)malloc(_buf_len * sizeof(float complex)); + memmove(buf_new, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); + free(_q->buf_out); + _q->buf_out = buf_new; + } + return LIQUID_OK; +} + int qdsync_cccf_execute(qdsync_cccf _q, liquid_float_complex * _buf, unsigned int _buf_len) From 869a294bf1ba1c5e3f3f3d3daca97bfdf6f9b99d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 22 Feb 2023 11:44:50 -0500 Subject: [PATCH 046/186] qdsync/autotest: making testbench specific to linear --- src/framing/tests/qdsync_cccf_autotest.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 39b252aa4..826fbf74d 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -57,14 +57,16 @@ int autotest_qdsync_callback(float complex * _buf, return 0; } -void testbench_qdsync(unsigned int _k) +void testbench_qdsync_linear(unsigned int _k, + unsigned int _m, + float _beta) { // options unsigned int seq_len = 240; // number of sync symbols unsigned int payload_len = 800; // number of payload symbols unsigned int k = _k; // samples/symbol - unsigned int m = 7; // filter delay [symbols] - float beta = 0.3f; // excess bandwidth factor + unsigned int m = _m; // filter delay [symbols] + float beta = _beta; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float nstd = 0.001f; @@ -158,7 +160,7 @@ void testbench_qdsync(unsigned int _k) } // test specific configurations -void autotest_qdsync_k2() { testbench_qdsync(2); } -void autotest_qdsync_k3() { testbench_qdsync(3); } -void autotest_qdsync_k4() { testbench_qdsync(4); } +void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } +void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } +void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } From def71e1841d20187dbf15e98adebb4c6f3074dd0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 22 Feb 2023 12:21:16 -0500 Subject: [PATCH 047/186] qdsync/autotest: simplifying test to use entire sequence --- src/framing/tests/qdsync_cccf_autotest.c | 72 +++++++++--------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 826fbf74d..a3d8e0841 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -27,7 +27,6 @@ // common structure for relaying information to/from callback typedef struct { - unsigned int seq_len; float complex * buf; unsigned int buf_len; unsigned int count; @@ -43,16 +42,11 @@ int autotest_qdsync_callback(float complex * _buf, autotest_qdsync_s * q = (autotest_qdsync_s *) _context; unsigned int i; for (i=0; i<_buf_len; i++) { - if (q->count < q->seq_len) { - // preamble sequence - } else if (q->count < q->seq_len + q->buf_len) { - // payload - q->buf[q->count - q->seq_len] = _buf[i]; - } if (q->count == q->seq_len + q->buf_len) { - // buffer full; reset synchronizer - return 1; - } - q->count++; + if (q->count == q->buf_len) + return 1; // buffer full; reset synchronizer + + // save payload + q->buf[q->count++] = _buf[i]; } return 0; } @@ -62,8 +56,7 @@ void testbench_qdsync_linear(unsigned int _k, float _beta) { // options - unsigned int seq_len = 240; // number of sync symbols - unsigned int payload_len = 800; // number of payload symbols + unsigned int seq_len = 1200; // total number of sync symbols unsigned int k = _k; // samples/symbol unsigned int m = _m; // filter delay [symbols] float beta = _beta; // excess bandwidth factor @@ -71,24 +64,17 @@ void testbench_qdsync_linear(unsigned int _k, float nstd = 0.001f; // generate synchronization sequence (QPSK symbols) - float complex seq[seq_len]; + float complex seq_tx[seq_len]; // transmitted + float complex seq_rx[seq_len]; // received with initial correction unsigned int i; for (i=0; i Date: Wed, 22 Feb 2023 13:06:21 -0500 Subject: [PATCH 048/186] qdsync: adding copy() method, autotest --- src/framing/src/qdsync_cccf.c | 15 ++-- src/framing/tests/qdsync_cccf_autotest.c | 90 +++++++++++++++++++++++- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 70995eb7b..1469b4f9c 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -113,7 +113,7 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, q->mf = firpfb_crcf_create_rnyquist(q->ftype, q->npfb, q->k, q->m, q->beta); // allocate buffer for storing output samples - q->buf_out_len = 64; // TODO: make user-defined? + q->buf_out_len = 64; // user can re-size this later q->buf_out = (float complex*) malloc(q->buf_out_len*sizeof(float complex)); // set callback and context values @@ -128,7 +128,6 @@ qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, // copy object qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) { -#if 0 // validate input if (q_orig == NULL) return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); @@ -137,14 +136,20 @@ qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) qdsync_cccf q_copy = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); memmove(q_copy, q_orig, sizeof(struct qdsync_cccf_s)); + // set callback and userdata fields + q_copy->callback = q_orig->callback; + q_copy->context = q_orig->context; + // copy sub-objects q_copy->detector = qdetector_cccf_copy(q_orig->detector); + q_copy->mixer = nco_crcf_copy (q_orig->mixer); + q_copy->mf = firpfb_crcf_copy (q_orig->mf); + + // copy memory in new allocation + q_copy->buf_out = (float complex*)liquid_malloc_copy(q_orig->buf_out, q_orig->buf_out_len, sizeof(float complex)); // return new object return q_copy; -#else - return liquid_error_config("qdsync_cccf_copy(), method not yet implemented"); -#endif } int qdsync_cccf_destroy(qdsync_cccf _q) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index a3d8e0841..d8e0a78ad 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -22,11 +22,13 @@ #include #include +#include #include "autotest/autotest.h" #include "liquid.h" // common structure for relaying information to/from callback typedef struct { + int id; float complex * buf; unsigned int buf_len; unsigned int count; @@ -37,9 +39,9 @@ int autotest_qdsync_callback(float complex * _buf, unsigned int _buf_len, void * _context) { - printf("qdsync callback got %u samples\n", _buf_len); // save samples to buffer as appropriate autotest_qdsync_s * q = (autotest_qdsync_s *) _context; + printf("[%d] qdsync callback got %u samples\n", q->id, _buf_len); unsigned int i; for (i=0; i<_buf_len; i++) { if (q->count == q->buf_len) @@ -73,7 +75,7 @@ void testbench_qdsync_linear(unsigned int _k, } // create sync object, only using first few symbols - autotest_qdsync_s obj = {.buf=seq_rx, .buf_len=seq_len, .count=0}; + autotest_qdsync_s obj = {.id=0, .buf=seq_rx, .buf_len=seq_len, .count=0}; qdsync_cccf q = qdsync_cccf_create_linear(seq_tx, 240, ftype, k, m, beta, autotest_qdsync_callback, (void*)&obj); qdsync_cccf_set_range(q, 0.001f); @@ -148,3 +150,87 @@ void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } +// test copying from one object to another +void autotest_qdsync_cccf_copy() +{ + // options + unsigned int seq_len= 2400; // total number of symbols in sequence + unsigned int split = 1033; // cut-off point where object gets copied + unsigned int k = 2; // samples/symbol + unsigned int m = 12; // filter delay [symbols] + float beta = 0.25; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + float nstd = 0.001f; + unsigned int i; + + // generate random frame sequence + float complex seq_tx [seq_len]; // transmitted + float complex seq_rx_orig[seq_len]; // received with initial correction (original) + float complex seq_rx_copy[seq_len]; // received with initial correction (original) + for (i=0; i Date: Wed, 22 Feb 2023 16:47:39 -0500 Subject: [PATCH 049/186] framesync64: using qdsync as internal detector type --- src/framing/src/framesync64.c | 446 ++++++----------------- src/framing/tests/framesync64_autotest.c | 6 + 2 files changed, 109 insertions(+), 343 deletions(-) diff --git a/src/framing/src/framesync64.c b/src/framing/src/framesync64.c index 256491752..6eca7a759 100644 --- a/src/framing/src/framesync64.c +++ b/src/framing/src/framesync64.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,27 +31,10 @@ #include "liquid.internal.h" -#define FRAMESYNC64_ENABLE_EQ 0 - -// push samples through detection stage -int framesync64_execute_seekpn(framesync64 _q, - float complex _x); - -// step receiver mixer, matched filter, decimator -// _q : frame synchronizer -// _x : input sample -// _y : output symbol -int framesync64_step(framesync64 _q, - float complex _x, - float complex * _y); - -// push samples through synchronizer, saving received p/n symbols -int framesync64_execute_rxpreamble(framesync64 _q, - float complex _x); - -// receive payload symbols -int framesync64_execute_rxpayload(framesync64 _q, - float complex _x); +// synchronization callback, return 0:continue, 1:reset +int framesync64_callback_internal(float complex * _buf, + unsigned int _buf_len, + void * _context); // export debugging based on return value // 0 : do not write file @@ -59,55 +42,31 @@ int framesync64_execute_rxpayload(framesync64 _q, // -1 : number of packets detected // -2 : id using first 4 bytes of header // -3 : write with random extension -int framesync64_debug_export(framesync64 _q, int _code); +int framesync64_debug_export(framesync64 _q, int _code, float complex * _payload_rx); // framesync64 object structure struct framesync64_s { // callback - framesync_callback callback; // user-defined callback function - void * userdata; // user-defined data structure + framesync_callback callback; // user-defined callback function + void * userdata; // user-defined data structure framesyncstats_s framesyncstats; // frame statistic object (synchronizer) framedatastats_s framedatastats; // frame statistic object (packet statistics) // synchronizer objects - unsigned int m; // filter delay (symbols) - float beta; // filter excess bandwidth factor - qdetector_cccf detector; // pre-demod detector - float tau_hat; // fractional timing offset estimate - float dphi_hat; // carrier frequency offset estimate - float phi_hat; // carrier phase offset estimate - float gamma_hat; // channel gain estimate - nco_crcf mixer; // coarse carrier frequency recovery - - // timing recovery objects, states - firpfb_crcf mf; // matched filter decimator - unsigned int npfb; // number of filters in symsync - int mf_counter; // matched filter output timer - unsigned int pfb_index; // filterbank index -#if FRAMESYNC64_ENABLE_EQ - eqlms_cccf equalizer; // equalizer (trained on p/n sequence) -#endif + unsigned int m; // filter delay (symbols) + float beta; // filter excess bandwidth factor + qdsync_cccf sync; // sequence detector // preamble - float complex preamble_pn[64]; // known 64-symbol p/n sequence - float complex preamble_rx[64]; // received p/n symbols - + float complex preamble_pn[64]; // known 64-symbol p/n sequence + float complex preamble_rx[64]; // received p/n symbols + // payload decoder - float complex payload_rx [630]; // received payload symbols with pilots - float complex payload_sym[600]; // received payload symbols - unsigned char payload_dec[ 72]; // decoded payload bytes - qpacketmodem dec; // packet demodulator/decoder - qpilotsync pilotsync; // pilot extraction, carrier recovery - int payload_valid; // did payload pass crc? - - // status variables - enum { - FRAMESYNC64_STATE_DETECTFRAME=0, // detect frame (seek p/n sequence) - FRAMESYNC64_STATE_RXPREAMBLE, // receive p/n sequence - FRAMESYNC64_STATE_RXPAYLOAD, // receive payload data - } state; - unsigned int preamble_counter; // counter: num of p/n syms received - unsigned int payload_counter; // counter: num of payload syms received + float complex payload_sym[600]; // received payload symbols + unsigned char payload_dec[ 72]; // decoded payload bytes + qpacketmodem dec; // packet demodulator/decoder + qpilotsync pilotsync; // pilot extraction, carrier recovery + int payload_valid; // did payload pass crc? windowcf buf_debug; // debug: raw input samples char * prefix; // debug: filename prefix @@ -137,24 +96,10 @@ framesync64 framesync64_create(framesync_callback _callback, } msequence_destroy(ms); - // create frame detector - unsigned int k = 2; // samples/symbol - q->detector = qdetector_cccf_create_linear(q->preamble_pn, 64, LIQUID_FIRFILT_ARKAISER, k, q->m, q->beta); - qdetector_cccf_set_threshold(q->detector, 0.5f); - - // create symbol timing recovery filters - q->npfb = 64; // number of filters in the bank - q->mf = firpfb_crcf_create_rnyquist(LIQUID_FIRFILT_ARKAISER, q->npfb,k,q->m,q->beta); - -#if FRAMESYNC64_ENABLE_EQ - // create equalizer - unsigned int p = 3; - q->equalizer = eqlms_cccf_create_lowpass(2*k*p+1, 0.4f); - eqlms_cccf_set_bw(q->equalizer, 0.05f); -#endif - - // create down-coverters for carrier phase tracking - q->mixer = nco_crcf_create(LIQUID_NCO); + // create frame detector with callback to run frame sync in one step + q->sync = qdsync_cccf_create_linear(q->preamble_pn, 64, + LIQUID_FIRFILT_ARKAISER, 2, q->m, q->beta, framesync64_callback_internal, q); + qdsync_cccf_set_buf_len(q->sync, 64 + 630); // create payload demodulator/decoder object int check = LIQUID_CRC_24; @@ -163,13 +108,12 @@ framesync64 framesync64_create(framesync_callback _callback, int mod_scheme = LIQUID_MODEM_QPSK; q->dec = qpacketmodem_create(); qpacketmodem_configure(q->dec, 72, check, fec0, fec1, mod_scheme); - //qpacketmodem_print(q->dec); assert( qpacketmodem_get_frame_len(q->dec)==600 ); // create pilot synchronizer q->pilotsync = qpilotsync_create(600, 21); assert( qpilotsync_get_frame_len(q->pilotsync)==630); - + // reset global data counters framesync64_reset_framedatastats(q); @@ -199,22 +143,24 @@ framesync64 framesync64_copy(framesync64 q_orig) memmove(q_copy, q_orig, sizeof(struct framesync64_s)); // set callback and userdata fields - q_copy->callback = q_orig->callback; - q_copy->userdata = q_orig->userdata; + //q_copy->callback = q_orig->callback; + //q_copy->userdata = q_orig->userdata; // copy objects - q_copy->detector = qdetector_cccf_copy(q_orig->detector); - q_copy->mixer = nco_crcf_copy (q_orig->mixer); - q_copy->mf = firpfb_crcf_copy (q_orig->mf); - q_copy->dec = qpacketmodem_copy (q_orig->dec); - q_copy->pilotsync= qpilotsync_copy (q_orig->pilotsync); - q_copy->buf_debug= windowcf_copy (q_orig->buf_debug); + q_copy->sync = qdsync_cccf_copy (q_orig->sync); + q_copy->dec = qpacketmodem_copy(q_orig->dec); + q_copy->pilotsync= qpilotsync_copy (q_orig->pilotsync); + q_copy->buf_debug= windowcf_copy (q_orig->buf_debug); // set prefix value q_copy->prefix = NULL; q_copy->filename = NULL; framesync64_set_prefix(q_copy, q_orig->prefix); + // update the context for the sync object to that detected frames will + // apply to this new frame synchronizer object + qdsync_cccf_set_context(q_copy->sync, q_copy); + return q_copy; } @@ -222,15 +168,10 @@ framesync64 framesync64_copy(framesync64 q_orig) int framesync64_destroy(framesync64 _q) { // destroy synchronization objects - qdetector_cccf_destroy(_q->detector); // frame detector - firpfb_crcf_destroy (_q->mf); // matched filter - nco_crcf_destroy (_q->mixer); // coarse NCO - qpacketmodem_destroy (_q->dec); // payload demodulator - qpilotsync_destroy (_q->pilotsync); // pilot synchronizer -#if FRAMESYNC64_ENABLE_EQ - eqlms_cccf_destroy (_q->equalizer); // LMS equalizer -#endif - windowcf_destroy(_q->buf_debug); + qdsync_cccf_destroy (_q->sync); // frame detector/synchronizer + qpacketmodem_destroy(_q->dec); // payload demodulator + qpilotsync_destroy (_q->pilotsync); // pilot synchronizer + windowcf_destroy (_q->buf_debug); // free main object memory free(_q); @@ -247,23 +188,8 @@ int framesync64_print(framesync64 _q) // reset frame synchronizer object int framesync64_reset(framesync64 _q) { - // reset binary pre-demod synchronizer - qdetector_cccf_reset(_q->detector); - - // reset carrier recovery objects - nco_crcf_reset(_q->mixer); - - // reset symbol timing recovery state - firpfb_crcf_reset(_q->mf); - - // reset state - _q->state = FRAMESYNC64_STATE_DETECTFRAME; - _q->preamble_counter= 0; - _q->payload_counter = 0; - - // reset frame statistics - _q->framesyncstats.evm = 0.0f; - + // reset synchronizer + qdsync_cccf_reset(_q->sync); return LIQUID_OK; } @@ -291,233 +217,64 @@ int framesync64_execute(framesync64 _q, float complex * _x, unsigned int _n) { - unsigned int i; - for (i=0; i<_n; i++) { - // push sample into debug buffer - windowcf_push(_q->buf_debug, _x[i]); - - switch (_q->state) { - case FRAMESYNC64_STATE_DETECTFRAME: - // detect frame (look for p/n sequence) - framesync64_execute_seekpn(_q, _x[i]); - break; - case FRAMESYNC64_STATE_RXPREAMBLE: - // receive p/n sequence symbols - framesync64_execute_rxpreamble(_q, _x[i]); - break; - case FRAMESYNC64_STATE_RXPAYLOAD: - // receive payload symbols - framesync64_execute_rxpayload(_q, _x[i]); - break; - default: - return liquid_error(LIQUID_EINT,"framesync64_exeucte(), unknown/unsupported state"); - } - } - return LIQUID_OK; + return qdsync_cccf_execute(_q->sync, _x, _n); } -// +// // internal methods // -// execute synchronizer, seeking p/n sequence -// _q : frame synchronizer object -// _x : input sample -// _sym : demodulated symbol -int framesync64_execute_seekpn(framesync64 _q, - float complex _x) -{ - // push through pre-demod synchronizer - float complex * v = qdetector_cccf_execute(_q->detector, _x); - - // check if frame has been detected - if (v != NULL) { - // get estimates - _q->tau_hat = qdetector_cccf_get_tau (_q->detector); - _q->gamma_hat = qdetector_cccf_get_gamma(_q->detector); - _q->dphi_hat = qdetector_cccf_get_dphi (_q->detector); - _q->phi_hat = qdetector_cccf_get_phi (_q->detector); - - // set appropriate filterbank index - if (_q->tau_hat > 0) { - _q->pfb_index = (unsigned int)( _q->tau_hat * _q->npfb) % _q->npfb; - _q->mf_counter = 0; - } else { - _q->pfb_index = (unsigned int)((1.0f+_q->tau_hat) * _q->npfb) % _q->npfb; - _q->mf_counter = 1; - } - //printf("***** frame detected! tau-hat:%8.4f(%u/%u), dphi-hat:%8.4f, gamma:%8.2f dB\n", - // _q->tau_hat, _q->pfb_index, _q->npfb, _q->dphi_hat, 20*log10f(_q->gamma_hat)); - - // output filter scale - firpfb_crcf_set_scale(_q->mf, 0.5f / _q->gamma_hat); - - // set frequency/phase of mixer - nco_crcf_set_frequency(_q->mixer, _q->dphi_hat); - nco_crcf_set_phase (_q->mixer, _q->phi_hat ); - - // update state - _q->state = FRAMESYNC64_STATE_RXPREAMBLE; - - // run buffered samples through synchronizer - unsigned int buf_len = qdetector_cccf_get_buf_len(_q->detector); - framesync64_execute(_q, v, buf_len); - } - return LIQUID_OK; -} - -// step receiver mixer, matched filter, decimator -// _q : frame synchronizer -// _x : input sample -// _y : output symbol -int framesync64_step(framesync64 _q, - float complex _x, - float complex * _y) +// synchronization callback, return 0:continue, 1:reset +int framesync64_callback_internal(float complex * _buf, + unsigned int _buf_len, + void * _context) { - // mix sample down - float complex v; - nco_crcf_mix_down(_q->mixer, _x, &v); - nco_crcf_step (_q->mixer); - - // push sample into filterbank - firpfb_crcf_push (_q->mf, v); - firpfb_crcf_execute(_q->mf, _q->pfb_index, &v); - -#if FRAMESYNC64_ENABLE_EQ - // push sample through equalizer - eqlms_cccf_push(_q->equalizer, v); -#endif - - // increment counter to determine if sample is available - _q->mf_counter++; - int sample_available = (_q->mf_counter >= 1) ? 1 : 0; - - // set output sample if available - if (sample_available) { -#if FRAMESYNC64_ENABLE_EQ - // compute equalizer output - eqlms_cccf_execute(_q->equalizer, &v); -#endif - - // set output - *_y = v; - - // decrement counter by k=2 samples/symbol - _q->mf_counter -= 2; + // type cast context input as frame synchronizer object + framesync64 _q = (framesync64)_context; + + // recover data symbols from pilots (input buffer with 64-symbol preamble offset) + qpilotsync_execute(_q->pilotsync, _buf + 64, _q->payload_sym); + + // decode payload + _q->payload_valid = qpacketmodem_decode(_q->dec, + _q->payload_sym, + _q->payload_dec); + + // update statistics + _q->framedatastats.num_frames_detected++; + _q->framedatastats.num_headers_valid += _q->payload_valid; + _q->framedatastats.num_payloads_valid += _q->payload_valid; + _q->framedatastats.num_bytes_received += _q->payload_valid ? 64 : 0; + + // set framesyncstats internals + _q->framesyncstats.evm = qpacketmodem_get_demodulator_evm(_q->dec); + _q->framesyncstats.rssi = 20*log10f( qdsync_cccf_get_gamma(_q->sync) ); + _q->framesyncstats.cfo = qdsync_cccf_get_dphi(_q->sync) + qpilotsync_get_dphi(_q->pilotsync) / 2.0f; + _q->framesyncstats.framesyms = _q->payload_sym; + _q->framesyncstats.num_framesyms = 600; + _q->framesyncstats.mod_scheme = LIQUID_MODEM_QPSK; + _q->framesyncstats.mod_bps = 2; + _q->framesyncstats.check = LIQUID_CRC_24; + _q->framesyncstats.fec0 = LIQUID_FEC_NONE; + _q->framesyncstats.fec1 = LIQUID_FEC_GOLAY2412; + + // invoke callback + if (_q->callback != NULL) { + int rc = + _q->callback(&_q->payload_dec[0], // header is first 8 bytes + _q->payload_valid, + &_q->payload_dec[8], // payload is last 64 bytes + 64, + _q->payload_valid, + _q->framesyncstats, + _q->userdata); + + // export debugging based on return value + framesync64_debug_export(_q, rc, _buf+64); } - // return flag - return sample_available; -} - -// execute synchronizer, receiving p/n sequence -// _q : frame synchronizer object -// _x : input sample -// _sym : demodulated symbol -int framesync64_execute_rxpreamble(framesync64 _q, - float complex _x) -{ - // step synchronizer - float complex mf_out = 0.0f; - int sample_available = framesync64_step(_q, _x, &mf_out); - - // compute output if timeout - if (sample_available) { - - // save output in p/n symbols buffer -#if FRAMESYNC64_ENABLE_EQ - unsigned int delay = 2*_q->m + 3; // delay from matched filter and equalizer -#else - unsigned int delay = 2*_q->m; // delay from matched filter -#endif - if (_q->preamble_counter >= delay) { - unsigned int index = _q->preamble_counter-delay; - - _q->preamble_rx[index] = mf_out; - -#if FRAMESYNC64_ENABLE_EQ - // train equalizer - eqlms_cccf_step(_q->equalizer, _q->preamble_pn[index], mf_out); -#endif - } - - // update p/n counter - _q->preamble_counter++; - - // update state - if (_q->preamble_counter == 64 + delay) - _q->state = FRAMESYNC64_STATE_RXPAYLOAD; - } - return LIQUID_OK; -} - -// execute synchronizer, receiving payload -// _q : frame synchronizer object -// _x : input sample -// _sym : demodulated symbol -int framesync64_execute_rxpayload(framesync64 _q, - float complex _x) -{ - // step synchronizer - float complex mf_out = 0.0f; - int sample_available = framesync64_step(_q, _x, &mf_out); - - // compute output if timeout - if (sample_available) { - // save payload symbols (modem input/output) - _q->payload_rx[_q->payload_counter] = mf_out; - - // increment counter - _q->payload_counter++; - - if (_q->payload_counter == 630) { - // recover data symbols from pilots - qpilotsync_execute(_q->pilotsync, _q->payload_rx, _q->payload_sym); - - // decode payload - _q->payload_valid = qpacketmodem_decode(_q->dec, - _q->payload_sym, - _q->payload_dec); - - // update statistics - _q->framedatastats.num_frames_detected++; - _q->framedatastats.num_headers_valid += _q->payload_valid; - _q->framedatastats.num_payloads_valid += _q->payload_valid; - _q->framedatastats.num_bytes_received += _q->payload_valid ? 64 : 0; - - // invoke callback - if (_q->callback != NULL) { - // set framesyncstats internals - _q->framesyncstats.evm = qpacketmodem_get_demodulator_evm(_q->dec); //qpilotsync_get_evm(_q->pilotsync); - _q->framesyncstats.rssi = 20*log10f(_q->gamma_hat); - _q->framesyncstats.cfo = nco_crcf_get_frequency(_q->mixer); - _q->framesyncstats.framesyms = _q->payload_sym; - _q->framesyncstats.num_framesyms = 600; - _q->framesyncstats.mod_scheme = LIQUID_MODEM_QPSK; - _q->framesyncstats.mod_bps = 2; - _q->framesyncstats.check = LIQUID_CRC_24; - _q->framesyncstats.fec0 = LIQUID_FEC_NONE; - _q->framesyncstats.fec1 = LIQUID_FEC_GOLAY2412; - - // invoke callback method - int rc = - _q->callback(&_q->payload_dec[0], // header is first 8 bytes - _q->payload_valid, - &_q->payload_dec[8], // payload is last 64 bytes - 64, - _q->payload_valid, - _q->framesyncstats, - _q->userdata); - - // export debugging based on return value - framesync64_debug_export(_q, rc); - } - - // reset frame synchronizer - return framesync64_reset(_q); - } - } - return LIQUID_OK; + // reset frame synchronizer + return framesync64_reset(_q); } // DEPRECATED: enable debugging @@ -542,14 +299,14 @@ int framesync64_debug_print(framesync64 _q, // get detection threshold float framesync64_get_threshold(framesync64 _q) { - return qdetector_cccf_get_threshold(_q->detector); + return qdsync_cccf_get_threshold(_q->sync); } // set detection threshold int framesync64_set_threshold(framesync64 _q, float _threshold) { - return qdetector_cccf_set_threshold(_q->detector, _threshold); + return qdsync_cccf_set_threshold(_q->sync, _threshold); } // set prefix for exporting debugging files, default: "framesync64" @@ -606,8 +363,9 @@ framedatastats_s framesync64_get_framedatastats(framesync64 _q) } // export debugging samples to file -int framesync64_debug_export(framesync64 _q, - int _code) +int framesync64_debug_export(framesync64 _q, + int _code, + float complex * _payload_rx) { // determine what to do based on callback return code if (_code == 0) { @@ -651,14 +409,16 @@ int framesync64_debug_export(framesync64 _q, //framesyncstats_export(_q->framesyncstats, fid); // export measured offsets - fwrite(&(_q->tau_hat), sizeof(float), 1, fid); - fwrite(&(_q->dphi_hat), sizeof(float), 1, fid); - fwrite(&(_q->phi_hat), sizeof(float), 1, fid); - fwrite(&(_q->gamma_hat),sizeof(float), 1, fid); - fwrite(&(_q->framesyncstats.evm), sizeof(float), 1, fid); + float tau_hat = 0.0f; + float phi_hat = 0.0f; + fwrite(&tau_hat, sizeof(float), 1, fid); + fwrite(&(_q->framesyncstats.cfo), sizeof(float), 1, fid); + fwrite(&phi_hat, sizeof(float), 1, fid); + fwrite(&(_q->framesyncstats.rssi), sizeof(float), 1, fid); + fwrite(&(_q->framesyncstats.evm), sizeof(float), 1, fid); // export payload values - fwrite(_q->payload_rx, sizeof(float complex), 630, fid); + fwrite(_payload_rx, sizeof(float complex), 630, fid); fwrite(_q->payload_sym, sizeof(float complex), 600, fid); fwrite(_q->payload_dec, sizeof(unsigned char), 72, fid); diff --git a/src/framing/tests/framesync64_autotest.c b/src/framing/tests/framesync64_autotest.c index ba495c99e..7a1b78cc7 100644 --- a/src/framing/tests/framesync64_autotest.c +++ b/src/framing/tests/framesync64_autotest.c @@ -121,6 +121,8 @@ void autotest_framesync64_copy() // copy object, but set different context framesync64 fs1 = framesync64_copy(fs0); framesync64_set_userdata(fs1, (void*)&frames_recovered_1); + framesync64_print(fs0); + framesync64_print(fs1); // try to receive the frame with each receiver for (i=0; i Date: Wed, 22 Feb 2023 17:16:27 -0500 Subject: [PATCH 050/186] qdsync: making macro in global header --- include/liquid.h | 150 ++++++++++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 67 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 2773884f3..38f575cd9 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -6260,73 +6260,89 @@ float qdetector_cccf_get_gamma (qdetector_cccf _q); // channel gain float qdetector_cccf_get_dphi (qdetector_cccf _q); // carrier frequency offset estimate float qdetector_cccf_get_phi (qdetector_cccf _q); // carrier phase offset estimate -// Frame detector and synchronizer; uses a novel correlation method to -// detect a synchronization pattern, estimate carrier frequency and -// phase offsets as well as timing phase, then correct for these -// impairments in a simple interface suitable for custom frame recovery. -typedef struct qdsync_cccf_s * qdsync_cccf; - -// synchronization callback, return 0:continue, 1:reset -typedef int (*qdsync_callback)(liquid_float_complex * _buf, - unsigned int _buf_len, - void * _context); -// metadata struct: -// - sample count since object was created -// - sample count since beginning of frame - -// create detector with generic sequence -// _s : sample sequence -// _s_len : length of sample sequence -qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _s, - unsigned int _s_len, - int _ftype, - unsigned int _k, - unsigned int _m, - float _beta, - qdsync_callback _callback, - void * _context); - -// Copy object recursively including all internal objects and state -qdsync_cccf qdsync_cccf_copy(qdsync_cccf _q); - -int qdsync_cccf_destroy(qdsync_cccf _q); -int qdsync_cccf_reset (qdsync_cccf _q); -int qdsync_cccf_print (qdsync_cccf _q); - -// get detection threshold -float qdsync_cccf_get_threshold(qdsync_cccf _q); - -// set detection threshold -int qdsync_cccf_set_threshold(qdsync_cccf _q, float _threshold); - -// set carrier offset search range -int qdsync_cccf_set_range(qdsync_cccf _q, - float _dphi_max); - -// set callback method -int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); - -// set context value -int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); - -// Set callback buffer size (the number of symbol provided to the callback -// whenever it is invoked). -int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len); - -// execute block of samples -int qdsync_cccf_execute(qdsync_cccf _q, - liquid_float_complex * _buf, - unsigned int _buf_len); - -// is synchronizer actively running? -int qdsync_cccf_is_open(qdsync_cccf _q); +// +// qdsync +// +#define LIQUID_QDSYNC_MANGLE_CCCF(name) LIQUID_CONCAT(qdsync_cccf,name) + +#define LIQUID_QDSYNC_DEFINE_API(QDSYNC,TO,TC,TI) \ + \ +/* Frame detector and synchronizer; uses a novel correlation method to */ \ +/* detect a synchronization pattern, estimate carrier frequency and */ \ +/* phase offsets as well as timing phase, then correct for these */ \ +/* impairments in a simple interface suitable for custom frame recovery.*/ \ +typedef struct QDSYNC(_s) * QDSYNC(); \ + \ +/* synchronization callback, return 0:continue, 1:reset */ \ +typedef int (*QDSYNC(_callback))(TO * _buf, \ + unsigned int _buf_len, \ + void * _context); \ + \ +/* create detector with generic sequence */ \ +/* _s : sample sequence */ \ +/* _s_len : length of sample sequence */ \ +/* _ftype : filter type */ \ +/* _k : samples per symbol */ \ +/* _m : filter semi-length */ \ +/* _beta : filter excess bandwidth factor */ \ +/* _callback : user-defined callback */ \ +/* _context : user-defined context */ \ +QDSYNC() QDSYNC(_create_linear)(TI * _s, \ + unsigned int _s_len, \ + int _ftype, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + QDSYNC(_callback) _callback, \ + void * _context); \ + \ +/* Copy object recursively including all internal objects and state */ \ +QDSYNC() QDSYNC(_copy)(QDSYNC() _q); \ + \ +int QDSYNC(_destroy)(QDSYNC() _q); \ +int QDSYNC(_reset) (QDSYNC() _q); \ +int QDSYNC(_print) (QDSYNC() _q); \ + \ +/* get detection threshold */ \ +float QDSYNC(_get_threshold)(QDSYNC() _q); \ + \ +/* set detection threshold */ \ +int QDSYNC(_set_threshold)(QDSYNC() _q, \ + float _threshold); \ + \ +/* set carrier offset search range */ \ +int QDSYNC(_set_range)(QDSYNC() _q, \ + float _dphi_max); \ + \ +/* set callback method */ \ +int QDSYNC(_set_callback)(QDSYNC() _q, \ + QDSYNC(_callback) _callback); \ + \ +/* set context value */ \ +int QDSYNC(_set_context)(QDSYNC() _q, void * _context); \ + \ +/* Set callback buffer size (the number of symbol provided to the */ \ +/* callback whenever it is invoked). */ \ +int QDSYNC(_set_buf_len )(QDSYNC() _q, unsigned int _buf_len); \ + \ +/* execute block of samples */ \ +int QDSYNC(_execute)(QDSYNC() _q, \ + TI * _buf, \ + unsigned int _buf_len); \ + \ +/* Return flag indicating if synchronizer actively running. */ \ +int QDSYNC(_is_open)(QDSYNC() _q); \ + \ +float QDSYNC(_get_rxy) (QDSYNC() _q); \ +float QDSYNC(_get_tau) (QDSYNC() _q); \ +float QDSYNC(_get_gamma)(QDSYNC() _q); \ +float QDSYNC(_get_dphi) (QDSYNC() _q); \ +float QDSYNC(_get_phi) (QDSYNC() _q); \ -// get detection metrics and offsets -float qdsync_cccf_get_rxy (qdsync_cccf _q); // correlator output -float qdsync_cccf_get_tau (qdsync_cccf _q); // fractional timing offset estimate -float qdsync_cccf_get_gamma(qdsync_cccf _q); // channel gain -float qdsync_cccf_get_dphi (qdsync_cccf _q); // carrier frequency offset estimate -float qdsync_cccf_get_phi (qdsync_cccf _q); // carrier phase offset estimate +LIQUID_QDSYNC_DEFINE_API(LIQUID_QDSYNC_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) // // Pre-demodulation detector From 99763b13a069a815b66cae28dd3d082ea2b80de8 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 19 Feb 2023 16:53:21 -0500 Subject: [PATCH 051/186] ignoring auto-generated files from autotest --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 912b3441d..0898d51ea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.gcno *.o *.so +*~ # ignore swap files for vim editing *.swp @@ -30,6 +31,9 @@ config.status makefile xautotest *.m +autotest.json +autotest/logs/*.bin +autotest/logs/*.gnu # miscellany octave-core From e819dfeddc8d982451b379dd1335a77ad0c51056 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 22 Feb 2023 19:22:37 -0500 Subject: [PATCH 052/186] qdsync/autotest: adding test to periodically change callback buf len --- src/framing/tests/qdsync_cccf_autotest.c | 57 ++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index d8e0a78ad..980f9844f 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -150,6 +150,63 @@ void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } +// test different configurations +void autotest_qdsync_config() +{ + // options + unsigned int seq_len = 2400; // total number of sync symbols + unsigned int k = 2; // samples/symbol + unsigned int m = 12; // filter delay [symbols] + float beta = 0.3f; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + + // generate synchronization sequence (QPSK symbols) + float complex seq_tx[seq_len]; // transmitted + float complex seq_rx[seq_len]; // received with initial correction + unsigned int i; + for (i=0; i Date: Thu, 23 Feb 2023 08:07:05 -0500 Subject: [PATCH 053/186] qdsync/autotest: disabling test to set buf len temporarily --- src/framing/tests/qdsync_cccf_autotest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 980f9844f..36509d98a 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -150,8 +150,8 @@ void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } -// test different configurations -void autotest_qdsync_config() +// test setting buffer length ot different sizes throughout run +void xautotest_qdsync_set_buf_len() { // options unsigned int seq_len = 2400; // total number of sync symbols From 3d56e87206bd4d03ad527975411c1aaa1fc03d7b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 14:16:56 +0000 Subject: [PATCH 054/186] qdsync: adjusting logic for resizing buffer to fix memory errors --- src/framing/src/qdsync_cccf.c | 19 +++++++++++++------ src/framing/tests/qdsync_cccf_autotest.c | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 1469b4f9c..2dcf01fbf 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -229,7 +229,10 @@ int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) // buffer might not be empty, but we aren't resizing within this space; // ok to resize so long as old samples are copied _q->buf_out_len = _buf_len; - _q->buf_out = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + if (buf_new == NULL) + return liquid_error(LIQUID_EIMEM,"qdsync_cccf_set_buf_len(), could not allocate %u samples", _buf_len); + _q->buf_out = buf_new; } else { // we are shrinking the buffer below the number of samples it currently // holds; invoke the callback as many times as needed to reduce its size @@ -242,11 +245,15 @@ int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) index += _buf_len; _q->buf_out_counter -= _buf_len; } - // now perform the reallocation, but we cannot use 'realloc' here because - // we are not copying values at the front of the buffer - float complex * buf_new = (float complex*)malloc(_buf_len * sizeof(float complex)); - memmove(buf_new, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); - free(_q->buf_out); + + // copy old values to front of buffer + memmove(_q->buf_out, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); + + // now resize the buffer appropriately + _q->buf_out_len = _buf_len; + float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + if (buf_new == NULL) + return liquid_error(LIQUID_EIMEM,"qdsync_cccf_set_buf_len(), could not allocate %u samples", _buf_len); _q->buf_out = buf_new; } return LIQUID_OK; diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 36509d98a..76d747637 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -151,7 +151,7 @@ void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } // test setting buffer length ot different sizes throughout run -void xautotest_qdsync_set_buf_len() +void autotest_qdsync_set_buf_len() { // options unsigned int seq_len = 2400; // total number of sync symbols From 362e78913f27468dcdec5639c8a08af31b7d5523 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 11:42:32 -0500 Subject: [PATCH 055/186] qdsync/autotest: adding config() test, extending coverage of existing --- src/framing/tests/qdsync_cccf_autotest.c | 68 +++++++++++++++++++----- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 76d747637..db59ce35e 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2018 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -64,6 +64,7 @@ void testbench_qdsync_linear(unsigned int _k, float beta = _beta; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float nstd = 0.001f; + float tau = 0.400f; // fractional sample timing offset // generate synchronization sequence (QPSK symbols) float complex seq_tx[seq_len]; // transmitted @@ -85,7 +86,7 @@ void testbench_qdsync_linear(unsigned int _k, // create delay object fdelay_crcf delay = fdelay_crcf_create_default(100); - fdelay_crcf_set_delay(delay, 10*k - 0.4); + fdelay_crcf_set_delay(delay, 10*k + tau); // run signal through sync object float complex buf[k]; @@ -107,8 +108,11 @@ void testbench_qdsync_linear(unsigned int _k, // run through synchronizer qdsync_cccf_execute(q, buf, k); } - float dphi_hat = qdsync_cccf_get_dphi(q); - float phi_hat = qdsync_cccf_get_phi(q); + float rxy_hat = qdsync_cccf_get_rxy (q); + float tau_hat = qdsync_cccf_get_tau (q); + float gamma_hat= qdsync_cccf_get_gamma(q); + float dphi_hat = qdsync_cccf_get_dphi (q); + float phi_hat = qdsync_cccf_get_phi (q); // compute error in terms of offset from unity; might be residual carrier phase/gain // TODO: perform residual carrier/phase error correction? @@ -118,11 +122,16 @@ void testbench_qdsync_linear(unsigned int _k, rmse += e*e; } rmse = 10*log10f( rmse / (float)seq_len ); - if (liquid_autotest_verbose) - printf("qdsync: dphi: %12.4e, phi: %12.8f, rmse: %12.3f\n", dphi_hat, phi_hat, rmse); - CONTEND_LESS_THAN( rmse, -30.0f ) - CONTEND_LESS_THAN( fabsf(dphi_hat), 1e-3f ) - CONTEND_LESS_THAN( fabsf( phi_hat), 0.4f ) + if (liquid_autotest_verbose) { + printf("qdsync: rxy:%5.3f, tau:%5.2f, gamma:%5.3f, dphi:%12.4e, phi:%8.5f, rmse:%5.2f\n", + rxy_hat, tau_hat, gamma_hat, dphi_hat, phi_hat, rmse); + } + CONTEND_LESS_THAN ( rmse, -30.0f ) + CONTEND_GREATER_THAN( rxy_hat, 0.75f ) + CONTEND_LESS_THAN ( fabsf(tau_hat-tau), 0.10f ) + CONTEND_GREATER_THAN( gamma_hat, 0.75f ) + CONTEND_LESS_THAN ( fabsf(dphi_hat), 1e-3f ) + CONTEND_LESS_THAN ( fabsf( phi_hat), 0.4f ) // clean up objects qdsync_cccf_destroy(q); @@ -146,9 +155,9 @@ void testbench_qdsync_linear(unsigned int _k, } // test specific configurations -void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } -void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } -void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } +void autotest_qdsync_cccf_k2() { testbench_qdsync_linear(2, 7, 0.3f); } +void autotest_qdsync_cccf_k3() { testbench_qdsync_linear(3, 7, 0.3f); } +void autotest_qdsync_cccf_k4() { testbench_qdsync_linear(4, 7, 0.3f); } // test setting buffer length ot different sizes throughout run void autotest_qdsync_set_buf_len() @@ -291,3 +300,38 @@ void autotest_qdsync_cccf_copy() qdsync_cccf_destroy(q_copy); } +void autotest_qdsync_cccf_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping qdsync_cccf config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(qdsync_cccf_copy(NULL)); + CONTEND_ISNULL(qdsync_cccf_create_linear(NULL,0,LIQUID_FIRFILT_ARKAISER,4,12,0.25f,NULL,NULL)); + + // create proper object and test configurations + float complex seq[] = {+1,-1,+1,-1,-1,+1,-1,+1,-1,+1,-1,+1,+1,+1,-1,+1,-1,-1,-1,-1,}; + qdsync_cccf q = qdsync_cccf_create_linear(seq,20,LIQUID_FIRFILT_ARKAISER,4,12,0.25f,NULL,NULL); + + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_print(q)) + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_set_callback(q,autotest_qdsync_callback)) + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_set_context(q,NULL)) + + // set/get threshold + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_set_threshold(q,0.654321f)) + CONTEND_EQUALITY(0.654321f, qdsync_cccf_get_threshold(q)) + + // set invalid buffer length + CONTEND_INEQUALITY(LIQUID_OK, qdsync_cccf_set_buf_len(q,0)) + + // query properties + CONTEND_EQUALITY(0, qdsync_cccf_is_open(q)) + + // destroy object + qdsync_cccf_destroy(q); +} + From 85d23e55c5cac5ca29c1686a319e6ac611686a11 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 15:22:19 -0500 Subject: [PATCH 056/186] gasearch: adding (basic) autotest on simple toy problem --- examples/gasearch_example.c | 112 ++++++++-------------------- makefile.in | 1 + src/optim/tests/gasearch_autotest.c | 96 ++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 79 deletions(-) create mode 100644 src/optim/tests/gasearch_autotest.c diff --git a/examples/gasearch_example.c b/examples/gasearch_example.c index 677bb3fba..ddc903651 100644 --- a/examples/gasearch_example.c +++ b/examples/gasearch_example.c @@ -1,7 +1,4 @@ -// -// gasearch_example.c -// - +// example demonstrating performance of GA search algorithm for finding basic function peak #include #include #include @@ -10,57 +7,59 @@ #define OUTPUT_FILENAME "gasearch_example.m" -// utility callback function -float utility_callback(void * _userdata, chromosome _c); - -// peak callback function; value nearest {0.5, 0.5, 0.5, ...} -float peak_callback(void * _userdata, chromosome _c); - -// rosenbrock callback function -float rosenbrock_callback(void * _userdata, chromosome _c); +// peak callback function; value nearest {p, p, p, ...} where p = 1/sqrt(2) +float peak_callback(void * _userdata, chromosome _c) +{ + unsigned int i, n = chromosome_get_num_traits(_c); + float u_global = 1.0f; + float sig = 0.2f; + float p = M_SQRT1_2; + for (i=0; i +#include +#include +#include +#include + +#include "liquid.internal.h" + +// peak callback function; value nearest {p, p, p, ...} where p = 1/sqrt(2) +float gasearch_autotest_peak_callback(void * _userdata, chromosome _c) +{ + unsigned int i, n = chromosome_get_num_traits(_c); + float u = 1.0f; + float sig = 0.2f; + float p = M_SQRT1_2; + for (i=0; i Date: Sat, 25 Feb 2023 16:37:57 -0500 Subject: [PATCH 057/186] gasearch/autotest: adding configuration tests --- src/optim/src/chromosome.c | 46 ++++++------ src/optim/src/gasearch.c | 12 +++- src/optim/tests/gasearch_autotest.c | 105 +++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 23 deletions(-) diff --git a/src/optim/src/chromosome.c b/src/optim/src/chromosome.c index 11dcc639b..ea1147a52 100644 --- a/src/optim/src/chromosome.c +++ b/src/optim/src/chromosome.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,32 +37,33 @@ // _bits_per_trait : array of bits/trait [size: _num_traits x 1] // _num_traits : number of traits in this chromosome chromosome chromosome_create(unsigned int * _bits_per_trait, - unsigned int _num_traits) + unsigned int _num_traits) { + // validate input + unsigned int i; + if (_num_traits < 1) + return liquid_error_config("chromosome_create(), must have at least one trait"); + for (i=0; i<_num_traits; i++) { + if (_bits_per_trait[i] > LIQUID_CHROMOSOME_MAX_SIZE) + return liquid_error_config("chromosome_create(), bits/trait cannot exceed %u", LIQUID_CHROMOSOME_MAX_SIZE); + } + chromosome q; q = (chromosome) malloc( sizeof(struct chromosome_s) ); q->num_traits = _num_traits; - // validate input - if (q->num_traits < 1) - return liquid_error_config("chromosome_create(), must have at least one trait"); - // initialize internal arrays q->bits_per_trait = (unsigned int *) malloc(q->num_traits*sizeof(unsigned int)); q->max_value = (unsigned long*) malloc(q->num_traits*sizeof(unsigned long)); q->traits = (unsigned long*) malloc(q->num_traits*sizeof(unsigned long)); // copy/initialize values - unsigned int i; q->num_bits = 0; for (i=0; inum_traits; i++) { q->bits_per_trait[i] = _bits_per_trait[i]; - if (q->bits_per_trait[i] > LIQUID_CHROMOSOME_MAX_SIZE) - return liquid_error_config("chromosome_create(), bits/trait cannot exceed %u", LIQUID_CHROMOSOME_MAX_SIZE); - - q->max_value[i] = 1 << q->bits_per_trait[i]; - q->traits[i] = 0; + q->max_value[i] = 1LU << q->bits_per_trait[i]; + q->traits[i] = 0LU; q->num_bits += q->bits_per_trait[i]; } @@ -79,6 +80,8 @@ chromosome chromosome_create_basic(unsigned int _num_traits, // validate input if (_num_traits == 0) return liquid_error_config("chromosome_create_basic(), must have at least one trait"); + if (_bits_per_trait == 0 || _bits_per_trait > 64) + return liquid_error_config("chromosome_create_basic(), bits per trait out of range"); unsigned int * bpt = (unsigned int *) malloc(_num_traits*sizeof(unsigned int)); unsigned int i; @@ -175,6 +178,7 @@ int chromosome_init(chromosome _c, { unsigned int i; for (i=0; i<_c->num_traits; i++) { + //printf("===> [%3u] bits:%3u, max:%12lu, value:%12lu\n", i, _c->bits_per_trait[i], _c->max_value[i], _v[i]); if (_v[i] >= _c->max_value[i]) return liquid_error(LIQUID_EIRANGE,"chromosome_init(), value exceeds maximum"); @@ -189,12 +193,14 @@ int chromosome_initf(chromosome _c, { unsigned int i; for (i=0; i<_c->num_traits; i++) { - if (_v[i] > 1.0f || _v[i] < 0.0f) + if (_v[i] < 0.0f || _v[i] > 1.0f) return liquid_error(LIQUID_EIRANGE,"chromosome_initf(), value must be in [0,1]"); // quantize sample - unsigned int N = 1 << _c->bits_per_trait[i]; - _c->traits[i] = (unsigned int) floorf( _v[i] * N ); + unsigned long N = 1LU << _c->bits_per_trait[i]; + _c->traits[i] = (unsigned long) floorf( _v[i] * N ); + //printf("===> [%3u] quantizing %8.2f, bits:%3u, N:%12lu, trait:%12lu/%12lu => %12.8f\n", + // i, _v[i], _c->bits_per_trait[i], N, _c->traits[i], _c->max_value[i], chromosome_valuef(_c,i)); } return LIQUID_OK; } @@ -212,10 +218,10 @@ int chromosome_mutate(chromosome _q, for (i=0; i<_q->num_traits; i++) { unsigned int b = _q->bits_per_trait[i]; if (t == _index) { - _q->traits[i] ^= (unsigned long)(1 << (b-1)); + _q->traits[i] ^= (unsigned long)(1LU << (b-1)); return LIQUID_OK; } else if (t > _index) { - _q->traits[i-1] ^= (unsigned long)(1 << (t-_index-1)); + _q->traits[i-1] ^= (unsigned long)(1LU << (t-_index-1)); return LIQUID_OK; } else { t += b; @@ -289,18 +295,18 @@ int chromosome_init_random(chromosome _q) { unsigned int i; for (i=0; i<_q->num_traits; i++) - _q->traits[i] = rand() & (_q->max_value[i]-1); + _q->traits[i] = rand() & (_q->max_value[i]-1LU); return LIQUID_OK; } -float chromosome_valuef(chromosome _q, +float chromosome_valuef(chromosome _q, unsigned int _index) { if (_index > _q->num_traits) { liquid_error(LIQUID_EIRANGE,"chromosome_valuef(), trait index exceeded"); return 0.0f; } - return (float) (_q->traits[_index]) / (float)(_q->max_value[_index] - 1); + return (float) (_q->traits[_index]) / (float)(_q->max_value[_index]-1LU); } unsigned int chromosome_value(chromosome _q, diff --git a/src/optim/src/gasearch.c b/src/optim/src/gasearch.c index 9ab3fb7ee..e33d190bd 100644 --- a/src/optim/src/gasearch.c +++ b/src/optim/src/gasearch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -66,8 +66,14 @@ gasearch gasearch_create_advanced(gasearch_utility _utility, gasearch ga; ga = (gasearch) malloc( sizeof(struct gasearch_s) ); + if (_utility == NULL) + return liquid_error_config("gasearch_create(), utility function cannot be NULL") + if (_parent == NULL) + return liquid_error_config("gasearch_create(), parent cannot be NULL") + if (_population_size < 2) + return liquid_error_config("gasearch_create(), population size exceeds minimum"); if (_population_size > LIQUID_GA_SEARCH_MAX_POPULATION_SIZE) - return liquid_error_config("gasearch_create(), population size exceeds maximum"); + return liquid_error_config("gasearch_create(), population size exceeds maximum (%u)",LIQUID_GA_SEARCH_MAX_POPULATION_SIZE); if (_mutation_rate < 0.0f || _mutation_rate > 1.0f) return liquid_error_config("gasearch_create(), mutation rate must be in [0,1]"); @@ -162,6 +168,8 @@ int gasearch_set_population_size(gasearch _g, // validate input if (_population_size < 2) return liquid_error(LIQUID_EICONFIG,"gasearch_set_population_size(), population must be at least 2"); + if (_population_size > LIQUID_GA_SEARCH_MAX_POPULATION_SIZE) + return liquid_error(LIQUID_EICONFIG,"gasearch_set_population_size(), population exceeds maximum (%u)",LIQUID_GA_SEARCH_MAX_POPULATION_SIZE); if (_selection_size == 0) return liquid_error(LIQUID_EICONFIG,"gasearch_set_population_size(), selection size must be greater than zero"); if (_selection_size >= _population_size) diff --git a/src/optim/tests/gasearch_autotest.c b/src/optim/tests/gasearch_autotest.c index e95afef36..77da5f9f3 100644 --- a/src/optim/tests/gasearch_autotest.c +++ b/src/optim/tests/gasearch_autotest.c @@ -82,7 +82,7 @@ void autotest_gasearch_peak() chromosome_printf(prototype); } - // destroy gradient descent search object + // destroy search object chromosome_destroy(prototype); gasearch_destroy(ga); @@ -94,3 +94,106 @@ void autotest_gasearch_peak() CONTEND_GREATER_THAN( optimum_utility, 0.70f ) } +// test chromosome configuration +void autotest_chromosome_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping chromosome config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // test chromosome + unsigned int bits_per_trait_invalid[8] = {6,6,6,6,6,6,6,1000}; + unsigned int bits_per_trait_valid [8] = {6,6,6,6,6,6,6, 32}; + CONTEND_ISNULL(chromosome_create(bits_per_trait_invalid, 8)) + CONTEND_ISNULL(chromosome_create(bits_per_trait_valid, 0)) + CONTEND_ISNULL(chromosome_create_basic(0, 12)) // too few traits + CONTEND_ISNULL(chromosome_create_basic(8, 0)) // bits per trait too small + CONTEND_ISNULL(chromosome_create_basic(8, 99)) // bits per trait too large + + // create prototype chromosome using basic method + chromosome prototype = chromosome_create_basic(20, 5); + CONTEND_EQUALITY(LIQUID_OK, chromosome_print(prototype)) + chromosome_destroy(prototype); + + // create prototype chromosome using more specific method + prototype = chromosome_create(bits_per_trait_valid, 8); + CONTEND_EQUALITY(LIQUID_OK, chromosome_print(prototype)) + + // test initialization + unsigned int values_invalid[] = {999,12,11,13,63,17, 3,123456789}; // invalid because first trait is only 6 bits + unsigned int values_valid [] = { 0,12,11,13,63,17, 3,123456789}; + CONTEND_INEQUALITY(LIQUID_OK, chromosome_init (prototype, values_invalid)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_init (prototype, values_valid )) + + // check individual values + CONTEND_EQUALITY( chromosome_value(prototype, 0), 0) + CONTEND_EQUALITY( chromosome_value(prototype, 1), 12) + CONTEND_EQUALITY( chromosome_value(prototype, 2), 11) + CONTEND_EQUALITY( chromosome_value(prototype, 3), 13) + CONTEND_EQUALITY( chromosome_value(prototype, 4), 63) + CONTEND_EQUALITY( chromosome_value(prototype, 5), 17) + CONTEND_EQUALITY( chromosome_value(prototype, 6), 3) + CONTEND_EQUALITY( chromosome_value(prototype, 7), 123456789) + + // test initialization (float values) + float valuesf_invalid[] = {0.0,0.1,0.2,0.3,0.4,0.5,0.6,999,}; + float valuesf_valid [] = {0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,}; + CONTEND_INEQUALITY(LIQUID_OK, chromosome_initf(prototype, valuesf_invalid)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_initf(prototype, valuesf_valid )) + + // check individual values + CONTEND_DELTA( chromosome_valuef(prototype, 0), 0.0f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 1), 0.1f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 2), 0.2f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 3), 0.3f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 4), 0.4f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 5), 0.5f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 6), 0.6f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 7), 0.7f, 0.02f ) + + // destroy objects + chromosome_destroy(prototype); +} + +// test configuration +void autotest_gasearch_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping gasearch config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // create prototype chromosome + chromosome prototype = chromosome_create_basic(8, 12); + + // check invalid function calls + CONTEND_ISNULL(gasearch_create_advanced( NULL, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, 16, 0.1f)) // bad utility function + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, NULL, LIQUID_OPTIM_MAXIMIZE, 0, 0.1f)) // bad parent chromosome + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, 0, 0.1f)) // bad population size + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, -1, 0.1f)) // bad population size + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, 16,-1.0f)) // bad mutation rate + + // create proper object and test configurations + gasearch ga = gasearch_create(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE); + CONTEND_EQUALITY(LIQUID_OK, gasearch_print(ga)) + + // test configurations + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga, 0, 8)) // population size too small + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga,-1, 8)) // population size too large + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga,24, 0)) // selection size too small + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga,24,24)) // selection size too large + CONTEND_EQUALITY (LIQUID_OK, gasearch_set_population_size(ga,24,12)) // ok + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_mutation_rate (ga,-1.0f)) // mutation rate out of range + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_mutation_rate (ga, 2.0f)) // mutation rate out of range + CONTEND_EQUALITY (LIQUID_OK, gasearch_set_mutation_rate (ga, 0.1f)) // ok + + // destroy objects + chromosome_destroy(prototype); + gasearch_destroy(ga); +} + From a25ee8708992d66e0562059c456d8480570b8728 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 16:46:13 -0500 Subject: [PATCH 058/186] chromosome/autotest: testing more edge cases in configuration --- src/optim/tests/gasearch_autotest.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/optim/tests/gasearch_autotest.c b/src/optim/tests/gasearch_autotest.c index 77da5f9f3..77ba6e2f8 100644 --- a/src/optim/tests/gasearch_autotest.c +++ b/src/optim/tests/gasearch_autotest.c @@ -120,13 +120,18 @@ void autotest_chromosome_config() // create prototype chromosome using more specific method prototype = chromosome_create(bits_per_trait_valid, 8); - CONTEND_EQUALITY(LIQUID_OK, chromosome_print(prototype)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_print (prototype)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_reset (prototype)) // test initialization unsigned int values_invalid[] = {999,12,11,13,63,17, 3,123456789}; // invalid because first trait is only 6 bits unsigned int values_valid [] = { 0,12,11,13,63,17, 3,123456789}; CONTEND_INEQUALITY(LIQUID_OK, chromosome_init (prototype, values_invalid)) CONTEND_EQUALITY (LIQUID_OK, chromosome_init (prototype, values_valid )) + CONTEND_EQUALITY ( 0, chromosome_value (prototype,999)) + CONTEND_EQUALITY ( 0.0f, chromosome_valuef (prototype,999)) + CONTEND_INEQUALITY(LIQUID_OK, chromosome_mutate (prototype,999)) + CONTEND_INEQUALITY(LIQUID_OK, chromosome_crossover(prototype,prototype,prototype,999)) // check individual values CONTEND_EQUALITY( chromosome_value(prototype, 0), 0) From c9621fc064864963a869c551c91a9448a27f12fc Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 18:33:59 -0500 Subject: [PATCH 059/186] qnsearch: adding basic autotest script --- makefile.in | 1 + src/optim/src/qnsearch.c | 6 +-- src/optim/tests/qnsearch_autotest.c | 63 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/optim/tests/qnsearch_autotest.c diff --git a/makefile.in b/makefile.in index 4b8787448..555c90508 100644 --- a/makefile.in +++ b/makefile.in @@ -1014,6 +1014,7 @@ $(optim_objects) : %.o : %.c $(include_headers) optim_autotests := \ src/optim/tests/gasearch_autotest.c \ src/optim/tests/gradsearch_autotest.c \ + src/optim/tests/qnsearch_autotest.c \ src/optim/tests/qs1dsearch_autotest.c \ # benchmarks diff --git a/src/optim/src/qnsearch.c b/src/optim/src/qnsearch.c index 168ba3162..bf9260df2 100644 --- a/src/optim/src/qnsearch.c +++ b/src/optim/src/qnsearch.c @@ -192,9 +192,9 @@ int qnsearch_step(qnsearch _q) return LIQUID_OK; } -float qnsearch_run(qnsearch _q, - unsigned int _max_iterations, - float _target_utility) +float qnsearch_execute(qnsearch _q, + unsigned int _max_iterations, + float _target_utility) { unsigned int i=0; do { diff --git a/src/optim/tests/qnsearch_autotest.c b/src/optim/tests/qnsearch_autotest.c new file mode 100644 index 000000000..449413a3e --- /dev/null +++ b/src/optim/tests/qnsearch_autotest.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "autotest/autotest.h" +#include "liquid.h" + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +void autotest_qnsearch_rosenbrock() +{ + float tol = 1e-2f; // error tolerance + unsigned int num_parameters = 6; // dimensionality of search (minimum 2) + unsigned int num_iterations = 4000; // number of iterations to run + + // initialize vector for optimization + float v_opt[num_parameters]; + unsigned int i; + for (i=0; i Date: Sat, 25 Feb 2023 18:54:50 -0500 Subject: [PATCH 060/186] gasearch: validating input before allocate memory in create() --- src/optim/src/gasearch.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/optim/src/gasearch.c b/src/optim/src/gasearch.c index e33d190bd..a846aabdf 100644 --- a/src/optim/src/gasearch.c +++ b/src/optim/src/gasearch.c @@ -63,9 +63,7 @@ gasearch gasearch_create_advanced(gasearch_utility _utility, unsigned int _population_size, float _mutation_rate) { - gasearch ga; - ga = (gasearch) malloc( sizeof(struct gasearch_s) ); - + // validate input if (_utility == NULL) return liquid_error_config("gasearch_create(), utility function cannot be NULL") if (_parent == NULL) @@ -77,7 +75,8 @@ gasearch gasearch_create_advanced(gasearch_utility _utility, if (_mutation_rate < 0.0f || _mutation_rate > 1.0f) return liquid_error_config("gasearch_create(), mutation rate must be in [0,1]"); - // initialize public values + // create object and initialize values + gasearch ga = (gasearch) malloc( sizeof(struct gasearch_s) ); ga->userdata = _userdata; ga->num_parameters = _parent->num_traits; ga->population_size = _population_size; From f4a0725e92376ea914fec9eea108a78b05019bd7 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 18:59:05 -0500 Subject: [PATCH 061/186] qnsearch: adding config() tests --- src/optim/src/qnsearch.c | 26 +++++++------------------- src/optim/tests/qnsearch_autotest.c | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/optim/src/qnsearch.c b/src/optim/src/qnsearch.c index bf9260df2..61bef0556 100644 --- a/src/optim/src/qnsearch.c +++ b/src/optim/src/qnsearch.c @@ -72,9 +72,14 @@ qnsearch qnsearch_create(void * _userdata, utility_function _u, int _minmax) { - qnsearch q = (qnsearch) malloc( sizeof(struct qnsearch_s) ); + // validate input + if (_u == NULL) + return liquid_error_config("qnsearch_create(), utility function cannot be NULL") + if (_num_parameters == 0) + return liquid_error_config("qnsearch_create(), number of parameters must be greater than zero"); - // initialize public values + // create object and initialize public values + qnsearch q = (qnsearch) malloc( sizeof(struct qnsearch_s) ); q->delta = 1e-6f; //_delta; q->gamma = 1e-3f; //_gamma; q->dgamma = 0.99f; @@ -146,7 +151,6 @@ int qnsearch_step(qnsearch _q) // compute normalized gradient vector qnsearch_compute_gradient(_q); - //qnsearch_normalize_gradient(_q); // TODO : perform line search to find optimal gamma @@ -231,22 +235,6 @@ int qnsearch_compute_gradient(qnsearch _q) return LIQUID_OK; } -// normalize gradient vector to unity -int qnsearch_normalize_gradient(qnsearch _q) -{ - // normalize gradient - float sig = 0.0f; - unsigned int i; - for (i=0; i<_q->num_parameters; i++) - sig += _q->gradient[i] * _q->gradient[i]; - - sig = 1.0f / sqrtf(sig/(float)(_q->num_parameters)); - - for (i=0; i<_q->num_parameters; i++) - _q->gradient[i] *= sig; - return LIQUID_OK; -} - // compute Hessian int qnsearch_compute_Hessian(qnsearch _q) { diff --git a/src/optim/tests/qnsearch_autotest.c b/src/optim/tests/qnsearch_autotest.c index 449413a3e..1a027c5be 100644 --- a/src/optim/tests/qnsearch_autotest.c +++ b/src/optim/tests/qnsearch_autotest.c @@ -59,5 +59,30 @@ void autotest_qnsearch_rosenbrock() // test value of utility (should be nearly 0) CONTEND_DELTA( liquid_rosenbrock(NULL, v_opt, num_parameters), 0.0f, tol ); + CONTEND_LESS_THAN( u_opt, tol ); +} + +// test configuration +void autotest_qnsearch_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping qnsearch config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + + // test configurations + float v[8] = {0,0,0,0,0,0,0,0}; + CONTEND_ISNULL(qnsearch_create(NULL, v, 0, liquid_rosenbrock, LIQUID_OPTIM_MINIMIZE)) // no parameters + CONTEND_ISNULL(qnsearch_create(NULL, v, 8, NULL, LIQUID_OPTIM_MINIMIZE)) // utility is null + + // create proper object and test configurations + qnsearch q = qnsearch_create(NULL, v, 8, liquid_rosenbrock, LIQUID_OPTIM_MINIMIZE); + CONTEND_EQUALITY(LIQUID_OK, qnsearch_print(q)) + + // destroy objects + qnsearch_destroy(q); } From b8d45a65ae8b895cb7f78a698b6103e64a0c0d04 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 20:54:04 -0500 Subject: [PATCH 062/186] qs1dsearch/autotest: adding config test --- src/optim/tests/qs1dsearch_autotest.c | 32 ++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/optim/tests/qs1dsearch_autotest.c b/src/optim/tests/qs1dsearch_autotest.c index e293788d8..bcfc41326 100644 --- a/src/optim/tests/qs1dsearch_autotest.c +++ b/src/optim/tests/qs1dsearch_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -105,3 +105,33 @@ void autotest_qs1dsearch_max_11() { test_qs1dsearch(qs1dsearch_umax, 0, -20, 15, void autotest_qs1dsearch_max_12() { test_qs1dsearch(qs1dsearch_umax, 0, -10, 15, 1, LIQUID_OPTIM_MAXIMIZE); } void autotest_qs1dsearch_max_13() { test_qs1dsearch(qs1dsearch_umax, 0, -.1, 15, 1, LIQUID_OPTIM_MAXIMIZE); } +// test configuration +void autotest_qs1dsearch_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping qs1dsearch config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(qs1dsearch_create(NULL, NULL, LIQUID_OPTIM_MAXIMIZE)) // utility is NULL + CONTEND_ISNULL(qs1dsearch_copy (NULL)) + + // create proper object and test configurations + float v_opt = 0; + qs1dsearch q = qs1dsearch_create(qs1dsearch_umax, &v_opt, LIQUID_OPTIM_MAXIMIZE); + CONTEND_EQUALITY(LIQUID_OK, qs1dsearch_print(q)) + + // test configurations + CONTEND_INEQUALITY(LIQUID_OK, qs1dsearch_step(q)) // object not yet initialized + qs1dsearch_init(q, 20); + + CONTEND_EQUALITY(LIQUID_OK, qs1dsearch_execute(q)) + CONTEND_EQUALITY( 0, qs1dsearch_get_num_steps(q)) + + // destroy objects + qs1dsearch_destroy(q); +} + From 26ab533aaf3bb20d4c996e17040b20d36e45182b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 21:03:42 -0500 Subject: [PATCH 063/186] optim/autotest: adding utility tests --- makefile.in | 1 + src/optim/tests/utility_autotest.c | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/optim/tests/utility_autotest.c diff --git a/makefile.in b/makefile.in index 555c90508..3fbf73e16 100644 --- a/makefile.in +++ b/makefile.in @@ -1016,6 +1016,7 @@ optim_autotests := \ src/optim/tests/gradsearch_autotest.c \ src/optim/tests/qnsearch_autotest.c \ src/optim/tests/qs1dsearch_autotest.c \ + src/optim/tests/utility_autotest.c \ # benchmarks optim_benchmarks := diff --git a/src/optim/tests/utility_autotest.c b/src/optim/tests/utility_autotest.c new file mode 100644 index 000000000..05ef49670 --- /dev/null +++ b/src/optim/tests/utility_autotest.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "autotest/autotest.h" +#include "liquid.h" + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +void autotest_optim_rosenbrock() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping rosenbrock config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + + // optimum + float v_ones[8] = {1,1,1,1,1,1,1,1}; + CONTEND_DELTA( liquid_rosenbrock(NULL, v_ones, 8), 0.0f, 1e-6f ) + CONTEND_DELTA( liquid_rosenbrock(NULL, v_ones, 1), 0.0f, 1e-6f ) + + // very far from optimum + float v_misc[8] = {0.3, 1.0, 4.5,-2.2, 6.7,-0.2, 1.1,-0.9,}; + CONTEND_GREATER_THAN( liquid_rosenbrock(NULL, v_misc, 8), 1000.0f ) + + // invalid configuration + CONTEND_EQUALITY( liquid_rosenbrock(NULL, v_misc, 0), 0.0f ) +} + From f31d0ba21b15f3e2ac49429764540734c538363d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 26 Feb 2023 08:21:52 -0500 Subject: [PATCH 064/186] fskframesync: adding (very) basic autotest * working copy until framing structure gets better definition * tests only that frame is recovered * should provide estimates of carrier offset, SNR, etc. * needs to support different payload lengths, coding rates, etc. --- examples/fskframesync_example.c | 5 - makefile.in | 1 + src/framing/src/fskframegen.c | 6 +- src/framing/src/fskframesync.c | 20 ++-- src/framing/tests/fskframesync_autotest.c | 113 ++++++++++++++++++++++ 5 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 src/framing/tests/fskframesync_autotest.c diff --git a/examples/fskframesync_example.c b/examples/fskframesync_example.c index c50c49b9f..52445cf0b 100644 --- a/examples/fskframesync_example.c +++ b/examples/fskframesync_example.c @@ -1,13 +1,8 @@ -// -// fskframesync_example.c -// // This example demonstrates the interfaces to the fskframegen and // fskframesync objects used to completely encapsulate data for // over-the-air transmission. // // SEE ALSO: flexframesync_example.c -// - #include #include #include diff --git a/makefile.in b/makefile.in index 3fbf73e16..71e5b16f1 100644 --- a/makefile.in +++ b/makefile.in @@ -694,6 +694,7 @@ framing_autotests := \ src/framing/tests/dsssframesync_autotest.c \ src/framing/tests/flexframesync_autotest.c \ src/framing/tests/framesync64_autotest.c \ + src/framing/tests/fskframesync_autotest.c \ src/framing/tests/gmskframe_autotest.c \ src/framing/tests/msource_autotest.c \ src/framing/tests/ofdmflexframe_autotest.c \ diff --git a/src/framing/src/fskframegen.c b/src/framing/src/fskframegen.c index 8aac7cf34..90557e53c 100644 --- a/src/framing/src/fskframegen.c +++ b/src/framing/src/fskframegen.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -321,7 +321,7 @@ int fskframegen_assemble(fskframegen _q, // encode payload symbols qpacketmodem_encode_syms(_q->payload_encoder, _payload, _q->payload_sym); -#if 1 +#if 0 printf("tx payload symbols (%u)\n", _q->payload_sym_len); unsigned int i; for (i=0; i<_q->payload_sym_len; i++) @@ -404,7 +404,7 @@ int fskframegen_encode_header(fskframegen _q, // run packet encoder, encoding into symbols qpacketmodem_encode_syms(_q->header_encoder, _q->header_dec, _q->header_sym); -#if 1 +#if 0 printf("tx header symbols (%u):\n", _q->header_sym_len); unsigned int i; for (i=0; i<_q->header_sym_len; i++) diff --git a/src/framing/src/fskframesync.c b/src/framing/src/fskframesync.c index 47ec916a5..f68c4cf4c 100644 --- a/src/framing/src/fskframesync.c +++ b/src/framing/src/fskframesync.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -456,14 +456,14 @@ int fskframesync_execute_detectframe(fskframesync _q, // NOTE: because this is a ratio of energies in frequency, we don't need // to take the absolute value here; only positive values should work if (rxy > 0.5f) { - printf("### fskframe detected! ###\n"); + //printf("### fskframe detected! ###\n"); _q->frame_detected = 1; } } else { // frame has already been detected; wait for signal to peak if (_q->rxy[1] > _q->rxy[2]) { - printf("signal peaked! %12.8f %12.8f %12.8f\n", - _q->rxy[0], _q->rxy[1], _q->rxy[2]); + //printf("signal peaked! %12.8f %12.8f %12.8f\n", + // _q->rxy[0], _q->rxy[1], _q->rxy[2]); // compute estimate, apply bias compensation float gamma = (_q->rxy[2] - _q->rxy[0]) / _q->rxy[1]; @@ -472,8 +472,8 @@ int fskframesync_execute_detectframe(fskframesync _q, float xf = fabsf(gamma); float tau_hat = copysignf(p2*xf*xf + p1*xf, gamma); int num_samples = round(tau_hat * _q->k); - printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", - gamma, tau_hat, num_samples); + //printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", + // gamma, tau_hat, num_samples); // TODO: set timer and filterbank index accordingly _q->timer = 2*_q->k; @@ -481,7 +481,7 @@ int fskframesync_execute_detectframe(fskframesync _q, // update state... _q->state = STATE_RXHEADER; } else { - printf("signal not yet peaked...\n"); + //printf("signal not yet peaked...\n"); } } return LIQUID_OK; @@ -524,7 +524,7 @@ int fskframesync_execute_rxheader(fskframesync _q, int header_valid = qpacketmodem_decode_syms(_q->header_decoder, _q->header_sym, _q->header_dec); -#if 1 +#if 0 printf("rx header symbols (%u):\n", _q->header_sym_len); unsigned int i; for (i=0; i<_q->header_sym_len; i++) @@ -612,7 +612,7 @@ int fskframesync_execute_rxpayload(fskframesync _q, // decode payload if appropriate if (_q->symbol_counter == _q->payload_sym_len) { -#if 1 +#if 0 printf("rx payload symbols (%u)\n", _q->payload_sym_len); unsigned int i; for (i=0; i<_q->payload_sym_len; i++) @@ -623,7 +623,7 @@ int fskframesync_execute_rxpayload(fskframesync _q, int payload_valid = qpacketmodem_decode_syms(_q->payload_decoder, _q->payload_sym, _q->payload_dec); - printf("payload: %s\n", payload_valid ? "valid" : "INVALID"); + //printf("payload: %s\n", payload_valid ? "valid" : "INVALID"); // invoke callback if (_q->callback != NULL) { diff --git a/src/framing/tests/fskframesync_autotest.c b/src/framing/tests/fskframesync_autotest.c new file mode 100644 index 000000000..9ffbef087 --- /dev/null +++ b/src/framing/tests/fskframesync_autotest.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "autotest/autotest.h" +#include "liquid.h" + +static int callback_fskframesync_autotest( + unsigned char * _header, + int _header_valid, + unsigned char * _payload, + unsigned int _payload_len, + int _payload_valid, + framesyncstats_s _stats, + void * _userdata) +{ + //printf("callback invoked, payload valid: %s\n", _payload_valid ? "yes" : "no"); + *((int*)(_userdata)) += _header_valid && _payload_valid ? 1 : 0; + return 0; +} + +// AUTOTEST : test simple recovery of frame in noise +void autotest_fskframesync() +{ + // options + float SNRdB = 20.0f; // signal-to-noise ratio + float noise_floor = -20.0f; // noise floor + //float dphi = 0.01f; // carrier frequency offset + //float theta = 0.0f; // carrier phase offset + //float dt = -0.2f; // fractional sample timing offset + + crc_scheme check = LIQUID_CRC_32; // data validity check + fec_scheme fec0 = LIQUID_FEC_NONE; // fec (inner) + fec_scheme fec1 = LIQUID_FEC_NONE; // fec (outer) + + // derived values + float nstd = powf(10.0f, noise_floor/20.0f); // noise std. dev. + float gamma = powf(10.0f, (SNRdB+noise_floor)/20.0f); // channel gain + + unsigned int i; + int frames_recovered = 0; + + // create objects + fskframegen fg = fskframegen_create(); + fskframesync fs = fskframesync_create(callback_fskframesync_autotest, + (void*)&frames_recovered); + + // assemble the frame + unsigned char header [ 8]; + unsigned char payload[200]; + for (i=0; i< 8; i++) header [i] = i; + for (i=0; i<200; i++) payload[i] = rand() & 0xff; + fskframegen_assemble(fg, header, payload, 200, check, fec0, fec1); + + // allocate memory for the frame samples + unsigned int buf_len = 256; + float complex buf_tx[buf_len]; // receive buffer + float complex buf_rx[buf_len]; // transmit buffer + + // try to receive the frame (operate in blocks) + int frame_complete = 0; + while (!frame_complete) + { + // generate frame samples + frame_complete = fskframegen_write_samples(fg, buf_tx, buf_len); + + // add noise, channel gain + for (i=0; i Date: Sun, 26 Feb 2023 10:34:53 -0500 Subject: [PATCH 065/186] qs1dsearch: resetting 'num_steps' counter --- src/optim/src/qs1dsearch.c | 1 + src/optim/tests/qs1dsearch_autotest.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/optim/src/qs1dsearch.c b/src/optim/src/qs1dsearch.c index 627fedcc1..35d051729 100644 --- a/src/optim/src/qs1dsearch.c +++ b/src/optim/src/qs1dsearch.c @@ -78,6 +78,7 @@ int qs1dsearch_print(qs1dsearch _q) int qs1dsearch_reset(qs1dsearch _q) { _q->init = 0; + _q->num_steps = 0; return LIQUID_OK; } diff --git a/src/optim/tests/qs1dsearch_autotest.c b/src/optim/tests/qs1dsearch_autotest.c index bcfc41326..495349f06 100644 --- a/src/optim/tests/qs1dsearch_autotest.c +++ b/src/optim/tests/qs1dsearch_autotest.c @@ -129,7 +129,13 @@ void autotest_qs1dsearch_config() qs1dsearch_init(q, 20); CONTEND_EQUALITY(LIQUID_OK, qs1dsearch_execute(q)) - CONTEND_EQUALITY( 0, qs1dsearch_get_num_steps(q)) + + // run a few steps + CONTEND_EQUALITY( 0, qs1dsearch_get_num_steps(q)) + qs1dsearch_step(q); + qs1dsearch_step(q); + qs1dsearch_step(q); + CONTEND_EQUALITY( 3, qs1dsearch_get_num_steps(q)) // destroy objects qs1dsearch_destroy(q); From fc7d4f606819d195f0439437d0f2a373e916df84 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 27 Feb 2023 17:49:30 -0500 Subject: [PATCH 066/186] ofdmflexframesync/autotest: testing configurations --- src/framing/tests/ofdmflexframe_autotest.c | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/framing/tests/ofdmflexframe_autotest.c b/src/framing/tests/ofdmflexframe_autotest.c index eabde9d73..cf24e6bac 100644 --- a/src/framing/tests/ofdmflexframe_autotest.c +++ b/src/framing/tests/ofdmflexframe_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -90,3 +90,27 @@ void autotest_ofdmflexframe_07() { testbench_ofdmflexframe(1200, 40, 20, 1, L void autotest_ofdmflexframe_08() { testbench_ofdmflexframe(1200, 0, 0, 800, LIQUID_MODEM_QPSK); } void autotest_ofdmflexframe_09() { testbench_ofdmflexframe(1200, 40, 20, 8217, LIQUID_MODEM_QPSK); } +void autotest_ofdmflexframe_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmflexframe config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmflexframesync_copy(NULL)); + CONTEND_ISNULL(ofdmflexframesync_create( 0, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframesync_create( 7, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframesync_create(65, 16, 4, NULL, NULL, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmflexframesync_create(64, 66, 4, NULL, NULL, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmflexframesync q = ofdmflexframesync_create(64, 16, 4, NULL, NULL, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmflexframesync_print(q)) + + ofdmflexframesync_destroy(q); +} + From 5d30bd07f1499ab39a6deccb583fed35b862b58a Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 27 Feb 2023 17:53:57 -0500 Subject: [PATCH 067/186] ofdmflexframegen/autotest: testing configurations --- src/framing/src/ofdmflexframegen.c | 8 ++++--- src/framing/src/ofdmflexframesync.c | 2 +- src/framing/tests/ofdmflexframe_autotest.c | 28 ++++++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/framing/src/ofdmflexframegen.c b/src/framing/src/ofdmflexframegen.c index 52a7a7fef..90191a814 100644 --- a/src/framing/src/ofdmflexframegen.c +++ b/src/framing/src/ofdmflexframegen.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2021 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -157,10 +157,12 @@ ofdmflexframegen ofdmflexframegen_create(unsigned int _M, ofdmflexframegenprops_s * _fgprops) { // validate input - if (_M < 2) - return liquid_error_config("ofdmflexframegen_create(), number of subcarriers must be at least 2"); + if (_M < 8) + return liquid_error_config("ofdmflexframegen_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmflexframegen_create(), number of subcarriers must be even"); + if (_cp_len > _M) + return liquid_error_config("ofdmflexframegen_create(), cyclic prefix length cannot exceed number of subcarriers"); ofdmflexframegen q = (ofdmflexframegen) malloc(sizeof(struct ofdmflexframegen_s)); q->M = _M; // number of subcarriers diff --git a/src/framing/src/ofdmflexframesync.c b/src/framing/src/ofdmflexframesync.c index a18165eed..44f9fcedc 100644 --- a/src/framing/src/ofdmflexframesync.c +++ b/src/framing/src/ofdmflexframesync.c @@ -152,7 +152,7 @@ ofdmflexframesync ofdmflexframesync_create(unsigned int _M, // validate input if (_M < 8) - return liquid_error_config("ofdmflexframesync_create(), less than 8 subcarriers"); + return liquid_error_config("ofdmflexframesync_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmflexframesync_create(), number of subcarriers must be even"); if (_cp_len > _M) diff --git a/src/framing/tests/ofdmflexframe_autotest.c b/src/framing/tests/ofdmflexframe_autotest.c index cf24e6bac..814ab0b29 100644 --- a/src/framing/tests/ofdmflexframe_autotest.c +++ b/src/framing/tests/ofdmflexframe_autotest.c @@ -90,10 +90,34 @@ void autotest_ofdmflexframe_07() { testbench_ofdmflexframe(1200, 40, 20, 1, L void autotest_ofdmflexframe_08() { testbench_ofdmflexframe(1200, 0, 0, 800, LIQUID_MODEM_QPSK); } void autotest_ofdmflexframe_09() { testbench_ofdmflexframe(1200, 40, 20, 8217, LIQUID_MODEM_QPSK); } -void autotest_ofdmflexframe_config() +void autotest_ofdmflexframegen_config() { #if LIQUID_STRICT_EXIT - AUTOTEST_WARN("skipping ofdmflexframe config test with strict exit enabled\n"); + AUTOTEST_WARN("skipping ofdmflexframegen config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmflexframegen_copy(NULL)); + CONTEND_ISNULL(ofdmflexframegen_create( 0, 16, 4, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframegen_create( 7, 16, 4, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframegen_create(65, 16, 4, NULL, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmflexframegen_create(64, 66, 4, NULL, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmflexframegen q = ofdmflexframegen_create(64, 16, 4, NULL, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmflexframegen_print(q)) + + ofdmflexframegen_destroy(q); +} + +void autotest_ofdmflexframesync_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmflexframesync config test with strict exit enabled\n"); return; #endif #if !LIQUID_SUPPRESS_ERROR_OUTPUT From ceec622d83bafbfa6ad028dc1a7992c0bf68f786 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 27 Feb 2023 18:27:38 -0500 Subject: [PATCH 068/186] ofdmframe/autotest: adding configuration tests --- makefile.in | 2 +- ...mesync_autotest.c => ofdmframe_autotest.c} | 53 ++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) rename src/multichannel/tests/{ofdmframesync_autotest.c => ofdmframe_autotest.c} (69%) diff --git a/makefile.in b/makefile.in index 71e5b16f1..7e415d7c1 100644 --- a/makefile.in +++ b/makefile.in @@ -949,7 +949,7 @@ multichannel_autotests := \ src/multichannel/tests/firpfbch2_crcf_autotest.c \ src/multichannel/tests/firpfbch_crcf_synthesizer_autotest.c \ src/multichannel/tests/firpfbch_crcf_analyzer_autotest.c \ - src/multichannel/tests/ofdmframesync_autotest.c \ + src/multichannel/tests/ofdmframe_autotest.c \ # benchmarks multichannel_benchmarks := \ diff --git a/src/multichannel/tests/ofdmframesync_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c similarity index 69% rename from src/multichannel/tests/ofdmframesync_autotest.c rename to src/multichannel/tests/ofdmframe_autotest.c index 4d43d72a0..b949a8ece 100644 --- a/src/multichannel/tests/ofdmframesync_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2019 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,8 @@ int ofdmframesync_autotest_callback(float complex * _X, unsigned int _M, void * _userdata) { - printf("******** callback invoked!\n"); + if (liquid_autotest_verbose) + printf("******** callback invoked!\n"); // type cast _userdata as complex float array float complex * X = (float complex *)_userdata; @@ -143,3 +144,51 @@ void autotest_ofdmframesync_acquire_n128() { ofdmframesync_acquire_test(128, 16 void autotest_ofdmframesync_acquire_n256() { ofdmframesync_acquire_test(256, 32, 0); } void autotest_ofdmframesync_acquire_n512() { ofdmframesync_acquire_test(512, 64, 0); } +void autotest_ofdmframegen_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmframegen config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmframegen_copy(NULL)); + CONTEND_ISNULL(ofdmframegen_create( 0, 16, 4, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframegen_create( 7, 16, 4, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframegen_create(65, 16, 4, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmframegen_create(64, 66, 4, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmframegen q = ofdmframegen_create(64, 16, 4, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmframegen_print(q)) + + ofdmframegen_destroy(q); +} + +void autotest_ofdmframesync_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmframesync config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmframesync_copy(NULL)); + CONTEND_ISNULL(ofdmframesync_create( 0, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframesync_create( 7, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframesync_create(65, 16, 4, NULL, NULL, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmframesync_create(64, 66, 4, NULL, NULL, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmframesync q = ofdmframesync_create(64, 16, 4, NULL, NULL, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmframesync_print(q)) + + ofdmframesync_destroy(q); +} + From 32275d2817d156ea7038542047fbff95340d1e04 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Tue, 28 Feb 2023 17:42:24 -0500 Subject: [PATCH 069/186] ofdmframe: moving subcarrier verification check inside common function --- src/multichannel/src/ofdmframe.common.c | 8 ++++++++ src/multichannel/src/ofdmframegen.c | 10 ++-------- src/multichannel/src/ofdmframesync.c | 12 ++++-------- src/multichannel/tests/ofdmframe_autotest.c | 2 ++ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/multichannel/src/ofdmframe.common.c b/src/multichannel/src/ofdmframe.common.c index cc30554c2..2d612eb15 100644 --- a/src/multichannel/src/ofdmframe.common.c +++ b/src/multichannel/src/ofdmframe.common.c @@ -294,6 +294,14 @@ int ofdmframe_validate_sctype(unsigned char * _p, } } + // validate subcarrier allocation + if ( (M_pilot + M_data) == 0) + return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least one enabled subcarrier"); + if (M_data == 0) + return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least one data subcarrier"); + if (M_pilot < 2) + return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least two pilot subcarriers"); + // set outputs *_M_null = M_null; *_M_pilot = M_pilot; diff --git a/src/multichannel/src/ofdmframegen.c b/src/multichannel/src/ofdmframegen.c index 8553ef538..e45719898 100644 --- a/src/multichannel/src/ofdmframegen.c +++ b/src/multichannel/src/ofdmframegen.c @@ -84,8 +84,8 @@ ofdmframegen ofdmframegen_create(unsigned int _M, unsigned char * _p) { // validate input - if (_M < 2) - return liquid_error_config("ofdmframegen_create(), number of subcarriers must be at least 2"); + if (_M < 8) + return liquid_error_config("ofdmframegen_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmframegen_create(), number of subcarriers must be even"); if (_cp_len > _M) @@ -111,12 +111,6 @@ ofdmframegen ofdmframegen_create(unsigned int _M, // validate and count subcarrier allocation if (ofdmframe_validate_sctype(q->p, q->M, &q->M_null, &q->M_pilot, &q->M_data)) return liquid_error_config("ofdmframegen_create(), invalid subcarrier allocation"); - if ( (q->M_pilot + q->M_data) == 0) - return liquid_error_config("ofdmframegen_create(), must have at least one enabled subcarrier"); - if (q->M_data == 0) - return liquid_error_config("ofdmframegen_create(), must have at least one data subcarriers"); - if (q->M_pilot < 2) - return liquid_error_config("ofdmframegen_create(), must have at least two pilot subcarriers"); unsigned int i; diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 0e7ce5448..f69e35e6e 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -149,11 +149,13 @@ ofdmframesync ofdmframesync_create(unsigned int _M, // validate input if (_M < 8) - return liquid_error_config("ofdmframesync_create(), less than 8 subcarriers"); + return liquid_error_config("ofdmframesync_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmframesync_create(), number of subcarriers must be even"); if (_cp_len > _M) return liquid_error_config("ofdmframesync_create(), cyclic prefix length cannot exceed number of subcarriers"); + if (_taper_len > _cp_len) + return liquid_error_config("ofdmframesync_create(), taper length cannot exceed cyclic prefix"); q->M = _M; q->cp_len = _cp_len; @@ -172,12 +174,6 @@ ofdmframesync ofdmframesync_create(unsigned int _M, // validate and count subcarrier allocation if (ofdmframe_validate_sctype(q->p, q->M, &q->M_null, &q->M_pilot, &q->M_data)) return liquid_error_config("ofdmframesync_create(), invalid subcarrier allocation"); - if ( (q->M_pilot + q->M_data) == 0) - return liquid_error_config("ofdmframesync_create(), must have at least one enabled subcarrier"); - if (q->M_data == 0) - return liquid_error_config("ofdmframesync_create(), must have at least one data subcarriers"); - if (q->M_pilot < 2) - return liquid_error_config("ofdmframesync_create(), must have at least two pilot subcarriers"); // create transform object q->X = (float complex*) FFT_MALLOC((q->M)*sizeof(float complex)); diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index b949a8ece..1b1643888 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -159,6 +159,7 @@ void autotest_ofdmframegen_config() CONTEND_ISNULL(ofdmframegen_create( 7, 16, 4, NULL)) // too few subcarriers CONTEND_ISNULL(ofdmframegen_create(65, 16, 4, NULL)) // odd-length subcarriers CONTEND_ISNULL(ofdmframegen_create(64, 66, 4, NULL)) // cyclic prefix length too large + CONTEND_ISNULL(ofdmframegen_create(64, 16,24, NULL)) // taper length greater than cyclic prefix // create proper object and test configurations ofdmframegen q = ofdmframegen_create(64, 16, 4, NULL); @@ -183,6 +184,7 @@ void autotest_ofdmframesync_config() CONTEND_ISNULL(ofdmframesync_create( 7, 16, 4, NULL, NULL, NULL)) // too few subcarriers CONTEND_ISNULL(ofdmframesync_create(65, 16, 4, NULL, NULL, NULL)) // odd-length subcarriers CONTEND_ISNULL(ofdmframesync_create(64, 66, 4, NULL, NULL, NULL)) // cyclic prefix length too large + CONTEND_ISNULL(ofdmframesync_create(64, 16,24, NULL, NULL, NULL)) // taper length greater than cyclic prefix // create proper object and test configurations ofdmframesync q = ofdmframesync_create(64, 16, 4, NULL, NULL, NULL); From aee1ebf65fe1f5431bd7eb48a49a93ef0beb2617 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 18:08:47 -0500 Subject: [PATCH 070/186] ofdmframesync: using internal method to unwrap phase --- src/multichannel/src/ofdmframesync.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index f69e35e6e..2a482cd1b 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -951,12 +951,7 @@ int ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, return liquid_error(LIQUID_EINT,"ofdmframesync_estimate_eqgain_poly(), pilot subcarrier mismatch"); // try to unwrap phase - for (i=1; i M_PI) - y_arg[i] -= 2*M_PI; - while ((y_arg[i] - y_arg[i-1]) < -M_PI) - y_arg[i] += 2*M_PI; - } + liquid_unwrap_phase(y_arg, N); // fit to polynomial polyf_fit(x_freq, y_abs, N, p_abs, _order+1); @@ -1032,12 +1027,7 @@ int ofdmframesync_rxsymbol(ofdmframesync _q) return liquid_error(LIQUID_EINT,"ofdmframesync_estimate_eqgain_poly(), pilot subcarrier mismatch"); // try to unwrap phase - for (i=1; i<_q->M_pilot; i++) { - while ((y_phase[i] - y_phase[i-1]) > M_PI) - y_phase[i] -= 2*M_PI; - while ((y_phase[i] - y_phase[i-1]) < -M_PI) - y_phase[i] += 2*M_PI; - } + liquid_unwrap_phase(y_phase, _q->M_pilot); // fit phase to 1st-order polynomial (2 coefficients) polyf_fit(x_phase, y_phase, _q->M_pilot, p_phase, 2); From d75799c62dc06e919c9445ddb04e2a4913c1a9c0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 18:12:27 -0500 Subject: [PATCH 071/186] ofdmframesync: adding additional configuration tests --- src/multichannel/src/ofdmframesync.c | 3 +-- src/multichannel/tests/ofdmframe_autotest.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 2a482cd1b..3fcf1d953 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -413,8 +413,7 @@ float ofdmframesync_get_cfo(ofdmframesync _q) // set receiver carrier frequency offset estimate int ofdmframesync_set_cfo(ofdmframesync _q, float _cfo) { - nco_crcf_set_frequency(_q->nco_rx, _cfo); - return LIQUID_OK; + return nco_crcf_set_frequency(_q->nco_rx, _cfo); } // diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index 1b1643888..2ca3ab93c 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -190,6 +190,8 @@ void autotest_ofdmframesync_config() ofdmframesync q = ofdmframesync_create(64, 16, 4, NULL, NULL, NULL); CONTEND_EQUALITY(LIQUID_OK, ofdmframesync_print(q)) + CONTEND_EQUALITY( 0, ofdmframesync_is_frame_open(q)) + CONTEND_EQUALITY(LIQUID_OK, ofdmframesync_set_cfo(q,0.0f)) ofdmframesync_destroy(q); } From 2ebf1e034d6439957bbc8e633cc4626049a76db9 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 18:17:09 -0500 Subject: [PATCH 072/186] ofdmframesync: moving internal funciton declarations from header --- include/liquid.internal.h | 45 ------------------------- src/multichannel/src/ofdmframesync.c | 49 +++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/include/liquid.internal.h b/include/liquid.internal.h index e8e2e0d90..e5f32f0a8 100644 --- a/include/liquid.internal.h +++ b/include/liquid.internal.h @@ -1486,51 +1486,6 @@ int ofdmframe_init_S1(unsigned char * _p, int ofdmframegen_gensymbol(ofdmframegen _q, float complex * _buffer); -int ofdmframesync_cpcorrelate(ofdmframesync _q); -int ofdmframesync_findrxypeak(ofdmframesync _q); -int ofdmframesync_rxpayload(ofdmframesync _q); - -int ofdmframesync_execute_seekplcp(ofdmframesync _q); -int ofdmframesync_execute_S0a(ofdmframesync _q); -int ofdmframesync_execute_S0b(ofdmframesync _q); -int ofdmframesync_execute_S1( ofdmframesync _q); -int ofdmframesync_execute_rxsymbols(ofdmframesync _q); - -int ofdmframesync_S0_metrics(ofdmframesync _q, - float complex * _G, - float complex * _s_hat); - -// estimate short sequence gain -// _q : ofdmframesync object -// _x : input array (time) -// _G : output gain (freq) -int ofdmframesync_estimate_gain_S0(ofdmframesync _q, - float complex * _x, - float complex * _G); - -// estimate long sequence gain -// _q : ofdmframesync object -// _x : input array (time) -// _G : output gain (freq) -int ofdmframesync_estimate_gain_S1(ofdmframesync _q, - float complex * _x, - float complex * _G); - -// estimate complex equalizer gain from G0 and G1 -// _q : ofdmframesync object -// _ntaps : number of time-domain taps for smoothing -int ofdmframesync_estimate_eqgain(ofdmframesync _q, - unsigned int _ntaps); - -// estimate complex equalizer gain from G0 and G1 using polynomial fit -// _q : ofdmframesync object -// _order : polynomial order -int ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, - unsigned int _order); - -// recover symbol, correcting for gain, pilot phase, etc. -int ofdmframesync_rxsymbol(ofdmframesync _q); - // // MODULE : nco (numerically-controlled oscillator) // diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 3fcf1d953..a1e2f5ecf 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -20,11 +20,7 @@ * THE SOFTWARE. */ -// -// ofdmframesync.c -// // OFDM frame synchronizer -// #include #include @@ -41,6 +37,49 @@ #define OFDMFRAMESYNC_ENABLE_SQUELCH 0 +// forward declaration of internal methods + +int ofdmframesync_execute_seekplcp(ofdmframesync _q); +int ofdmframesync_execute_S0a(ofdmframesync _q); +int ofdmframesync_execute_S0b(ofdmframesync _q); +int ofdmframesync_execute_S1( ofdmframesync _q); +int ofdmframesync_execute_rxsymbols(ofdmframesync _q); + +int ofdmframesync_S0_metrics(ofdmframesync _q, + float complex * _G, + float complex * _s_hat); + +// estimate short sequence gain +// _q : ofdmframesync object +// _x : input array (time) +// _G : output gain (freq) +int ofdmframesync_estimate_gain_S0(ofdmframesync _q, + float complex * _x, + float complex * _G); + +// estimate long sequence gain +// _q : ofdmframesync object +// _x : input array (time) +// _G : output gain (freq) +int ofdmframesync_estimate_gain_S1(ofdmframesync _q, + float complex * _x, + float complex * _G); + +// estimate complex equalizer gain from G0 and G1 +// _q : ofdmframesync object +// _ntaps : number of time-domain taps for smoothing +int ofdmframesync_estimate_eqgain(ofdmframesync _q, + unsigned int _ntaps); + +// estimate complex equalizer gain from G0 and G1 using polynomial fit +// _q : ofdmframesync object +// _order : polynomial order +int ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, + unsigned int _order); + +// recover symbol, correcting for gain, pilot phase, etc. +int ofdmframesync_rxsymbol(ofdmframesync _q); + struct ofdmframesync_s { unsigned int M; // number of subcarriers unsigned int M2; // number of subcarriers (divided by 2) @@ -752,7 +791,7 @@ int ofdmframesync_execute_rxsymbols(ofdmframesync _q) } // compute S0 metrics -int ofdmframesync_S0_metrics(ofdmframesync _q, +int ofdmframesync_S0_metrics(ofdmframesync _q, float complex * _G, float complex * _s_hat) { From a7b1372f7c500b53bd2e766790d3302efcc3c6a5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 20:40:51 -0500 Subject: [PATCH 073/186] ofdmframesync: disabling debugging (preprocessor) --- src/multichannel/src/ofdmframesync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index a1e2f5ecf..bd88ff947 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -30,7 +30,7 @@ #include "liquid.internal.h" -#define DEBUG_OFDMFRAMESYNC 1 +#define DEBUG_OFDMFRAMESYNC 0 #define DEBUG_OFDMFRAMESYNC_PRINT 0 #define DEBUG_OFDMFRAMESYNC_FILENAME "ofdmframesync_internal_debug.m" #define DEBUG_OFDMFRAMESYNC_BUFFER_LEN (2048) From 50b51a0ec570e3b85ae0da40c0f5cfbc8fbab547 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 20:46:53 -0500 Subject: [PATCH 074/186] ofdmframesync: block-commenting unused method for estimating equalizer gain --- src/multichannel/src/ofdmframesync.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index bd88ff947..8a05e20b1 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -68,8 +68,7 @@ int ofdmframesync_estimate_gain_S1(ofdmframesync _q, // estimate complex equalizer gain from G0 and G1 // _q : ofdmframesync object // _ntaps : number of time-domain taps for smoothing -int ofdmframesync_estimate_eqgain(ofdmframesync _q, - unsigned int _ntaps); +//int ofdmframesync_estimate_eqgain(ofdmframesync _q, unsigned int _ntaps); // estimate complex equalizer gain from G0 and G1 using polynomial fit // _q : ofdmframesync object @@ -879,6 +878,7 @@ int ofdmframesync_estimate_gain_S1(ofdmframesync _q, return LIQUID_OK; } +#if 0 // estimate complex equalizer gain from G0 and G1 // _q : ofdmframesync object // _ntaps : number of time-domain taps for smoothing @@ -939,6 +939,7 @@ int ofdmframesync_estimate_eqgain(ofdmframesync _q, } return LIQUID_OK; } +#endif // estimate complex equalizer gain from G0 and G1 using polynomial fit // _q : ofdmframesync object From b9f927b1afd818140bb71d043707918b90a86d9d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 20:51:15 -0500 Subject: [PATCH 075/186] ofdmframegen: moving internal method declarations from header --- include/liquid.internal.h | 6 +----- src/multichannel/src/ofdmframegen.c | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/liquid.internal.h b/include/liquid.internal.h index e5f32f0a8..d38648460 100644 --- a/include/liquid.internal.h +++ b/include/liquid.internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -1482,10 +1482,6 @@ int ofdmframe_init_S1(unsigned char * _p, float complex * _s1, unsigned int * _M_S1); -// generate symbol (add cyclic prefix/postfix, overlap) -int ofdmframegen_gensymbol(ofdmframegen _q, - float complex * _buffer); - // // MODULE : nco (numerically-controlled oscillator) // diff --git a/src/multichannel/src/ofdmframegen.c b/src/multichannel/src/ofdmframegen.c index e45719898..150cb8532 100644 --- a/src/multichannel/src/ofdmframegen.c +++ b/src/multichannel/src/ofdmframegen.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,10 @@ #define DEBUG_OFDMFRAMEGEN 1 +// generate symbol (add cyclic prefix/postfix, overlap) +int ofdmframegen_gensymbol(ofdmframegen _q, + float complex * _buffer); + struct ofdmframegen_s { unsigned int M; // number of subcarriers unsigned int cp_len; // cyclic prefix length From 3acaf727fb6aade3fbaef834eb8ef88c640931a8 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 21:03:24 -0500 Subject: [PATCH 076/186] firpfbch2/autotest: adding configuration testing --- .../tests/firpfbch2_crcf_autotest.c | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/multichannel/tests/firpfbch2_crcf_autotest.c b/src/multichannel/tests/firpfbch2_crcf_autotest.c index c00a728b5..9c5f135ec 100644 --- a/src/multichannel/tests/firpfbch2_crcf_autotest.c +++ b/src/multichannel/tests/firpfbch2_crcf_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -97,7 +97,6 @@ void autotest_firpfbch2_crcf_n16() { firpfbch2_crcf_runtest( 16, 5, 60.0f); } void autotest_firpfbch2_crcf_n32() { firpfbch2_crcf_runtest( 32, 5, 60.0f); } void autotest_firpfbch2_crcf_n64() { firpfbch2_crcf_runtest( 64, 5, 60.0f); } - void autotest_firpfbch2_crcf_copy() { // create channelizer @@ -140,3 +139,36 @@ void autotest_firpfbch2_crcf_copy() firpfbch2_crcf_destroy(q_copy); } +void autotest_firpfbch2_crcf_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping firpfbch2_crcf config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(firpfbch2_crcf_create( 77, 76, 12, NULL)) // invalid type + CONTEND_ISNULL(firpfbch2_crcf_create(LIQUID_ANALYZER, 0, 12, NULL)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create(LIQUID_ANALYZER, 17, 12, NULL)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create(LIQUID_ANALYZER, 76, 0, NULL)) // invalid filter semi-length + + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser( 77, 76, 12, 60.0f)) // invalid type + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 0, 12, 60.0f)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 17, 12, 60.0f)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 76, 0, 60.0f)) // invalid filter semi-length + + CONTEND_ISNULL(firpfbch2_crcf_copy(NULL)) + + // create proper object and test configurations + firpfbch2_crcf q = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 76, 12, 60.0f); + + CONTEND_EQUALITY(LIQUID_OK, firpfbch2_crcf_print(q)) + CONTEND_EQUALITY(LIQUID_ANALYZER, firpfbch2_crcf_get_type(q)) + CONTEND_EQUALITY( 76, firpfbch2_crcf_get_M(q)) + CONTEND_EQUALITY( 12, firpfbch2_crcf_get_m(q)) + + firpfbch2_crcf_destroy(q); +} + From 77e3e634ce8773ea6c492d7db83a13c4f135bffd Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Fri, 3 Mar 2023 08:21:20 -0500 Subject: [PATCH 077/186] firpfbch: adding configuration testing --- makefile.in | 1 + .../tests/firpfbch_crcf_autotest.c | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/multichannel/tests/firpfbch_crcf_autotest.c diff --git a/makefile.in b/makefile.in index 7e415d7c1..3c0246d9b 100644 --- a/makefile.in +++ b/makefile.in @@ -949,6 +949,7 @@ multichannel_autotests := \ src/multichannel/tests/firpfbch2_crcf_autotest.c \ src/multichannel/tests/firpfbch_crcf_synthesizer_autotest.c \ src/multichannel/tests/firpfbch_crcf_analyzer_autotest.c \ + src/multichannel/tests/firpfbch_crcf_autotest.c \ src/multichannel/tests/ofdmframe_autotest.c \ # benchmarks diff --git a/src/multichannel/tests/firpfbch_crcf_autotest.c b/src/multichannel/tests/firpfbch_crcf_autotest.c new file mode 100644 index 000000000..fb9deb967 --- /dev/null +++ b/src/multichannel/tests/firpfbch_crcf_autotest.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "autotest/autotest.h" +#include "liquid.h" + +void autotest_firpfbch_crcf_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping firpfbch_crcf config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(firpfbch_crcf_create( 77, 76, 12, NULL)) // invalid type + CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 0, 12, NULL)) // invalid number of channels + CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 76, 0, NULL)) // invalid filter semi-length + + //CONTEND_ISNULL(firpfbch_crcf_copy(NULL)) + + // create proper object and test configurations + firpfbch_crcf q = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, 76, 12, 60.0f); + + CONTEND_EQUALITY(LIQUID_OK, firpfbch_crcf_print(q)) + + firpfbch_crcf_destroy(q); +} + From 7d31ed82007528da000d528e2fa85deccdf33dd5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 07:48:33 -0500 Subject: [PATCH 078/186] firpfbch/autotest: adding more configuration tests --- src/multichannel/src/firpfbch.proto.c | 14 ++++++-------- src/multichannel/tests/firpfbch_crcf_autotest.c | 8 ++++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/multichannel/src/firpfbch.proto.c b/src/multichannel/src/firpfbch.proto.c index 28826a2b5..1f147e795 100644 --- a/src/multichannel/src/firpfbch.proto.c +++ b/src/multichannel/src/firpfbch.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,7 @@ * THE SOFTWARE. */ -// -// firpfbch.c -// -// finite impulse response polyphase filterbank channelizer -// +// firpfbch : finite impulse response polyphase filterbank channelizer #include #include @@ -77,7 +73,7 @@ FIRPFBCH() FIRPFBCH(_create)(int _type, { // validate input if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) - return liquid_error_config("firpfbch_%s_create(), invalid type %d", EXTENSION_FULL, _type); + return liquid_error_config("firpfbch_%s_create(), invalid type: %d", EXTENSION_FULL, _type); if (_M == 0) return liquid_error_config("firpfbch_%s_create(), number of channels must be greater than 0", EXTENSION_FULL); if (_p == 0) @@ -148,6 +144,8 @@ FIRPFBCH() FIRPFBCH(_create_kaiser)(int _type, float _as) { // validate input + if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) + return liquid_error_config("firpfbch_%s_create_kaiser(), invalid type: %d", EXTENSION_FULL, _type); if (_M == 0) return liquid_error_config("firpfbch_%s_create_kaiser(), number of channels must be greater than 0", EXTENSION_FULL); if (_m == 0) @@ -190,7 +188,7 @@ FIRPFBCH() FIRPFBCH(_create_rnyquist)(int _type, { // validate input if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) - return liquid_error_config("firpfbch_%s_create_rnyquist(), invalid type %d", EXTENSION_FULL, _type); + return liquid_error_config("firpfbch_%s_create_rnyquist(), invalid type: %d", EXTENSION_FULL, _type); if (_M == 0) return liquid_error_config("firpfbch_%s_create_rnyquist(), number of channels must be greater than 0", EXTENSION_FULL); if (_m == 0) diff --git a/src/multichannel/tests/firpfbch_crcf_autotest.c b/src/multichannel/tests/firpfbch_crcf_autotest.c index fb9deb967..55204b882 100644 --- a/src/multichannel/tests/firpfbch_crcf_autotest.c +++ b/src/multichannel/tests/firpfbch_crcf_autotest.c @@ -38,6 +38,14 @@ void autotest_firpfbch_crcf_config() CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 0, 12, NULL)) // invalid number of channels CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 76, 0, NULL)) // invalid filter semi-length + CONTEND_ISNULL(firpfbch_crcf_create_kaiser( 77, 76, 12, 60.0f)) // invalid type + CONTEND_ISNULL(firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, 0, 12, 60.0f)) // invalid number of channels + CONTEND_ISNULL(firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, 76, 0, 60.0f)) // invalid filter semi-length + + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist( 77, 76, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid type + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 0, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid number of channels + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 0, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid filter semi-length + //CONTEND_ISNULL(firpfbch_crcf_copy(NULL)) // create proper object and test configurations From 9328b18e2a28f8e9548ff71aadf78cca9de64bc7 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 08:10:57 -0500 Subject: [PATCH 079/186] firpfbch: using prototype filter generation for create_rnyquist() --- src/multichannel/src/firpfbch.proto.c | 22 ++----------------- .../tests/firpfbch_crcf_autotest.c | 2 ++ 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/multichannel/src/firpfbch.proto.c b/src/multichannel/src/firpfbch.proto.c index 1f147e795..0683c3a1a 100644 --- a/src/multichannel/src/firpfbch.proto.c +++ b/src/multichannel/src/firpfbch.proto.c @@ -197,26 +197,8 @@ FIRPFBCH() FIRPFBCH(_create_rnyquist)(int _type, // design filter based on requested prototype unsigned int h_len = 2*_M*_m + 1; float h[h_len]; - switch (_ftype) { - case LIQUID_FIRFILT_ARKAISER: - // root-Nyquist Kaiser (approximate optimum) - liquid_firdes_arkaiser(_M, _m, _beta, 0.0f, h); - break; - case LIQUID_FIRFILT_RKAISER: - // root-Nyquist Kaiser (true optimum) - liquid_firdes_rkaiser(_M, _m, _beta, 0.0f, h); - break; - case LIQUID_FIRFILT_RRC: - // root raised-cosine - liquid_firdes_rrcos(_M, _m, _beta, 0.0f, h); - break; - case LIQUID_FIRFILT_hM3: - // harris-Moerder-3 filter - liquid_firdes_hM3(_M, _m, _beta, 0.0f, h); - break; - default: - return liquid_error_config("firpfbch_%s_create_rnyquist(), unknown/invalid prototype (%d)", EXTENSION_FULL, _ftype); - } + if (liquid_firdes_prototype(_ftype, _M, _m, _beta, 0.0f, h) != LIQUID_OK) + return liquid_error_config("firpfbch_%s_create_rnyquist(), invalid filter type/configuration", EXTENSION_FULL); // copy coefficients to type-specfic array, reversing order if // channelizer is an analyzer, matched filter: g(-t) diff --git a/src/multichannel/tests/firpfbch_crcf_autotest.c b/src/multichannel/tests/firpfbch_crcf_autotest.c index 55204b882..7ada5fd56 100644 --- a/src/multichannel/tests/firpfbch_crcf_autotest.c +++ b/src/multichannel/tests/firpfbch_crcf_autotest.c @@ -45,6 +45,8 @@ void autotest_firpfbch_crcf_config() CONTEND_ISNULL(firpfbch_crcf_create_rnyquist( 77, 76, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid type CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 0, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid number of channels CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 0, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid filter semi-length + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 12, 77.f, LIQUID_FIRFILT_ARKAISER)) // invalid filter excess bandwidth + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 12, 0.2f, LIQUID_FIRFILT_UNKNOWN )) // invalid filter type //CONTEND_ISNULL(firpfbch_crcf_copy(NULL)) From 7a624d6dd24f32bb658b721b4a90586c1024f489 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 08:38:20 -0500 Subject: [PATCH 080/186] ofdmframe/autotest: including configuration tests for common functions --- src/multichannel/src/ofdmframe.common.c | 8 +++--- src/multichannel/tests/ofdmframe_autotest.c | 32 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/multichannel/src/ofdmframe.common.c b/src/multichannel/src/ofdmframe.common.c index 2d612eb15..ab7e4d5b1 100644 --- a/src/multichannel/src/ofdmframe.common.c +++ b/src/multichannel/src/ofdmframe.common.c @@ -302,10 +302,10 @@ int ofdmframe_validate_sctype(unsigned char * _p, if (M_pilot < 2) return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least two pilot subcarriers"); - // set outputs - *_M_null = M_null; - *_M_pilot = M_pilot; - *_M_data = M_data; + // set outputs if requested + if (_M_null != NULL) *_M_null = M_null; + if (_M_pilot != NULL) *_M_pilot = M_pilot; + if (_M_data != NULL) *_M_data = M_data; return LIQUID_OK; } diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index 2ca3ab93c..bcb369d45 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -144,6 +144,38 @@ void autotest_ofdmframesync_acquire_n128() { ofdmframesync_acquire_test(128, 16 void autotest_ofdmframesync_acquire_n256() { ofdmframesync_acquire_test(256, 32, 0); } void autotest_ofdmframesync_acquire_n512() { ofdmframesync_acquire_test(512, 64, 0); } +void autotest_ofdmframe_common_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmframe common config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_default_sctype(0, NULL)) // too few subcarriers + + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range( 0, -0.4f, +0.4f, NULL)) // too few subcarriers + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.7f, +0.4f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.4f, +0.7f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.2f, -0.3f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, 0.3f, 0.2f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.02f,+0.02f,NULL)) // too few effective subcarriers + + // generate valid subcarrier allocation + unsigned int M = 120; + unsigned char p[M]; + + // default subcarrier allocation + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_init_default_sctype(M, p)) + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_validate_sctype(p, M, NULL, NULL, NULL)) + + // subcarrier allocation within an occupied frequency range + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(M, -0.4f,+0.4f,p)) + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_validate_sctype(p, M, NULL, NULL, NULL)) +} + void autotest_ofdmframegen_config() { #if LIQUID_STRICT_EXIT From 3cf3ceefcd58e8db8f0d22cbf4e403db40cd267e Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 09:01:21 -0500 Subject: [PATCH 081/186] ofdmframe/autotest: testing edge cases for subcarrier allocations --- src/multichannel/tests/ofdmframe_autotest.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index bcb369d45..2e0aed52e 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -174,6 +174,18 @@ void autotest_ofdmframe_common_config() // subcarrier allocation within an occupied frequency range CONTEND_EQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(M, -0.4f,+0.4f,p)) CONTEND_EQUALITY(LIQUID_OK, ofdmframe_validate_sctype(p, M, NULL, NULL, NULL)) + + // invalid subcarrier allocations + unsigned int i; + for (i=0; i Date: Sat, 11 Mar 2023 07:15:02 -0500 Subject: [PATCH 082/186] qpacketmodem/example: adjusting number of trials based on error performance --- examples/qpacketmodem_performance_example.c | 181 ++++++++------------ 1 file changed, 76 insertions(+), 105 deletions(-) diff --git a/examples/qpacketmodem_performance_example.c b/examples/qpacketmodem_performance_example.c index 300c516f3..607d36ab9 100644 --- a/examples/qpacketmodem_performance_example.c +++ b/examples/qpacketmodem_performance_example.c @@ -1,11 +1,6 @@ -// -// qpacketmodem_performance_example.c -// // This example demonstrates the performance of the qpacket modem // object to combine forward error-correction and modulation in one // simple interface. -// - #include #include #include @@ -21,39 +16,41 @@ void usage() { printf("qpacketmodem_performance_example [options]\n"); - printf(" h : print usage\n"); - printf(" p : payload length [bytes], default: 400\n"); - printf(" m : modulation scheme (qpsk default)\n"); + printf(" -h : print usage\n"); + printf(" -p : payload length [bytes], default: 400\n"); + printf(" -m : modulation scheme (qpsk default)\n"); liquid_print_modulation_schemes(); - printf(" v : data integrity check: crc32 default\n"); + printf(" -v : data integrity check: crc32 default\n"); liquid_print_crc_schemes(); - printf(" c : coding scheme (inner): g2412 default\n"); - printf(" k : coding scheme (outer): none default\n"); + printf(" -c : coding scheme (inner): g2412 default\n"); + printf(" -k : coding scheme (outer): none default\n"); liquid_print_fec_schemes(); - printf(" s : SNR start [dB], default: -2\n"); - printf(" x : SNR max [dB], default: 13\n"); - printf(" n : number of SNR steps, default: 16\n"); - printf(" t : number of trials, default: 800\n"); + printf(" -s : SNR start [dB], default: -5\n"); + printf(" -x : SNR max [dB], default: 20\n"); + printf(" -d : SNR step [dB], default: 0.5\n"); + printf(" -e : minimum number of packet errors per step, default: 50\n"); + printf(" -t : minimum number of packet trials per step, default: 2000\n"); + printf(" -T : maximum number of packet trials per step, default: 40000\n"); } int main(int argc, char *argv[]) { - //srand( time(NULL) ); - - // options + // options (defaults to frame64 parameters) modulation_scheme ms = LIQUID_MODEM_QPSK; // mod. scheme - crc_scheme check = LIQUID_CRC_32; // data validity check - fec_scheme fec0 = LIQUID_FEC_GOLAY2412; // fec (inner) - fec_scheme fec1 = LIQUID_FEC_NONE; // fec (outer) - unsigned int payload_len = 400; // payload length + crc_scheme check = LIQUID_CRC_24; // data validity check + fec_scheme fec0 = LIQUID_FEC_NONE; // fec (inner) + fec_scheme fec1 = LIQUID_FEC_GOLAY2412; // fec (outer) + unsigned int payload_len = 72; // payload length float SNRdB_min = -5.0f; // signal-to-noise ratio (minimum) - float SNRdB_max = 10.0f; // signal-to-noise ratio (maximum) - unsigned int num_snr = 31; // number of SNR steps - unsigned int num_packet_trials = 800; // number of trials + float SNRdB_max = 20.0f; // signal-to-noise ratio (maximum) + float SNRdB_step = 0.5f; // signal-to-noise ratio (maximum) + unsigned int min_packet_errors = 50; // minimum errors to observe for each step + unsigned int min_packet_trials = 2000; // minimum number of packets for each step + unsigned int max_packet_trials =40000; // maximum number of packets for each step // get options int dopt; - while((dopt = getopt(argc,argv,"hp:m:v:c:k:s:x:n:t:")) != EOF){ + while((dopt = getopt(argc,argv,"hp:m:v:c:k:s:x:d:e:t:T:")) != EOF){ switch (dopt) { case 'h': usage(); return 0; case 'p': payload_len = atol(optarg); break; @@ -63,18 +60,16 @@ int main(int argc, char *argv[]) case 'k': fec1 = liquid_getopt_str2fec(optarg); break; case 's': SNRdB_min = atof(optarg); break; case 'x': SNRdB_max = atof(optarg); break; - case 'n': num_snr = atoi(optarg); break; - case 't': num_packet_trials = atoi(optarg); break; + case 'd': SNRdB_step = atof(optarg); break; + case 'e': min_packet_errors = atoi(optarg); break; + case 't': min_packet_trials = atoi(optarg); break; + case 'T': max_packet_trials = atoi(optarg); break; default: exit(-1); } } - unsigned int i; - // derived values - float SNRdB_step = (SNRdB_max - SNRdB_min) / (num_snr-1); - // create and configure packet encoder/decoder object qpacketmodem q = qpacketmodem_create(); qpacketmodem_configure(q, payload_len, check, fec0, fec1, ms); @@ -82,32 +77,27 @@ int main(int argc, char *argv[]) // get frame length unsigned int frame_len = qpacketmodem_get_frame_len(q); - unsigned int num_bit_trials = 8*num_packet_trials*payload_len; // initialize payload unsigned char payload_tx [payload_len]; // payload (transmitted) unsigned char payload_rx [payload_len]; // payload (received) float complex frame_tx [frame_len]; // frame samples (transmitted) float complex frame_rx [frame_len]; // frame samples (received) - unsigned int num_bit_errors [num_snr]; // bit errors for each SNR point - unsigned int num_packet_errors[num_snr]; // packet errors for each SNR point - float BER [num_snr]; // bit error rate - float PER [num_snr]; // packet error rate + + // output file + FILE* fid = fopen(OUTPUT_FILENAME, "w"); + fprintf(fid,"%% %s: auto-generated file\n", OUTPUT_FILENAME); + fprintf(fid,"clear all; close all; SNR=[]; ber=[]; per=[];\n"); printf(" %8s %8s %8s %12s %8s %8s %6s\n", "SNR [dB]", "errors", "bits", "BER", "errors", "packets", "PER"); - unsigned int s; - for (s=0; s= min_packet_errors) + break; + if (num_packet_trials >= max_packet_trials) + break; } - BER[s] = (float)num_bit_errors[s] / (float)num_bit_trials; - PER[s] = (float)num_packet_errors[s] / (float)num_packet_trials; + float BER = (float)num_bit_errors / (float)num_bit_trials; + float PER = (float)num_packet_errors / (float)num_packet_trials; printf(" %8.2f %8u %8u %12.4e %8u %8u %6.2f%%\n", SNRdB, - num_bit_errors[s], num_bit_trials, BER[s], - num_packet_errors[s], num_packet_trials, PER[s]*100.0f); + num_bit_errors, num_bit_trials, BER, + num_packet_errors, num_packet_trials, PER*100.0f); + fprintf(fid,"SNR(end+1)=%g; ber(end+1)=%g; per(end+1)=%g;\n", SNRdB, BER, PER); + if (num_packet_errors < min_packet_errors) + break; + SNRdB += SNRdB_step; } // destroy allocated objects qpacketmodem_destroy(q); - // - // export output file - // - FILE * fid = fopen(OUTPUT_FILENAME, "w"); - fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); - fprintf(fid,"\n\n"); - fprintf(fid,"clear all\n"); - fprintf(fid,"close all\n"); - fprintf(fid,"payload_len = %u; %% payload length [bytes]\n", payload_len); - fprintf(fid,"frame_len = %u; %% frame length [symbols]\n", frame_len); - fprintf(fid,"num_snr = %u;\n", num_snr); - fprintf(fid,"num_packet_trials = %u;\n", num_packet_trials); - fprintf(fid,"num_bit_trials = %u;\n", num_bit_trials); - fprintf(fid,"SNRdB_min = %8.2f;\n", SNRdB_min); - fprintf(fid,"SNRdB_max = %8.2f;\n", SNRdB_max); - fprintf(fid,"scheme = '%s/%s/%s';\n", - fec_scheme_str[fec0][1], - fec_scheme_str[fec1][1], - modulation_types[ms].name); - fprintf(fid,"rate = 8*payload_len / frame_len; %% true rate [bits/symbol]\n"); - for (i=0; i Date: Sat, 11 Mar 2023 08:33:53 -0500 Subject: [PATCH 083/186] setting noise correctly in framesync64 performance example --- examples/framesync64_performance_example.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/framesync64_performance_example.c b/examples/framesync64_performance_example.c index fc43291c3..c50234f36 100644 --- a/examples/framesync64_performance_example.c +++ b/examples/framesync64_performance_example.c @@ -10,10 +10,11 @@ // add noise to channel void frame64_add_noise(float complex * _buf, float _SNRdB) { - float nstd = powf(10.0f, -_SNRdB/20.0f) * M_SQRT1_2; + float nstd = powf(10.0f, -_SNRdB/20.0f); + nstd *= M_SQRT2; // scale noise to account for signal being over-sampled by 2 unsigned int i; for (i=0; i Date: Sat, 11 Mar 2023 08:37:23 -0500 Subject: [PATCH 084/186] framesync64: adding interpolated SNR estimate for target PER --- examples/framesync64_performance_example.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/framesync64_performance_example.c b/examples/framesync64_performance_example.c index c50234f36..7242ca563 100644 --- a/examples/framesync64_performance_example.c +++ b/examples/framesync64_performance_example.c @@ -25,15 +25,18 @@ int main(int argc, char*argv[]) unsigned int min_errors = 50; unsigned int min_trials = 500; unsigned int max_trials = 5000; + float per_target = 1e-2f; // create buffer for the frame samples float complex frame[LIQUID_FRAME64_LEN]; - float SNRdB = -6.0f; + float SNRdB = -3.0f; + float per_0 =-1.0f, per_1 = -1.0f; + float snr_0 = 0.0f, snr_1 = 0.0f; FILE* fid = fopen(OUTPUT_FILENAME, "w"); fprintf(fid,"%% %s: auto-generated file\n", OUTPUT_FILENAME); fprintf(fid,"clear all; close all;\n"); fprintf(fid,"SNR=[]; pdetect=[]; pvalid=[];\n"); - printf("# %8s %6s %6s %6s\n", "SNR", "detect", "valid", "trials"); + printf("# %8s %6s %6s %6s %12s\n", "SNR", "detect", "valid", "trials", "PER"); while (SNRdB < 10.0f) { framesync64_reset_framedatastats(fs); unsigned int num_trials = 0, num_errors = 0; @@ -61,8 +64,11 @@ int main(int argc, char*argv[]) } // print results framedatastats_s stats = framesync64_get_framedatastats(fs); - printf(" %8.3f %6u %6u %6u\n", - SNRdB,stats.num_frames_detected,stats.num_payloads_valid,num_trials); + float per = (float)(num_trials - stats.num_payloads_valid)/(float)num_trials; + if (per >= per_target) { per_0 = per; snr_0 = SNRdB; } + else if (per_1 < 0.0f ) { per_1 = per; snr_1 = SNRdB; } + printf(" %8.3f %6u %6u %6u %12.4e\n", + SNRdB,stats.num_frames_detected,stats.num_payloads_valid,num_trials,per); fprintf(fid,"SNR(end+1)=%g; pdetect(end+1)=%g; pvalid(end+1)=%g;\n", SNRdB, (float)stats.num_frames_detected / (float)num_trials, @@ -71,6 +77,10 @@ int main(int argc, char*argv[]) break; SNRdB += 0.5f; } + float m = (logf(per_1) - logf(per_0)) / (snr_1 - snr_0); + float snr_target = snr_0 + (logf(per_target) - logf(per_0)) / m; + printf("per:{%12.4e,%12.4e}, snr:{%5.2f,%5.2f} => %.3f dB for PER %12.4e\n", + per_0, per_1, snr_0, snr_1, snr_target, per_target); fprintf(fid,"figure;\n"); fprintf(fid,"hold on;\n"); fprintf(fid," semilogy(SNR, 1-pdetect+eps,'-o', 'LineWidth',2, 'MarkerSize',2);\n"); @@ -89,3 +99,4 @@ int main(int argc, char*argv[]) framesync64_destroy(fs); return 0; } + From 0e31f7794ff42fd59dbfe7f81bd3d12e8051abbd Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 11 Mar 2023 12:41:57 -0500 Subject: [PATCH 085/186] ofdmframesync: returning appropriate value with debugging macros --- src/multichannel/src/ofdmframesync.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 8a05e20b1..7f008b054 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -1152,7 +1152,7 @@ int ofdmframesync_debug_enable(ofdmframesync _q) _q->debug_objects_created = 1; return LIQUID_OK; #else - fprintf(stderr,"ofdmframesync_debug_enable(): compile-time debugging disabled\n"); + return liquid_error(LIQUID_EICONFIG,"ofdmframesync_debug_enable(): compile-time debugging disabled"); #endif } @@ -1163,7 +1163,7 @@ int ofdmframesync_debug_disable(ofdmframesync _q) _q->debug_enabled = 0; return LIQUID_OK; #else - fprintf(stderr,"ofdmframesync_debug_disable(): compile-time debugging disabled\n"); + return liquid_error(LIQUID_EICONFIG,"ofdmframesync_debug_disable(): compile-time debugging disabled"); #endif } @@ -1325,7 +1325,7 @@ int ofdmframesync_debug_print(ofdmframesync _q, printf("ofdmframesync/debug: results written to '%s'\n", _filename); return LIQUID_OK; #else - fprintf(stderr,"ofdmframesync_debug_print(): compile-time debugging disabled\n"); + return liquid_error(LIQUID_EICONFIG,"ofdmframesync_debug_print(): compile-time debugging disabled"); #endif } From 60854477794411d19a6cd19e1bda80d25d295cd5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 12 Mar 2023 18:52:04 -0400 Subject: [PATCH 086/186] framesync64: respecting arguments with CLI, printing minimal info to screen --- scripts/framesync64_debug.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/framesync64_debug.py b/scripts/framesync64_debug.py index 27c09c0aa..47c455f4f 100755 --- a/scripts/framesync64_debug.py +++ b/scripts/framesync64_debug.py @@ -11,9 +11,9 @@ def main(argv=None): args = p.parse_args() for fname in args.sources: - filename = framesync64_plot(fname,args.export) + filename = framesync64_plot(fname,args.export,args.nodisplay) -def framesync64_plot(filename,export=None): +def framesync64_plot(filename,export=None,nodisplay=True): # open file and read values fid = open(filename,'rb') buf = np.fromfile(fid, count=1440, dtype=np.csingle) @@ -26,7 +26,7 @@ def framesync64_plot(filename,export=None): payload_sym = np.fromfile(fid, count= 600, dtype=np.csingle) payload_dec = np.fromfile(fid, count= 72, dtype=np.int8) - # compute filter response in dB + # compute smooth spectral response in dB nfft = 2400 f = np.arange(nfft)/nfft-0.5 psd = np.abs(np.fft.fftshift(np.fft.fft(buf, nfft)))**2 @@ -35,7 +35,6 @@ def framesync64_plot(filename,export=None): h = np.concatenate((w[m:], np.zeros(nfft-2*m-1), w[:m])) / (sum(w) * nfft) H = np.fft.fft(h) psd = 10*np.log10( np.real(np.fft.ifft(H * np.fft.fft(psd))) ) - #psd = 20*np.log10(np.abs(np.fft.fftshift(np.fft.fft(buf, nfft)))) # plot impulse and spectral responses fig, _ax = plt.subplots(2,2,figsize=(12,12)) @@ -58,11 +57,13 @@ def framesync64_plot(filename,export=None): _ax.set_ylabel('Imag') for _ax in ax: _ax.grid(True) - fig.suptitle('frame64, tau:%.6f, dphi:%.6f, phi:%.6f, rssi:%.3f dB, evm:%.3f' % \ - (tau_hat, dphi_hat, phi_hat, 20*np.log10(gamma_hat), evm)) - if export==None: + title = '%s, tau:%9.6f, dphi:%9.6f, phi:%9.6f, rssi:%6.3f dB, evm:%6.3f' % \ + (filename, tau_hat, dphi_hat, phi_hat, 20*np.log10(gamma_hat), evm) + print(title) + fig.suptitle(title) + if not nodisplay: plt.show() - else: + if export is not None: fig.savefig(os.path.splitext(filename)[0]+'.png',bbox_inches='tight') plt.close() From 65d20a1da8299e0686e7997b64daba2d355bfd8c Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 16 Mar 2023 17:44:13 -0400 Subject: [PATCH 087/186] crc: adding simple tests for data validity checks --- include/liquid.h | 2 +- src/fec/src/crc.c | 10 ++++---- src/fec/tests/crc_autotest.c | 47 +++++++++++++++++++++++------------- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 2773884f3..6d65c46f1 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -1194,7 +1194,7 @@ typedef enum { extern const char * crc_scheme_str[LIQUID_CRC_NUM_SCHEMES][2]; // Print compact list of existing and available CRC schemes -void liquid_print_crc_schemes(); +int liquid_print_crc_schemes(); // returns crc_scheme based on input string crc_scheme liquid_getopt_str2crc(const char * _str); diff --git a/src/fec/src/crc.c b/src/fec/src/crc.c index b3ee388ab..0b7ed7fa7 100644 --- a/src/fec/src/crc.c +++ b/src/fec/src/crc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -46,12 +46,12 @@ const char * crc_scheme_str[LIQUID_CRC_NUM_SCHEMES][2] = { // Print compact list of existing and available crc schemes -void liquid_print_crc_schemes() +int liquid_print_crc_schemes() { unsigned int i; unsigned int len = 10; - // print all available MOD schemes + // print all available CRC schemes printf(" "); for (i=0; i Date: Sat, 18 Mar 2023 09:59:20 -0400 Subject: [PATCH 088/186] build: stripping version number off archive --- makefile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makefile.in b/makefile.in index 3c0246d9b..90decdbb6 100644 --- a/makefile.in +++ b/makefile.in @@ -1,4 +1,4 @@ -# Copyright (c) 2007 - 2022 Joseph Gaeddert +# Copyright (c) 2007 - 2023 Joseph Gaeddert # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -1294,7 +1294,7 @@ install: all @echo "" mkdir -p $(DESTDIR)$(prefix)/include/liquid mkdir -p $(DESTDIR)$(libdir) - install -m 644 -p libliquid.${AR_LIB} $(DESTDIR)$(libdir)/libliquid.${AR_LIB}.${VERSION} + install -m 644 -p libliquid.${AR_LIB} $(DESTDIR)$(libdir)/libliquid.${AR_LIB} install -m 644 -p libliquid.${SH_LIB} $(DESTDIR)$(libdir)/libliquid.${SH_LIB}.${VERSION} install -m 644 -p include/liquid.h $(DESTDIR)$(prefix)/include/liquid/liquid.h ln -sf libliquid.${SH_LIB}.${VERSION} $(DESTDIR)$(libdir)/libliquid.${SH_LIB} @@ -1321,7 +1321,7 @@ install: all uninstall: @echo "uninstalling..." $(RM) $(DESTDIR)$(prefix)/include/liquid/liquid.h - $(RM) $(DESTDIR)$(libdir)/libliquid.${AR_LIB}.${VERSION} + $(RM) $(DESTDIR)$(libdir)/libliquid.${AR_LIB} $(RM) $(DESTDIR)$(libdir)/libliquid.${SH_LIB}.${VERSION} $(RM) $(DESTDIR)$(libdir)/libliquid.${SH_LIB}.1 $(RM) $(DESTDIR)$(libdir)/libliquid.${SH_LIB} From 48b34d3000d1289191038271f6573c2eac89f216 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 23 Mar 2023 08:09:54 -0400 Subject: [PATCH 089/186] fec/hamming(31,26): checking symbol size edge cases --- src/fec/tests/fec_hamming3126_autotest.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/fec/tests/fec_hamming3126_autotest.c b/src/fec/tests/fec_hamming3126_autotest.c index a3731c0f4..7713eaa9d 100644 --- a/src/fec/tests/fec_hamming3126_autotest.c +++ b/src/fec/tests/fec_hamming3126_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -65,3 +65,16 @@ void autotest_hamming3126_codec() } } +void autotest_fec_hamming3126_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping fec_hamming3126 config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // symbols too large + CONTEND_EQUALITY(fec_hamming3126_encode_symbol(1u<<26), 0) + CONTEND_EQUALITY(fec_hamming3126_decode_symbol(1u<<31), 0) +} From 2661827103c5757b39f1b6da6451dbcf33a7aed2 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Fri, 24 Mar 2023 07:56:07 -0400 Subject: [PATCH 090/186] fec: moving configuration tests to separte file --- makefile.in | 1 + src/fec/tests/fec_config_autotest.c | 51 ++++++++++++++++++++++++ src/fec/tests/fec_hamming3126_autotest.c | 13 ------ 3 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 src/fec/tests/fec_config_autotest.c diff --git a/makefile.in b/makefile.in index 90decdbb6..d92ca74c8 100644 --- a/makefile.in +++ b/makefile.in @@ -303,6 +303,7 @@ $(fec_objects) : %.o : %.c $(include_headers) fec_autotests := \ src/fec/tests/crc_autotest.c \ src/fec/tests/fec_autotest.c \ + src/fec/tests/fec_config_autotest.c \ src/fec/tests/fec_copy_autotest.c \ src/fec/tests/fec_soft_autotest.c \ src/fec/tests/fec_golay2412_autotest.c \ diff --git a/src/fec/tests/fec_config_autotest.c b/src/fec/tests/fec_config_autotest.c new file mode 100644 index 000000000..729a5725e --- /dev/null +++ b/src/fec/tests/fec_config_autotest.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "autotest/autotest.h" +#include "liquid.internal.h" + +void autotest_fec_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping fec_hamming3126 config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // symbols too large + CONTEND_EQUALITY(fec_golay2412_encode_symbol(1u<<12), 0) + CONTEND_EQUALITY(fec_golay2412_decode_symbol(1u<<24), 0) + + CONTEND_EQUALITY(fec_hamming3126_encode_symbol(1u<<26), 0) + CONTEND_EQUALITY(fec_hamming3126_decode_symbol(1u<<31), 0) + + CONTEND_EQUALITY(fec_hamming1511_encode_symbol(1u<<11), 0) + CONTEND_EQUALITY(fec_hamming1511_decode_symbol(1u<<15), 0) + + CONTEND_EQUALITY(fec_hamming128_encode_symbol(1u<<8), 0) + CONTEND_EQUALITY(fec_hamming128_decode_symbol(1u<<12), 0) +} + diff --git a/src/fec/tests/fec_hamming3126_autotest.c b/src/fec/tests/fec_hamming3126_autotest.c index 7713eaa9d..4e0090fe9 100644 --- a/src/fec/tests/fec_hamming3126_autotest.c +++ b/src/fec/tests/fec_hamming3126_autotest.c @@ -65,16 +65,3 @@ void autotest_hamming3126_codec() } } -void autotest_fec_hamming3126_config() -{ -#if LIQUID_STRICT_EXIT - AUTOTEST_WARN("skipping fec_hamming3126 config test with strict exit enabled\n"); - return; -#endif -#if !LIQUID_SUPPRESS_ERROR_OUTPUT - fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); -#endif - // symbols too large - CONTEND_EQUALITY(fec_hamming3126_encode_symbol(1u<<26), 0) - CONTEND_EQUALITY(fec_hamming3126_decode_symbol(1u<<31), 0) -} From 445c352d163008cba82d1aa8aefb55d881929360 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Mar 2023 13:17:12 +0000 Subject: [PATCH 091/186] asgram: filling entire ASCII buffer with zeros; avoid compiler warning --- src/fft/src/asgram.proto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fft/src/asgram.proto.c b/src/fft/src/asgram.proto.c index d8adcbc31..c4d62c596 100644 --- a/src/fft/src/asgram.proto.c +++ b/src/fft/src/asgram.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -256,7 +256,7 @@ int ASGRAM(_print)(ASGRAM() _q) float maxval; float maxfreq; char ascii[_q->nfft+1]; - ascii[_q->nfft] = '\0'; // append null character to end of string + memset(ascii, '\0', _q->nfft+1); // fill buffer with null characters // execute the spectrogram ASGRAM(_execute)(_q, ascii, &maxval, &maxfreq); From 05a0bdf171fc4c7f474ffffbb264eaaf0e7613f7 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Mar 2023 13:19:33 +0000 Subject: [PATCH 092/186] fskframesync: block-commenting unused section to avoid compiler warnings --- src/framing/src/fskframesync.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/framing/src/fskframesync.c b/src/framing/src/fskframesync.c index f68c4cf4c..8d30274e6 100644 --- a/src/framing/src/fskframesync.c +++ b/src/framing/src/fskframesync.c @@ -465,6 +465,7 @@ int fskframesync_execute_detectframe(fskframesync _q, //printf("signal peaked! %12.8f %12.8f %12.8f\n", // _q->rxy[0], _q->rxy[1], _q->rxy[2]); +#if 0 // compute estimate, apply bias compensation float gamma = (_q->rxy[2] - _q->rxy[0]) / _q->rxy[1]; float p2 = 9.54907046918287e-01f; @@ -472,8 +473,9 @@ int fskframesync_execute_detectframe(fskframesync _q, float xf = fabsf(gamma); float tau_hat = copysignf(p2*xf*xf + p1*xf, gamma); int num_samples = round(tau_hat * _q->k); - //printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", - // gamma, tau_hat, num_samples); + printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", + gamma, tau_hat, num_samples); +#endif // TODO: set timer and filterbank index accordingly _q->timer = 2*_q->k; From 8f092ddcb9f12863c72c6579da7c99908946b3da Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Mar 2023 13:26:41 +0000 Subject: [PATCH 093/186] autoscript: using sprintf to avoid compiler warning --- scripts/autoscript.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/autoscript.c b/scripts/autoscript.c index fd669808e..14ca83be0 100644 --- a/scripts/autoscript.c +++ b/scripts/autoscript.c @@ -297,11 +297,7 @@ void autoscript_parsefile(autoscript _q, // generate tag (e.g. "void benchmark_"); unsigned int tag_len = 5 + strlen(_q->type) + 2; char tag[tag_len]; - strcpy(tag, "void "); - strcpy(tag+5, _q->type); - tag[tag_len-2] = '_'; - tag[tag_len-1] = '\0'; - //printf("// tag : '%s'\n", tag); + sprintf(tag, "void %s_", _q->type); // parse file, looking for key char buffer[1024]; // line buffer From 583cf31ca6f4aebae69c79e0f891de0c73647dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Sat, 19 Nov 2022 06:31:52 +0000 Subject: [PATCH 094/186] Stop using SIMD dot product instructions as they are way slower than mul and add Removing the dedicated dp instruction effectively makes the dotprod_rrrf SSE4 implementation redundant, remove it Rename MMX implementations to SSE since they do not use MMX instructions anyway Add loop unrolled implementation of sumsq Prepare for AVX-512 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 35 +- library.json | 3 +- ...{dotprod_cccf.mmx.c => dotprod_cccf.sse.c} | 30 +- src/dotprod/src/dotprod_crcf.avx.c | 20 +- ...{dotprod_crcf.mmx.c => dotprod_crcf.sse.c} | 61 ++-- src/dotprod/src/dotprod_rrrf.avx.c | 23 +- ...{dotprod_rrrf.mmx.c => dotprod_rrrf.sse.c} | 58 ++-- src/dotprod/src/dotprod_rrrf.sse4.c | 307 ------------------ src/dotprod/src/sumsq.avx.c | 85 ++++- src/dotprod/src/{sumsq.mmx.c => sumsq.sse.c} | 87 ++++- 10 files changed, 270 insertions(+), 439 deletions(-) rename src/dotprod/src/{dotprod_cccf.mmx.c => dotprod_cccf.sse.c} (95%) rename src/dotprod/src/{dotprod_crcf.mmx.c => dotprod_crcf.sse.c} (88%) rename src/dotprod/src/{dotprod_rrrf.mmx.c => dotprod_rrrf.sse.c} (86%) delete mode 100644 src/dotprod/src/dotprod_rrrf.sse4.c rename src/dotprod/src/{sumsq.mmx.c => sumsq.sse.c} (60%) diff --git a/configure.ac b/configure.ac index 12d4b7cd8..33742fcee 100644 --- a/configure.ac +++ b/configure.ac @@ -170,9 +170,18 @@ else # SSSE3 : tmmintrin.h # SSE4.1/2: smmintrin.h # AVX : immintrin.h + # AVX2 : immintrin.h + # AVX512 : immintrin.h AX_EXT - if [ test "$ax_cv_have_avx2_ext" = yes ]; then + if [ test "$ax_cv_have_avx512f_ext" = yes ]; then + # AVX512 extensions + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ + src/dotprod/src/dotprod_crcf.avx.o \ + src/dotprod/src/dotprod_rrrf.avx.o \ + src/dotprod/src/sumsq.avx.o" + ARCH_OPTION='-mavx512f' + elif [ test "$ax_cv_have_avx2_ext" = yes ]; then # AVX2 extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ src/dotprod/src/dotprod_crcf.avx.o \ @@ -188,24 +197,24 @@ else ARCH_OPTION='-mavx' elif [ test "$ax_cv_have_sse41_ext" = yes ]; then # SSE4.1/2 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.sse4.o \ - src/dotprod/src/sumsq.mmx.o" + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.sse.o \ + src/dotprod/src/dotprod_crcf.sse.o \ + src/dotprod/src/dotprod_rrrf.sse.o \ + src/dotprod/src/sumsq.sse.o" ARCH_OPTION='-msse4.1' elif [ test "$ax_cv_have_sse3_ext" = yes ]; then # SSE3 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.mmx.o \ - src/dotprod/src/sumsq.mmx.o" + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.sse.o \ + src/dotprod/src/dotprod_crcf.sse.o \ + src/dotprod/src/dotprod_rrrf.sse.o \ + src/dotprod/src/sumsq.sse.o" ARCH_OPTION='-msse3' elif [ test "$ax_cv_have_sse2_ext" = yes ]; then # SSE2 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.mmx.o \ - src/dotprod/src/sumsq.mmx.o" + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.sse.o \ + src/dotprod/src/dotprod_crcf.sse.o \ + src/dotprod/src/dotprod_rrrf.sse.o \ + src/dotprod/src/sumsq.sse.o" ARCH_OPTION='-msse2' else # portable C version diff --git a/library.json b/library.json index 6f14be837..753bb1a00 100644 --- a/library.json +++ b/library.json @@ -39,9 +39,8 @@ "-<.git/>", "-<*/src/*.proto.c>", "-<*/src/*.av.c>", - "-<*/src/*.mmx.c>", "-<*/src/*.neon.c>", - "-<*/src/*.sse4.c>", + "-<*/src/*.sse.c>", "-<*/src/*.avx.c>", "-<*/src/*.x86.s>" ] diff --git a/src/dotprod/src/dotprod_cccf.mmx.c b/src/dotprod/src/dotprod_cccf.sse.c similarity index 95% rename from src/dotprod/src/dotprod_cccf.mmx.c rename to src/dotprod/src/dotprod_cccf.sse.c index 6794bfcfe..6bfc10053 100644 --- a/src/dotprod/src/dotprod_cccf.mmx.c +++ b/src/dotprod/src/dotprod_cccf.sse.c @@ -21,7 +21,7 @@ */ // -// Floating-point dot product (MMX) +// Floating-point dot product (SSE) // #include @@ -33,10 +33,6 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX -#include // MMX -#endif - #if HAVE_SSE #include // SSE #endif @@ -49,14 +45,14 @@ #include // SSE3 #endif -#define DEBUG_DOTPROD_CCCF_MMX 0 +#define DEBUG_DOTPROD_CCCF_sse 0 // forward declaration of internal methods -int dotprod_cccf_execute_mmx(dotprod_cccf _q, +int dotprod_cccf_execute_sse(dotprod_cccf _q, float complex * _x, float complex * _y); -int dotprod_cccf_execute_mmx4(dotprod_cccf _q, +int dotprod_cccf_execute_sse4(dotprod_cccf _q, float complex * _x, float complex * _y); @@ -104,7 +100,7 @@ int dotprod_cccf_run4(float complex * _h, // -// structured MMX dot product +// structured sse dot product // struct dotprod_cccf_s { @@ -177,7 +173,7 @@ dotprod_cccf dotprod_cccf_copy(dotprod_cccf q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("dotprod_cccf_copy().mmx, object cannot be NULL"); + return liquid_error_config("dotprod_cccf_copy().sse, object cannot be NULL"); dotprod_cccf q_copy = (dotprod_cccf)malloc(sizeof(struct dotprod_cccf_s)); q_copy->n = q_orig->n; @@ -206,7 +202,7 @@ int dotprod_cccf_destroy(dotprod_cccf _q) int dotprod_cccf_print(dotprod_cccf _q) { - printf("dotprod_cccf [mmx, %u coefficients]\n", _q->n); + printf("dotprod_cccf [sse, %u coefficients]\n", _q->n); unsigned int i; for (i=0; i<_q->n; i++) printf(" %3u : %12.9f +j%12.9f\n", i, _q->hi[i], _q->hq[i]); @@ -223,12 +219,12 @@ int dotprod_cccf_execute(dotprod_cccf _q, { // switch based on size if (_q->n < 32) { - return dotprod_cccf_execute_mmx(_q, _x, _y); + return dotprod_cccf_execute_sse(_q, _x, _y); } - return dotprod_cccf_execute_mmx4(_q, _x, _y); + return dotprod_cccf_execute_sse4(_q, _x, _y); } -// use MMX/SSE extensions +// use SSE extensions // // (a + jb)(c + jd) = (ac - bd) + j(ad + bc) // @@ -248,7 +244,7 @@ int dotprod_cccf_execute(dotprod_cccf _q, // x[1].real * h[1].imag, // x[1].imag * h[1].imag }; // -int dotprod_cccf_execute_mmx(dotprod_cccf _q, +int dotprod_cccf_execute_sse(dotprod_cccf _q, float complex * _x, float complex * _y) { @@ -341,8 +337,8 @@ int dotprod_cccf_execute_mmx(dotprod_cccf _q, return LIQUID_OK; } -// use MMX/SSE extensions -int dotprod_cccf_execute_mmx4(dotprod_cccf _q, +// use SSE extensions +int dotprod_cccf_execute_sse4(dotprod_cccf _q, float complex * _x, float complex * _y) { diff --git a/src/dotprod/src/dotprod_crcf.avx.c b/src/dotprod/src/dotprod_crcf.avx.c index 6e14af3ad..525148210 100644 --- a/src/dotprod/src/dotprod_crcf.avx.c +++ b/src/dotprod/src/dotprod_crcf.avx.c @@ -274,10 +274,7 @@ int dotprod_crcf_execute_avx4(dotprod_crcf _q, __m256 s0, s1, s2, s3; // dot products [re, im, re, im] // load zeros into sum registers - __m256 sum0 = _mm256_setzero_ps(); - __m256 sum1 = _mm256_setzero_ps(); - __m256 sum2 = _mm256_setzero_ps(); - __m256 sum3 = _mm256_setzero_ps(); + __m256 sum = _mm256_setzero_ps(); // r = 8*floor(n/32) unsigned int r = (n >> 5) << 3; @@ -304,22 +301,17 @@ int dotprod_crcf_execute_avx4(dotprod_crcf _q, s3 = _mm256_mul_ps(v3, h3); // parallel addition - sum0 = _mm256_add_ps( sum0, s0 ); - sum1 = _mm256_add_ps( sum1, s1 ); - sum2 = _mm256_add_ps( sum2, s2 ); - sum3 = _mm256_add_ps( sum3, s3 ); + sum = _mm256_add_ps( sum, s0 ); + sum = _mm256_add_ps( sum, s1 ); + sum = _mm256_add_ps( sum, s2 ); + sum = _mm256_add_ps( sum, s3 ); } - // fold down - sum0 = _mm256_add_ps( sum0, sum1 ); - sum2 = _mm256_add_ps( sum2, sum3 ); - sum0 = _mm256_add_ps( sum0, sum2 ); - // aligned output array float w[8] __attribute__((aligned(32))); // unload packed array and perform manual sum - _mm256_store_ps(w, sum0); + _mm256_store_ps(w, sum); w[0] += w[2] + w[4] + w[6]; w[1] += w[3] + w[5] + w[7]; diff --git a/src/dotprod/src/dotprod_crcf.mmx.c b/src/dotprod/src/dotprod_crcf.sse.c similarity index 88% rename from src/dotprod/src/dotprod_crcf.mmx.c rename to src/dotprod/src/dotprod_crcf.sse.c index 4ba9d320d..182133d22 100644 --- a/src/dotprod/src/dotprod_crcf.mmx.c +++ b/src/dotprod/src/dotprod_crcf.sse.c @@ -21,7 +21,7 @@ */ // -// Floating-point dot product (MMX) +// Floating-point dot product (SSE) // #include @@ -32,13 +32,28 @@ #include "liquid.internal.h" -#define DEBUG_DOTPROD_CRCF_MMX 0 +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#if HAVE_SSE +#include // SSE +#endif + +#if HAVE_SSE2 +#include // SSE2 +#endif + +#if HAVE_SSE3 +#include // SSE3 +#endif + +#define DEBUG_DOTPROD_CRCF_SSE 0 // forward declaration of internal methods -int dotprod_crcf_execute_mmx(dotprod_crcf _q, +int dotprod_crcf_execute_sse(dotprod_crcf _q, float complex * _x, float complex * _y); -int dotprod_crcf_execute_mmx4(dotprod_crcf _q, +int dotprod_crcf_execute_sse4(dotprod_crcf _q, float complex * _x, float complex * _y); @@ -86,7 +101,7 @@ int dotprod_crcf_run4(float * _h, // -// structured MMX dot product +// structured SSE dot product // struct dotprod_crcf_s { @@ -153,7 +168,7 @@ dotprod_crcf dotprod_crcf_copy(dotprod_crcf q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("dotprod_crcf_copy().mmx, object cannot be NULL"); + return liquid_error_config("dotprod_crcf_copy().sse, object cannot be NULL"); dotprod_crcf q_copy = (dotprod_crcf)malloc(sizeof(struct dotprod_crcf_s)); q_copy->n = q_orig->n; @@ -181,7 +196,7 @@ int dotprod_crcf_print(dotprod_crcf _q) { // print coefficients to screen, skipping odd entries (due // to repeated coefficients) - printf("dotprod_crcf [mmx, %u coefficients]\n", _q->n); + printf("dotprod_crcf [sse, %u coefficients]\n", _q->n); unsigned int i; for (i=0; i<_q->n; i++) printf(" %3u : %12.9f\n", i, _q->h[2*i]); @@ -195,13 +210,13 @@ int dotprod_crcf_execute(dotprod_crcf _q, { // switch based on size if (_q->n < 32) { - return dotprod_crcf_execute_mmx(_q, _x, _y); + return dotprod_crcf_execute_sse(_q, _x, _y); } - return dotprod_crcf_execute_mmx4(_q, _x, _y); + return dotprod_crcf_execute_sse4(_q, _x, _y); } -// use MMX/SSE extensions -int dotprod_crcf_execute_mmx(dotprod_crcf _q, +// use SSE extensions +int dotprod_crcf_execute_sse(dotprod_crcf _q, float complex * _x, float complex * _y) { @@ -257,8 +272,8 @@ int dotprod_crcf_execute_mmx(dotprod_crcf _q, return LIQUID_OK; } -// use MMX/SSE extensions -int dotprod_crcf_execute_mmx4(dotprod_crcf _q, +// use SSE extensions +int dotprod_crcf_execute_sse4(dotprod_crcf _q, float complex * _x, float complex * _y) { @@ -274,10 +289,7 @@ int dotprod_crcf_execute_mmx4(dotprod_crcf _q, __m128 s0, s1, s2, s3; // dot products [re, im, re, im] // load zeros into sum registers - __m128 sum0 = _mm_setzero_ps(); - __m128 sum1 = _mm_setzero_ps(); - __m128 sum2 = _mm_setzero_ps(); - __m128 sum3 = _mm_setzero_ps(); + __m128 sum = _mm_setzero_ps(); // r = 4*floor(n/16) unsigned int r = (n >> 4) << 2; @@ -304,22 +316,17 @@ int dotprod_crcf_execute_mmx4(dotprod_crcf _q, s3 = _mm_mul_ps(v3, h3); // parallel addition - sum0 = _mm_add_ps( sum0, s0 ); - sum1 = _mm_add_ps( sum1, s1 ); - sum2 = _mm_add_ps( sum2, s2 ); - sum3 = _mm_add_ps( sum3, s3 ); + sum = _mm_add_ps( sum, s0 ); + sum = _mm_add_ps( sum, s1 ); + sum = _mm_add_ps( sum, s2 ); + sum = _mm_add_ps( sum, s3 ); } - // fold down - sum0 = _mm_add_ps( sum0, sum1 ); - sum2 = _mm_add_ps( sum2, sum3 ); - sum0 = _mm_add_ps( sum0, sum2 ); - // aligned output array float w[4] __attribute__((aligned(16))); // unload packed array and perform manual sum - _mm_store_ps(w, sum0); + _mm_store_ps(w, sum); w[0] += w[2]; w[1] += w[3]; diff --git a/src/dotprod/src/dotprod_rrrf.avx.c b/src/dotprod/src/dotprod_rrrf.avx.c index 2af478375..08a87539c 100644 --- a/src/dotprod/src/dotprod_rrrf.avx.c +++ b/src/dotprod/src/dotprod_rrrf.avx.c @@ -219,12 +219,17 @@ int dotprod_rrrf_execute_avx(dotprod_rrrf _q, h = _mm256_load_ps(&_q->h[i]); // compute dot product - s = _mm256_dp_ps(v, h, 0xff); - + s = _mm256_mul_ps(v, h); + // parallel addition sum = _mm256_add_ps( sum, s ); } + // fold down into single value + __m256 z = _mm256_setzero_ps(); + sum = _mm256_hadd_ps(sum, z); + sum = _mm256_hadd_ps(sum, z); + // aligned output array float w[8] __attribute__((aligned(32))); @@ -270,19 +275,23 @@ int dotprod_rrrf_execute_avxu(dotprod_rrrf _q, h3 = _mm256_load_ps(&_q->h[4*i+24]); // compute dot products - s0 = _mm256_dp_ps(v0, h0, 0xff); - s1 = _mm256_dp_ps(v1, h1, 0xff); - s2 = _mm256_dp_ps(v2, h2, 0xff); - s3 = _mm256_dp_ps(v3, h3, 0xff); + s0 = _mm256_mul_ps(v0, h0); + s1 = _mm256_mul_ps(v1, h1); + s2 = _mm256_mul_ps(v2, h2); + s3 = _mm256_mul_ps(v3, h3); // parallel addition - // FIXME: these additions are by far the limiting factor sum = _mm256_add_ps( sum, s0 ); sum = _mm256_add_ps( sum, s1 ); sum = _mm256_add_ps( sum, s2 ); sum = _mm256_add_ps( sum, s3 ); } + // fold down into single value + __m256 z = _mm256_setzero_ps(); + sum = _mm256_hadd_ps(sum, z); + sum = _mm256_hadd_ps(sum, z); + // aligned output array float w[8] __attribute__((aligned(32))); diff --git a/src/dotprod/src/dotprod_rrrf.mmx.c b/src/dotprod/src/dotprod_rrrf.sse.c similarity index 86% rename from src/dotprod/src/dotprod_rrrf.mmx.c rename to src/dotprod/src/dotprod_rrrf.sse.c index 613eb707a..a9f4c58f1 100644 --- a/src/dotprod/src/dotprod_rrrf.mmx.c +++ b/src/dotprod/src/dotprod_rrrf.sse.c @@ -21,7 +21,7 @@ */ // -// Floating-point dot product (MMX) +// Floating-point dot product (SSE) // #include @@ -34,10 +34,6 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX -#include // MMX -#endif - #if HAVE_SSE #include // SSE #endif @@ -50,13 +46,13 @@ #include // SSE3 #endif -#define DEBUG_DOTPROD_RRRF_MMX 0 +#define DEBUG_DOTPROD_RRRF_SSE 0 // internal methods -int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, +int dotprod_rrrf_execute_sse(dotprod_rrrf _q, float * _x, float * _y); -int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, +int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, float * _x, float * _y); @@ -104,7 +100,7 @@ int dotprod_rrrf_run4(float * _h, // -// structured MMX dot product +// structured SSE dot product // struct dotprod_rrrf_s { @@ -167,7 +163,7 @@ dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("dotprod_rrrf_copy().mmx, object cannot be NULL"); + return liquid_error_config("dotprod_rrrf_copy().sse, object cannot be NULL"); dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); q_copy->n = q_orig->n; @@ -191,7 +187,7 @@ int dotprod_rrrf_destroy(dotprod_rrrf _q) int dotprod_rrrf_print(dotprod_rrrf _q) { - printf("dotprod_rrrf [mmx, %u coefficients]\n", _q->n); + printf("dotprod_rrrf [sse, %u coefficients]\n", _q->n); unsigned int i; for (i=0; i<_q->n; i++) printf("%3u : %12.9f\n", i, _q->h[i]); @@ -205,13 +201,13 @@ int dotprod_rrrf_execute(dotprod_rrrf _q, { // switch based on size if (_q->n < 16) { - return dotprod_rrrf_execute_mmx(_q, _x, _y); + return dotprod_rrrf_execute_sse(_q, _x, _y); } - return dotprod_rrrf_execute_mmx4(_q, _x, _y); + return dotprod_rrrf_execute_sse4(_q, _x, _y); } -// use MMX/SSE extensions -int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, +// use SSE extensions +int dotprod_rrrf_execute_sse(dotprod_rrrf _q, float * _x, float * _y) { @@ -235,7 +231,7 @@ int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, // compute multiplication s = _mm_mul_ps(v, h); - + // parallel addition sum = _mm_add_ps( sum, s ); } @@ -267,8 +263,8 @@ int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, return LIQUID_OK; } -// use MMX/SSE extensions, unrolled loop -int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, +// use SSE extensions, unrolled loop +int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, float * _x, float * _y) { @@ -278,10 +274,7 @@ int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, __m128 s0, s1, s2, s3; // load zeros into sum registers - __m128 sum0 = _mm_setzero_ps(); - __m128 sum1 = _mm_setzero_ps(); - __m128 sum2 = _mm_setzero_ps(); - __m128 sum3 = _mm_setzero_ps(); + __m128 sum = _mm_setzero_ps(); // r = 4*floor(n/16) unsigned int r = (_q->n >> 4) << 2; @@ -308,32 +301,27 @@ int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, s3 = _mm_mul_ps(v3, h3); // parallel addition - sum0 = _mm_add_ps( sum0, s0 ); - sum1 = _mm_add_ps( sum1, s1 ); - sum2 = _mm_add_ps( sum2, s2 ); - sum3 = _mm_add_ps( sum3, s3 ); + sum = _mm_add_ps( sum, s0 ); + sum = _mm_add_ps( sum, s1 ); + sum = _mm_add_ps( sum, s2 ); + sum = _mm_add_ps( sum, s3 ); } - // fold down into single 4-element register - sum0 = _mm_add_ps( sum0, sum1 ); - sum2 = _mm_add_ps( sum2, sum3 ); - sum0 = _mm_add_ps( sum0, sum2); - // aligned output array float w[4] __attribute__((aligned(16))); #if HAVE_SSE3 // SSE3: fold down to single value using _mm_hadd_ps() __m128 z = _mm_setzero_ps(); - sum0 = _mm_hadd_ps(sum0, z); - sum0 = _mm_hadd_ps(sum0, z); + sum = _mm_hadd_ps(sum, z); + sum = _mm_hadd_ps(sum, z); // unload single (lower value) - _mm_store_ss(w, sum0); + _mm_store_ss(w, sum); float total = w[0]; #else // SSE2 and below: unload packed array and perform manual sum - _mm_store_ps(w, sum0); + _mm_store_ps(w, sum); float total = w[0] + w[1] + w[2] + w[3]; #endif diff --git a/src/dotprod/src/dotprod_rrrf.sse4.c b/src/dotprod/src/dotprod_rrrf.sse4.c deleted file mode 100644 index 5bddc12d5..000000000 --- a/src/dotprod/src/dotprod_rrrf.sse4.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// Floating-point dot product (SSE4.1/2) -// - -#include -#include -#include -#include - -#include "liquid.internal.h" - -// include proper SIMD extensions for x86 platforms -// NOTE: these pre-processor macros are defined in config.h - -#if 0 -#include // MMX -#include // SSE -#include // SSE2 -#include // SSE3 -#endif -#include // SSE4.1/2 - -#define DEBUG_DOTPROD_RRRF_SSE4 0 - -// internal methods -int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, - float * _x, - float * _y); -int dotprod_rrrf_execute_sse4u(dotprod_rrrf _q, - float * _x, - float * _y); - -// basic dot product (ordinal calculation) -int dotprod_rrrf_run(float * _h, - float * _x, - unsigned int _n, - float * _y) -{ - float r=0; - unsigned int i; - for (i=0; i<_n; i++) - r += _h[i] * _x[i]; - *_y = r; - return LIQUID_OK; -} - -// basic dot product (ordinal calculation) with loop unrolled -int dotprod_rrrf_run4(float * _h, - float * _x, - unsigned int _n, - float * _y) -{ - float r=0; - - // t = 4*(floor(_n/4)) - unsigned int t=(_n>>2)<<2; - - // compute dotprod in groups of 4 - unsigned int i; - for (i=0; in = _n; - - // allocate memory for coefficients, 16-byte aligned - q->h = (float*) _mm_malloc( q->n*sizeof(float), 16); - - // set coefficients - unsigned int i; - for (i=0; in; i++) - q->h[i] = _h[_rev ? q->n-i-1 : i]; - - // return object - return q; -} - -dotprod_rrrf dotprod_rrrf_create(float * _h, - unsigned int _n) -{ - return dotprod_rrrf_create_opt(_h, _n, 0); -} - -dotprod_rrrf dotprod_rrrf_create_rev(float * _h, - unsigned int _n) -{ - return dotprod_rrrf_create_opt(_h, _n, 1); -} - -// re-create the structured dotprod object -dotprod_rrrf dotprod_rrrf_recreate(dotprod_rrrf _q, - float * _h, - unsigned int _n) -{ - // completely destroy and re-create dotprod object - dotprod_rrrf_destroy(_q); - return dotprod_rrrf_create(_h,_n); -} - -// re-create the structured dotprod object, coefficients reversed -dotprod_rrrf dotprod_rrrf_recreate_rev(dotprod_rrrf _q, - float * _h, - unsigned int _n) -{ - // completely destroy and re-create dotprod object - dotprod_rrrf_destroy(_q); - return dotprod_rrrf_create_rev(_h,_n); -} - -dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) -{ - // validate input - if (q_orig == NULL) - return liquid_error_config("dotprod_rrrf_copy().sse4, object cannot be NULL"); - - dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); - q_copy->n = q_orig->n; - - // allocate memory for coefficients, 16-byte aligned - q_copy->h = (float*) _mm_malloc( q_copy->n*sizeof(float), 16 ); - - // copy coefficients array - memmove(q_copy->h, q_orig->h, q_orig->n*sizeof(float)); - - // return object - return q_copy; -} - -int dotprod_rrrf_destroy(dotprod_rrrf _q) -{ - _mm_free(_q->h); - free(_q); - return LIQUID_OK; -} - -int dotprod_rrrf_print(dotprod_rrrf _q) -{ - printf("dotprod_rrrf [sse4.1/4.2, %u coefficients]\n", _q->n); - unsigned int i; - for (i=0; i<_q->n; i++) - printf("%3u : %12.9f\n", i, _q->h[i]); - return LIQUID_OK; -} - -// -int dotprod_rrrf_execute(dotprod_rrrf _q, - float * _x, - float * _y) -{ - // switch based on size - if (_q->n < 16) { - return dotprod_rrrf_execute_sse4(_q, _x, _y); - } - return dotprod_rrrf_execute_sse4u(_q, _x, _y); -} - -// use MMX/SSE extensions -int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, - float * _x, - float * _y) -{ - __m128 v; // input vector - __m128 h; // coefficients vector - __m128 s; // dot product - __m128 sum = _mm_setzero_ps(); // load zeros into sum register - - // t = 4*(floor(_n/4)) - unsigned int t = (_q->n >> 2) << 2; - - // - unsigned int i; - for (i=0; ih[i]); - - // compute dot product - s = _mm_dp_ps(v, h, 0xff); - - // parallel addition - sum = _mm_add_ps( sum, s ); - } - - // aligned output array - float w[4] __attribute__((aligned(16))); - - // unload packed array - _mm_store_ps(w, sum); - float total = w[0]; - - // cleanup - for (; i<_q->n; i++) - total += _x[i] * _q->h[i]; - - // set return value - *_y = total; - return LIQUID_OK; -} - -// use MMX/SSE extensions (unrolled) -int dotprod_rrrf_execute_sse4u(dotprod_rrrf _q, - float * _x, - float * _y) -{ - __m128 v0, v1, v2, v3; - __m128 h0, h1, h2, h3; - __m128 s0, s1, s2, s3; - __m128 sum = _mm_setzero_ps(); // load zeros into sum register - - // t = 4*(floor(_n/16)) - unsigned int r = (_q->n >> 4) << 2; - - // - unsigned int i; - for (i=0; ih[4*i+ 0]); - h1 = _mm_load_ps(&_q->h[4*i+ 4]); - h2 = _mm_load_ps(&_q->h[4*i+ 8]); - h3 = _mm_load_ps(&_q->h[4*i+12]); - - // compute dot products - s0 = _mm_dp_ps(v0, h0, 0xff); - s1 = _mm_dp_ps(v1, h1, 0xff); - s2 = _mm_dp_ps(v2, h2, 0xff); - s3 = _mm_dp_ps(v3, h3, 0xff); - - // parallel addition - // FIXME: these additions are by far the limiting factor - sum = _mm_add_ps( sum, s0 ); - sum = _mm_add_ps( sum, s1 ); - sum = _mm_add_ps( sum, s2 ); - sum = _mm_add_ps( sum, s3 ); - } - - // aligned output array - float w[4] __attribute__((aligned(16))); - - // unload packed array - _mm_store_ps(w, sum); - float total = w[0]; - - // cleanup - for (i=4*r; i<_q->n; i++) - total += _x[i] * _q->h[i]; - - // set return value - *_y = total; - return LIQUID_OK; -} - diff --git a/src/dotprod/src/sumsq.avx.c b/src/dotprod/src/sumsq.avx.c index 76e3c59de..032903179 100644 --- a/src/dotprod/src/sumsq.avx.c +++ b/src/dotprod/src/sumsq.avx.c @@ -56,12 +56,12 @@ // sum squares, basic loop // _v : input array [size: 1 x _n] // _n : input length -float liquid_sumsqf(float * _v, - unsigned int _n) +float liquid_sumsqf_avx(float * _v, + unsigned int _n) { // first cut: ... __m256 v; // input vector - __m256 s; // dot product + __m256 s; // product __m256 sum = _mm256_setzero_ps(); // load zeros into sum register // t = 8*(floor(_n/8)) @@ -80,27 +80,94 @@ float liquid_sumsqf(float * _v, sum = _mm256_add_ps( sum, s ); } + // fold down into single value + __m256 z = _mm256_setzero_ps(); + sum = _mm256_hadd_ps(sum, z); + sum = _mm256_hadd_ps(sum, z); + // aligned output array - float total; + float w[8] __attribute__((aligned(32))); + + _mm256_store_ps(w, sum); + float total = w[0] + w[4]; + + // cleanup + for (; i<_n; i++) + total += _v[i] * _v[i]; + + // set return value + return total; +} + +// sum squares, unrolled loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf_avxu(float * _v, + unsigned int _n) +{ + // first cut: ... + __m256 v0, v1, v2, v3; // input vector + __m256 s0, s1, s2, s3; // product + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/32)) + unsigned int t = (_n >> 5) << 3; + + // + unsigned int i; + for (i=0; i @@ -33,10 +33,6 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX -#include // MMX -#endif - #if HAVE_SSE #include // SSE #endif @@ -52,8 +48,8 @@ // sum squares, basic loop // _v : input array [size: 1 x _n] // _n : input length -float liquid_sumsqf(float * _v, - unsigned int _n) +float liquid_sumsqf_sse(float * _v, + unsigned int _n) { // first cut: ... __m128 v; // input vector @@ -102,7 +98,82 @@ float liquid_sumsqf(float * _v, return total; } -// sum squares, basic loop +// sum squares, unrolled loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf_sseu(float * _v, + unsigned int _n) +{ + // first cut: ... + __m128 v0, v1, v2, v3; // input vector + __m128 s0, s1, s2, s3; // product + __m128 sum = _mm_setzero_ps(); // load zeros into sum register + + // t = 4*(floor(_n/16)) + unsigned int t = (_n >> 4) << 2; + + // + unsigned int i; + for (i=0; i Date: Sat, 25 Mar 2023 21:42:21 +0000 Subject: [PATCH 095/186] Fix typo in sumsq.avx.c header Add sumsq AVX512-F implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 2 +- src/dotprod/src/sumsq.avx.c | 2 +- src/dotprod/src/sumsq.avx512f.c | 164 ++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/dotprod/src/sumsq.avx512f.c diff --git a/configure.ac b/configure.ac index 33742fcee..d9c1286ed 100644 --- a/configure.ac +++ b/configure.ac @@ -179,7 +179,7 @@ else MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ src/dotprod/src/dotprod_crcf.avx.o \ src/dotprod/src/dotprod_rrrf.avx.o \ - src/dotprod/src/sumsq.avx.o" + src/dotprod/src/sumsq.avx512f.o" ARCH_OPTION='-mavx512f' elif [ test "$ax_cv_have_avx2_ext" = yes ]; then # AVX2 extensions diff --git a/src/dotprod/src/sumsq.avx.c b/src/dotprod/src/sumsq.avx.c index 032903179..88685fca7 100644 --- a/src/dotprod/src/sumsq.avx.c +++ b/src/dotprod/src/sumsq.avx.c @@ -21,7 +21,7 @@ */ // -// sumsq.mmx.c : floating-point sum of squares (MMX) +// sumsq.avx.c : floating-point sum of squares (AVX) // #include diff --git a/src/dotprod/src/sumsq.avx512f.c b/src/dotprod/src/sumsq.avx512f.c new file mode 100644 index 000000000..ad9c75305 --- /dev/null +++ b/src/dotprod/src/sumsq.avx512f.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2007 - 2015 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// sumsq.avx512f.c : floating-point sum of squares (AVX512-F) +// + +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#if HAVE_MMX +#include // MMX +#endif + +#if HAVE_SSE +#include // SSE +#endif + +#if HAVE_SSE2 +#include // SSE2 +#endif + +#if HAVE_SSE3 +#include // SSE3 +#endif + +#if HAVE_AVX +#include // AVX +#endif + +// sum squares, basic loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf_avx(float * _v, + unsigned int _n) +{ + // first cut: ... + __m512 v; // input vector + __m512 s; // product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/16)) + unsigned int t = (_n >> 4) << 4; + + // + unsigned int i; + for (i=0; i> 6) << 4; + + // + unsigned int i; + for (i=0; i Date: Sat, 25 Mar 2023 22:57:43 +0000 Subject: [PATCH 096/186] Add AVX512-F dotprod implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 6 +- library.json | 1 + src/dotprod/src/dotprod_cccf.avx512f.c | 406 +++++++++++++++++++++++++ src/dotprod/src/dotprod_crcf.avx512f.c | 324 ++++++++++++++++++++ src/dotprod/src/dotprod_rrrf.avx512f.c | 292 ++++++++++++++++++ 5 files changed, 1026 insertions(+), 3 deletions(-) create mode 100644 src/dotprod/src/dotprod_cccf.avx512f.c create mode 100644 src/dotprod/src/dotprod_crcf.avx512f.c create mode 100644 src/dotprod/src/dotprod_rrrf.avx512f.c diff --git a/configure.ac b/configure.ac index d9c1286ed..34ab3dfc3 100644 --- a/configure.ac +++ b/configure.ac @@ -176,9 +176,9 @@ else if [ test "$ax_cv_have_avx512f_ext" = yes ]; then # AVX512 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ - src/dotprod/src/dotprod_crcf.avx.o \ - src/dotprod/src/dotprod_rrrf.avx.o \ + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx512f.o \ + src/dotprod/src/dotprod_crcf.avx512f.o \ + src/dotprod/src/dotprod_rrrf.avx512f.o \ src/dotprod/src/sumsq.avx512f.o" ARCH_OPTION='-mavx512f' elif [ test "$ax_cv_have_avx2_ext" = yes ]; then diff --git a/library.json b/library.json index 753bb1a00..5a05e3128 100644 --- a/library.json +++ b/library.json @@ -42,6 +42,7 @@ "-<*/src/*.neon.c>", "-<*/src/*.sse.c>", "-<*/src/*.avx.c>", + "-<*/src/*.avx512f.c>", "-<*/src/*.x86.s>" ] }, diff --git a/src/dotprod/src/dotprod_cccf.avx512f.c b/src/dotprod/src/dotprod_cccf.avx512f.c new file mode 100644 index 000000000..abb7768c6 --- /dev/null +++ b/src/dotprod/src/dotprod_cccf.avx512f.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX512-F) +// + +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#include // AVX + +#define DEBUG_DOTPROD_CCCF_AVX 0 + +// forward declaration of internal methods +int dotprod_cccf_execute_avx512f(dotprod_cccf _q, + float complex * _x, + float complex * _y); + +int dotprod_cccf_execute_avx512f4(dotprod_cccf _q, + float complex * _x, + float complex * _y); + +// basic dot product (ordinal calculation) +int dotprod_cccf_run(float complex * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_cccf_run4(float complex * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 64-byte aligned + q->hi = (float*) _mm_malloc( 2*q->n*sizeof(float), 64 ); + q->hq = (float*) _mm_malloc( 2*q->n*sizeof(float), 64 ); + + // set coefficients, repeated + // hi = { crealf(_h[0]), crealf(_h[0]), ... crealf(_h[n-1]), crealf(_h[n-1])} + // hq = { cimagf(_h[0]), cimagf(_h[0]), ... cimagf(_h[n-1]), cimagf(_h[n-1])} + unsigned int i; + for (i=0; in; i++) { + unsigned int k = _rev ? q->n-i-1 : i; + q->hi[2*i+0] = crealf(_h[k]); + q->hi[2*i+1] = crealf(_h[k]); + + q->hq[2*i+0] = cimagf(_h[k]); + q->hq[2*i+1] = cimagf(_h[k]); + } + + // return object + return q; +} + +dotprod_cccf dotprod_cccf_create(float complex * _h, + unsigned int _n) +{ + return dotprod_cccf_create_opt(_h, _n, 0); +} + +dotprod_cccf dotprod_cccf_create_rev(float complex * _h, + unsigned int _n) +{ + return dotprod_cccf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_cccf dotprod_cccf_recreate(dotprod_cccf _q, + float complex * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_cccf_destroy(_q); + return dotprod_cccf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_cccf dotprod_cccf_recreate_rev(dotprod_cccf _q, + float complex * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_cccf_destroy(_q); + return dotprod_cccf_create_rev(_h,_n); +} + +dotprod_cccf dotprod_cccf_copy(dotprod_cccf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_cccf_copy().avx512f, object cannot be NULL"); + + dotprod_cccf q_copy = (dotprod_cccf)malloc(sizeof(struct dotprod_cccf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 64-byte aligned (repeated) + q_copy->hi = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 64 ); + q_copy->hq = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 64 ); + + // copy coefficients array (repeated) + // hi = { crealf(_h[0]), crealf(_h[0]), ... crealf(_h[n-1]), crealf(_h[n-1])} + // hq = { cimagf(_h[0]), cimagf(_h[0]), ... cimagf(_h[n-1]), cimagf(_h[n-1])} + memmove(q_copy->hi, q_orig->hi, 2*q_orig->n*sizeof(float)); + memmove(q_copy->hq, q_orig->hq, 2*q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + +int dotprod_cccf_destroy(dotprod_cccf _q) +{ + _mm_free(_q->hi); + _mm_free(_q->hq); + free(_q); + return LIQUID_OK; +} + +int dotprod_cccf_print(dotprod_cccf _q) +{ + printf("dotprod_cccf [avx512f, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf(" %3u : %12.9f +j%12.9f\n", i, _q->hi[i], _q->hq[i]); + return LIQUID_OK; +} + +// execute structured dot product +// _q : dotprod object +// _x : input array +// _y : output sample +int dotprod_cccf_execute(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // switch based on size + if (_q->n < 128) { + return dotprod_cccf_execute_avx512f(_q, _x, _y); + } + return dotprod_cccf_execute_avx512f4(_q, _x, _y); +} + +// use AVX512-F extensions +// +// (a + jb)(c + jd) = (ac - bd) + j(ad + bc) +// +// mm_x = { x[0].real, x[0].imag, x[1].real, x[1].imag, x[2].real, x[2].imag, x[3].real, x[3].imag } +// mm_hi = { h[0].real, h[0].real, h[1].real, h[1].real, h[2].real, h[2].real, h[3].real, h[3].real } +// mm_hq = { h[0].imag, h[0].imag, h[1].imag, h[1].imag, h[2].imag, h[2].imag, h[3].imag, h[3].imag } +// +// mm_y0 = mm_x * mm_hi +// = { x[0].real * h[0].real, +// x[0].imag * h[0].real, +// x[1].real * h[1].real, +// x[1].imag * h[1].real, +// x[2].real * h[2].real, +// x[2].imag * h[2].real, +// x[3].real * h[3].real, +// x[3].imag * h[3].real }; +// +// mm_y1 = mm_x * mm_hq +// = { x[0].real * h[0].imag, +// x[0].imag * h[0].imag, +// x[1].real * h[1].imag, +// x[1].imag * h[1].imag, +// x[2].real * h[2].imag, +// x[2].imag * h[2].imag, +// x[3].real * h[3].imag, +// x[3].imag * h[3].imag }; +// +int dotprod_cccf_execute_avx512f(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // temporary buffers + __m512 v; // input vector + __m512 hi; // coefficients vector (real) + __m512 hq; // coefficients vector (imag) + __m512 ci; // output multiplication (v * hi) + __m512 cq; // output multiplication (v * hq) + + __m512 s; // dot product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + __m512 one = _mm512_set1_ps(1.0f); // load ones into register + + // t = 16*(floor(_n/16)) + unsigned int t = (n >> 4) << 4; + + // + unsigned int i; + for (i=0; ihi[i]); + hq = _mm512_load_ps(&_q->hq[i]); + + // compute parallel multiplications + ci = _mm512_mul_ps(v, hi); + cq = _mm512_mul_ps(v, hq); + + // shuffle values + cq = _mm512_shuffle_ps( cq, cq, _MM_SHUFFLE(2,3,0,1) ); + + // combine using addsub_ps() + s = _mm512_fmaddsub_ps( ci, one, cq ); + + // accumulate + sum = _mm512_add_ps(sum, s); + } + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sum); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sum); + + //float complex total = *((float complex*)w); + float complex total = w[0] + w[1] * _Complex_I; + + // cleanup + for (i=t/2; i<_q->n; i++) + total += _x[i] * ( _q->hi[2*i] + _q->hq[2*i]*_Complex_I ); + + // set return value + *_y = total; + return LIQUID_OK; +} + +// use AVX512-F extensions +int dotprod_cccf_execute_avx512f4(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m512 v0, v1, v2, v3; // input vectors + __m512 hi0, hi1, hi2, hi3; // coefficients vectors (real) + __m512 hq0, hq1, hq2, hq3; // coefficients vectors (imag) + __m512 ci0, ci1, ci2, ci3; // output multiplications (v * hi) + __m512 cq0, cq1, cq2, cq3; // output multiplications (v * hq) + + // load zeros into sum registers + __m512 sumi = _mm512_setzero_ps(); + __m512 sumq = _mm512_setzero_ps(); + + + __m512 one = _mm512_set1_ps(1.0f); // load ones into register + + // r = 16*floor(n/64) + unsigned int r = (n >> 6) << 4; + + // + unsigned int i; + for (i=0; ihi[4*i+0]); + hi1 = _mm512_load_ps(&_q->hi[4*i+16]); + hi2 = _mm512_load_ps(&_q->hi[4*i+32]); + hi3 = _mm512_load_ps(&_q->hi[4*i+48]); + + // load real coefficients into registers (aligned) + hq0 = _mm512_load_ps(&_q->hq[4*i+0]); + hq1 = _mm512_load_ps(&_q->hq[4*i+16]); + hq2 = _mm512_load_ps(&_q->hq[4*i+32]); + hq3 = _mm512_load_ps(&_q->hq[4*i+48]); + + // compute parallel multiplications (real) + ci0 = _mm512_mul_ps(v0, hi0); + ci1 = _mm512_mul_ps(v1, hi1); + ci2 = _mm512_mul_ps(v2, hi2); + ci3 = _mm512_mul_ps(v3, hi3); + + // compute parallel multiplications (imag) + cq0 = _mm512_mul_ps(v0, hq0); + cq1 = _mm512_mul_ps(v1, hq1); + cq2 = _mm512_mul_ps(v2, hq2); + cq3 = _mm512_mul_ps(v3, hq3); + + // accumulate + sumi = _mm512_add_ps(sumi, ci0); sumq = _mm512_add_ps(sumq, cq0); + sumi = _mm512_add_ps(sumi, ci1); sumq = _mm512_add_ps(sumq, cq1); + sumi = _mm512_add_ps(sumi, ci2); sumq = _mm512_add_ps(sumq, cq2); + sumi = _mm512_add_ps(sumi, ci3); sumq = _mm512_add_ps(sumq, cq3); + } + + // shuffle values + sumq = _mm512_shuffle_ps( sumq, sumq, _MM_SHUFFLE(2,3,0,1) ); + + // combine using addsub_ps() + sumi = _mm512_fmaddsub_ps( sumi, one, sumq ); + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sumi); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sumi); + + float complex total = w[0] + w[1] * _Complex_I; + + // cleanup (note: n _must_ be even) + // TODO : clean this method up + for (i=2*r; i<_q->n; i++) { + total += _x[i] * ( _q->hi[2*i] + _q->hq[2*i]*_Complex_I ); + } + + // set return value + *_y = total; + return LIQUID_OK; +} + diff --git a/src/dotprod/src/dotprod_crcf.avx512f.c b/src/dotprod/src/dotprod_crcf.avx512f.c new file mode 100644 index 000000000..fae077f3f --- /dev/null +++ b/src/dotprod/src/dotprod_crcf.avx512f.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX512-F) +// + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +#define DEBUG_DOTPROD_CRCF_AVX 0 + +// forward declaration of internal methods +int dotprod_crcf_execute_avx512f(dotprod_crcf _q, + float complex * _x, + float complex * _y); +int dotprod_crcf_execute_avx512f4(dotprod_crcf _q, + float complex * _x, + float complex * _y); + +// basic dot product (ordinal calculation) +int dotprod_crcf_run(float * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_crcf_run4(float * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 64-byte aligned + q->h = (float*) _mm_malloc( 2*q->n*sizeof(float), 64 ); + + // set coefficients, repeated + // h = { _h[0], _h[0], _h[1], _h[1], ... _h[n-1], _h[n-1]} + unsigned int i; + for (i=0; in; i++) { + unsigned int k = _rev ? q->n-i-1 : i; + q->h[2*i+0] = _h[k]; + q->h[2*i+1] = _h[k]; + } + + // return object + return q; +} + +dotprod_crcf dotprod_crcf_create(float * _h, + unsigned int _n) +{ + return dotprod_crcf_create_opt(_h, _n, 0); +} + +dotprod_crcf dotprod_crcf_create_rev(float * _h, + unsigned int _n) +{ + return dotprod_crcf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_crcf dotprod_crcf_recreate(dotprod_crcf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_crcf_destroy(_q); + return dotprod_crcf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_crcf dotprod_crcf_recreate_rev(dotprod_crcf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_crcf_destroy(_q); + return dotprod_crcf_create_rev(_h,_n); +} + +dotprod_crcf dotprod_crcf_copy(dotprod_crcf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_crcf_copy().avx512f, object cannot be NULL"); + + dotprod_crcf q_copy = (dotprod_crcf)malloc(sizeof(struct dotprod_crcf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 64-byte aligned (repeated) + q_copy->h = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 64 ); + + // copy coefficients array (repeated) + // h = { _h[0], _h[0], _h[1], _h[1], ... _h[n-1], _h[n-1]} + memmove(q_copy->h, q_orig->h, 2*q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + + +int dotprod_crcf_destroy(dotprod_crcf _q) +{ + _mm_free(_q->h); + free(_q); + return LIQUID_OK; +} + +int dotprod_crcf_print(dotprod_crcf _q) +{ + // print coefficients to screen, skipping odd entries (due + // to repeated coefficients) + printf("dotprod_crcf [avx512f, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf(" %3u : %12.9f\n", i, _q->h[2*i]); + return LIQUID_OK; +} + +// +int dotprod_crcf_execute(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // switch based on size + if (_q->n < 128) { + return dotprod_crcf_execute_avx512f(_q, _x, _y); + } + return dotprod_crcf_execute_avx512f4(_q, _x, _y); +} + +// use AVX512-F extensions +int dotprod_crcf_execute_avx512f(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m512 v; // input vector + __m512 h; // coefficients vector + __m512 s; // dot product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/16)) + unsigned int t = (n >> 4) << 4; + + // + unsigned int i; + for (i=0; ih[i]); + + // compute multiplication + s = _mm512_mul_ps(v, h); + + // accumulate + sum = _mm512_add_ps(sum, s); + } + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sum); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sum); + + // cleanup (note: n _must_ be even) + for (; ih[i ]; + w[1] += x[i+1] * _q->h[i+1]; + } + + // set return value + *_y = w[0] + _Complex_I*w[1]; + return LIQUID_OK; +} + +// use AVX512-F extensions +int dotprod_crcf_execute_avx512f4(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m512 v0, v1, v2, v3; // input vectors + __m512 h0, h1, h2, h3; // coefficients vectors + __m512 s0, s1, s2, s3; // dot products [re, im, re, im] + + // load zeros into sum registers + __m512 sum = _mm512_setzero_ps(); + + // r = 16*floor(n/64) + unsigned int r = (n >> 6) << 4; + + // + unsigned int i; + for (i=0; ih[4*i+0]); + h1 = _mm512_load_ps(&_q->h[4*i+16]); + h2 = _mm512_load_ps(&_q->h[4*i+32]); + h3 = _mm512_load_ps(&_q->h[4*i+48]); + + // compute multiplication + s0 = _mm512_mul_ps(v0, h0); + s1 = _mm512_mul_ps(v1, h1); + s2 = _mm512_mul_ps(v2, h2); + s3 = _mm512_mul_ps(v3, h3); + + // parallel addition + sum = _mm512_add_ps( sum, s0 ); + sum = _mm512_add_ps( sum, s1 ); + sum = _mm512_add_ps( sum, s2 ); + sum = _mm512_add_ps( sum, s3 ); + } + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sum); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sum); + + // cleanup (note: n _must_ be even) + for (i=4*r; ih[i ]; + w[1] += x[i+1] * _q->h[i+1]; + } + + // set return value + *_y = w[0] + w[1]*_Complex_I; + return LIQUID_OK; +} + diff --git a/src/dotprod/src/dotprod_rrrf.avx512f.c b/src/dotprod/src/dotprod_rrrf.avx512f.c new file mode 100644 index 000000000..c11a744a9 --- /dev/null +++ b/src/dotprod/src/dotprod_rrrf.avx512f.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX512-F) +// + +#include +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#include // AVX + +#define DEBUG_DOTPROD_RRRF_AVX 0 + +// internal methods +int dotprod_rrrf_execute_avx512f(dotprod_rrrf _q, + float * _x, + float * _y); +int dotprod_rrrf_execute_avx512fu(dotprod_rrrf _q, + float * _x, + float * _y); + +// basic dot product (ordinal calculation) +int dotprod_rrrf_run(float * _h, + float * _x, + unsigned int _n, + float * _y) +{ + float r=0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_rrrf_run4(float * _h, + float * _x, + unsigned int _n, + float * _y) +{ + float r=0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 64-byte aligned + q->h = (float*) _mm_malloc( q->n*sizeof(float), 64); + + // set coefficients + unsigned int i; + for (i=0; in; i++) + q->h[i] = _h[_rev ? q->n-i-1 : i]; + + // return object + return q; +} + +dotprod_rrrf dotprod_rrrf_create(float * _h, + unsigned int _n) +{ + return dotprod_rrrf_create_opt(_h, _n, 0); +} + +dotprod_rrrf dotprod_rrrf_create_rev(float * _h, + unsigned int _n) +{ + return dotprod_rrrf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_rrrf dotprod_rrrf_recreate(dotprod_rrrf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_rrrf_destroy(_q); + return dotprod_rrrf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_rrrf dotprod_rrrf_recreate_rev(dotprod_rrrf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_rrrf_destroy(_q); + return dotprod_rrrf_create_rev(_h,_n); +} + +dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_rrrf_copy().avx512f, object cannot be NULL"); + + dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 64-byte aligned + q_copy->h = (float*) _mm_malloc( q_copy->n*sizeof(float), 64 ); + + // copy coefficients array + memmove(q_copy->h, q_orig->h, q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + +int dotprod_rrrf_destroy(dotprod_rrrf _q) +{ + _mm_free(_q->h); + free(_q); + return LIQUID_OK; +} + +int dotprod_rrrf_print(dotprod_rrrf _q) +{ + printf("dotprod_rrrf [avx512f, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf("%3u : %12.9f\n", i, _q->h[i]); + return LIQUID_OK; +} + +// +int dotprod_rrrf_execute(dotprod_rrrf _q, + float * _x, + float * _y) +{ + // switch based on size + if (_q->n < 64) { + return dotprod_rrrf_execute_avx512f(_q, _x, _y); + } + return dotprod_rrrf_execute_avx512fu(_q, _x, _y); +} + +// use AVX512-F extensions +int dotprod_rrrf_execute_avx512f(dotprod_rrrf _q, + float * _x, + float * _y) +{ + __m512 v; // input vector + __m512 h; // coefficients vector + __m512 s; // dot product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/16)) + unsigned int t = (_q->n >> 4) << 4; + + // + unsigned int i; + for (i=0; ih[i]); + + // compute dot product + s = _mm512_mul_ps(v, h); + + // parallel addition + sum = _mm512_add_ps( sum, s ); + } + + // fold down into single value + float total = _mm512_reduce_add_ps(sum); + + // cleanup + for (; i<_q->n; i++) + total += _x[i] * _q->h[i]; + + // set return value + *_y = total; + return LIQUID_OK; +} + +// use AVX512-F extensions (unrolled) +int dotprod_rrrf_execute_avx512fu(dotprod_rrrf _q, + float * _x, + float * _y) +{ + __m512 v0, v1, v2, v3; + __m512 h0, h1, h2, h3; + __m512 s0, s1, s2, s3; + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/64)) + unsigned int r = (_q->n >> 6) << 4; + + // + unsigned int i; + for (i=0; ih[4*i+ 0]); + h1 = _mm512_load_ps(&_q->h[4*i+16]); + h2 = _mm512_load_ps(&_q->h[4*i+32]); + h3 = _mm512_load_ps(&_q->h[4*i+48]); + + // compute dot products + s0 = _mm512_mul_ps(v0, h0); + s1 = _mm512_mul_ps(v1, h1); + s2 = _mm512_mul_ps(v2, h2); + s3 = _mm512_mul_ps(v3, h3); + + // parallel addition + sum = _mm512_add_ps( sum, s0 ); + sum = _mm512_add_ps( sum, s1 ); + sum = _mm512_add_ps( sum, s2 ); + sum = _mm512_add_ps( sum, s3 ); + } + + // fold down into single value + float total = _mm512_reduce_add_ps(sum); + + // cleanup + for (i=4*r; i<_q->n; i++) + total += _x[i] * _q->h[i]; + + // set return value + *_y = total; + return LIQUID_OK; +} + From 15fcabc556c9b5776a0fd18b7f326d5043841bf3 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 30 Mar 2023 17:17:14 -0400 Subject: [PATCH 097/186] benchmark compare: specifying argument type as float --- scripts/benchmark_compare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/benchmark_compare.py b/scripts/benchmark_compare.py index 328e134c1..0fdc7295f 100755 --- a/scripts/benchmark_compare.py +++ b/scripts/benchmark_compare.py @@ -6,7 +6,7 @@ p = argparse.ArgumentParser(description=__doc__) p.add_argument('old', help='old benchmark file (.json)') p.add_argument('new', help='new benchmark file (.json)') -p.add_argument('-thresh', default=1.5, help='threshold for displaying deltas') +p.add_argument('-thresh', default=1.5, type=float, help='threshold for displaying deltas') args = p.parse_args() # load json files From 25a3cc7f60f5963ba82c29aef0100a7be8cdc54f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Apr 2023 16:03:03 -0400 Subject: [PATCH 098/186] fec/autotest: cleaning up warning when libfec is not installed --- src/fec/tests/fec_autotest.c | 40 +++++++++++++++---------------- src/fec/tests/fec_copy_autotest.c | 40 +++++++++++++++---------------- src/fec/tests/fec_soft_autotest.c | 40 +++++++++++++++---------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/fec/tests/fec_autotest.c b/src/fec/tests/fec_autotest.c index df1bdfffa..d03712ce1 100644 --- a/src/fec/tests/fec_autotest.c +++ b/src/fec/tests/fec_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,25 +29,25 @@ void fec_test_codec(fec_scheme _fs, unsigned int _n, void * _opts) { #if !LIBFEC_ENABLED - if ( _fs == LIQUID_FEC_CONV_V27 || - _fs == LIQUID_FEC_CONV_V29 || - _fs == LIQUID_FEC_CONV_V39 || - _fs == LIQUID_FEC_CONV_V615 || - _fs == LIQUID_FEC_CONV_V27P23 || - _fs == LIQUID_FEC_CONV_V27P34 || - _fs == LIQUID_FEC_CONV_V27P45 || - _fs == LIQUID_FEC_CONV_V27P56 || - _fs == LIQUID_FEC_CONV_V27P67 || - _fs == LIQUID_FEC_CONV_V27P78 || - _fs == LIQUID_FEC_CONV_V29P23 || - _fs == LIQUID_FEC_CONV_V29P34 || - _fs == LIQUID_FEC_CONV_V29P45 || - _fs == LIQUID_FEC_CONV_V29P56 || - _fs == LIQUID_FEC_CONV_V29P67 || - _fs == LIQUID_FEC_CONV_V29P78 || - _fs == LIQUID_FEC_RS_M8) - { - AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)\n"); + switch (_fs) { + case LIQUID_FEC_CONV_V27: + case LIQUID_FEC_CONV_V29: + case LIQUID_FEC_CONV_V39: + case LIQUID_FEC_CONV_V615: + case LIQUID_FEC_CONV_V27P23: + case LIQUID_FEC_CONV_V27P34: + case LIQUID_FEC_CONV_V27P45: + case LIQUID_FEC_CONV_V27P56: + case LIQUID_FEC_CONV_V27P67: + case LIQUID_FEC_CONV_V27P78: + case LIQUID_FEC_CONV_V29P23: + case LIQUID_FEC_CONV_V29P34: + case LIQUID_FEC_CONV_V29P45: + case LIQUID_FEC_CONV_V29P56: + case LIQUID_FEC_CONV_V29P67: + case LIQUID_FEC_CONV_V29P78: + case LIQUID_FEC_RS_M8: + AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; } #endif diff --git a/src/fec/tests/fec_copy_autotest.c b/src/fec/tests/fec_copy_autotest.c index 34344eb5b..bcb2d1ca3 100644 --- a/src/fec/tests/fec_copy_autotest.c +++ b/src/fec/tests/fec_copy_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,25 +29,25 @@ void fec_test_copy(fec_scheme _fs) { #if !LIBFEC_ENABLED - if ( _fs == LIQUID_FEC_CONV_V27 || - _fs == LIQUID_FEC_CONV_V29 || - _fs == LIQUID_FEC_CONV_V39 || - _fs == LIQUID_FEC_CONV_V615 || - _fs == LIQUID_FEC_CONV_V27P23 || - _fs == LIQUID_FEC_CONV_V27P34 || - _fs == LIQUID_FEC_CONV_V27P45 || - _fs == LIQUID_FEC_CONV_V27P56 || - _fs == LIQUID_FEC_CONV_V27P67 || - _fs == LIQUID_FEC_CONV_V27P78 || - _fs == LIQUID_FEC_CONV_V29P23 || - _fs == LIQUID_FEC_CONV_V29P34 || - _fs == LIQUID_FEC_CONV_V29P45 || - _fs == LIQUID_FEC_CONV_V29P56 || - _fs == LIQUID_FEC_CONV_V29P67 || - _fs == LIQUID_FEC_CONV_V29P78 || - _fs == LIQUID_FEC_RS_M8) - { - AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)\n"); + switch (_fs) { + case LIQUID_FEC_CONV_V27: + case LIQUID_FEC_CONV_V29: + case LIQUID_FEC_CONV_V39: + case LIQUID_FEC_CONV_V615: + case LIQUID_FEC_CONV_V27P23: + case LIQUID_FEC_CONV_V27P34: + case LIQUID_FEC_CONV_V27P45: + case LIQUID_FEC_CONV_V27P56: + case LIQUID_FEC_CONV_V27P67: + case LIQUID_FEC_CONV_V27P78: + case LIQUID_FEC_CONV_V29P23: + case LIQUID_FEC_CONV_V29P34: + case LIQUID_FEC_CONV_V29P45: + case LIQUID_FEC_CONV_V29P56: + case LIQUID_FEC_CONV_V29P67: + case LIQUID_FEC_CONV_V29P78: + case LIQUID_FEC_RS_M8: + AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; } #endif diff --git a/src/fec/tests/fec_soft_autotest.c b/src/fec/tests/fec_soft_autotest.c index 34c92803d..0ef350493 100644 --- a/src/fec/tests/fec_soft_autotest.c +++ b/src/fec/tests/fec_soft_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,25 +32,25 @@ void fec_test_soft_codec(fec_scheme _fs, void * _opts) { #if !LIBFEC_ENABLED - if ( _fs == LIQUID_FEC_CONV_V27 || - _fs == LIQUID_FEC_CONV_V29 || - _fs == LIQUID_FEC_CONV_V39 || - _fs == LIQUID_FEC_CONV_V615 || - _fs == LIQUID_FEC_CONV_V27P23 || - _fs == LIQUID_FEC_CONV_V27P34 || - _fs == LIQUID_FEC_CONV_V27P45 || - _fs == LIQUID_FEC_CONV_V27P56 || - _fs == LIQUID_FEC_CONV_V27P67 || - _fs == LIQUID_FEC_CONV_V27P78 || - _fs == LIQUID_FEC_CONV_V29P23 || - _fs == LIQUID_FEC_CONV_V29P34 || - _fs == LIQUID_FEC_CONV_V29P45 || - _fs == LIQUID_FEC_CONV_V29P56 || - _fs == LIQUID_FEC_CONV_V29P67 || - _fs == LIQUID_FEC_CONV_V29P78 || - _fs == LIQUID_FEC_RS_M8) - { - AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)\n"); + switch (_fs) { + case LIQUID_FEC_CONV_V27: + case LIQUID_FEC_CONV_V29: + case LIQUID_FEC_CONV_V39: + case LIQUID_FEC_CONV_V615: + case LIQUID_FEC_CONV_V27P23: + case LIQUID_FEC_CONV_V27P34: + case LIQUID_FEC_CONV_V27P45: + case LIQUID_FEC_CONV_V27P56: + case LIQUID_FEC_CONV_V27P67: + case LIQUID_FEC_CONV_V27P78: + case LIQUID_FEC_CONV_V29P23: + case LIQUID_FEC_CONV_V29P34: + case LIQUID_FEC_CONV_V29P45: + case LIQUID_FEC_CONV_V29P56: + case LIQUID_FEC_CONV_V29P67: + case LIQUID_FEC_CONV_V29P78: + case LIQUID_FEC_RS_M8: + AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; } #endif From 41507de1872e8c56dcd3773b5dfed634ea37ac98 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Apr 2023 16:11:48 -0400 Subject: [PATCH 099/186] build: bumping date in global header --- include/liquid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/liquid.h b/include/liquid.h index 6d65c46f1..ea16f3fb3 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From b3527e01431209b298570e395599deb6d6646b39 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 8 Feb 2023 23:03:03 -0500 Subject: [PATCH 100/186] gcd: fixing logic for stopping loop, adding more test cases --- src/math/src/modular_arithmetic.c | 2 +- src/math/tests/gcd_autotest.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/math/src/modular_arithmetic.c b/src/math/src/modular_arithmetic.c index 01d4b8a39..5146d73b5 100644 --- a/src/math/src/modular_arithmetic.c +++ b/src/math/src/modular_arithmetic.c @@ -131,7 +131,7 @@ unsigned int liquid_gcd(unsigned int _p, // dumb, slow method unsigned int gcd = 1; unsigned int r = 2; // root - while ( r*r <= _p ) { + while ( r <= _q ) { while ((_p % r)==0 && (_q % r) == 0) { _p /= r; _q /= r; diff --git a/src/math/tests/gcd_autotest.c b/src/math/tests/gcd_autotest.c index fd1fcb81f..2dfa81338 100644 --- a/src/math/tests/gcd_autotest.c +++ b/src/math/tests/gcd_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2018 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -63,5 +63,9 @@ void autotest_gcd_base() testbench_gcd( 2*2*3*5*7, 2*3, 17); testbench_gcd( 2*2*3*5*7, 2*3, 17*17); testbench_gcd( 2*2*3*5*7, 2*3, 17*17*17); + testbench_gcd( 11, 3, 1); + testbench_gcd( 3, 127, 131); + testbench_gcd( 131, 127, 3); + testbench_gcd( 127, 131, 3); } From 93e0c6008840743019701d2fe0811ced5460eac4 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 19 Feb 2023 16:53:21 -0500 Subject: [PATCH 101/186] ignoring auto-generated files from autotest --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 912b3441d..0898d51ea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.gcno *.o *.so +*~ # ignore swap files for vim editing *.swp @@ -30,6 +31,9 @@ config.status makefile xautotest *.m +autotest.json +autotest/logs/*.bin +autotest/logs/*.gnu # miscellany octave-core From 8ab1740f990551aa2a0be0d61a368b54558f3d83 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 22 Feb 2023 19:22:37 -0500 Subject: [PATCH 102/186] qdsync/autotest: adding test to periodically change callback buf len --- src/framing/tests/qdsync_cccf_autotest.c | 57 ++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index c01555d9c..f6a8fc5ea 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -151,6 +151,63 @@ void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } +// test different configurations +void autotest_qdsync_config() +{ + // options + unsigned int seq_len = 2400; // total number of sync symbols + unsigned int k = 2; // samples/symbol + unsigned int m = 12; // filter delay [symbols] + float beta = 0.3f; // excess bandwidth factor + int ftype = LIQUID_FIRFILT_ARKAISER; + + // generate synchronization sequence (QPSK symbols) + float complex seq_tx[seq_len]; // transmitted + float complex seq_rx[seq_len]; // received with initial correction + unsigned int i; + for (i=0; i Date: Thu, 23 Feb 2023 08:07:05 -0500 Subject: [PATCH 103/186] qdsync/autotest: disabling test to set buf len temporarily --- src/framing/tests/qdsync_cccf_autotest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index f6a8fc5ea..a067b026c 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -151,8 +151,8 @@ void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } -// test different configurations -void autotest_qdsync_config() +// test setting buffer length ot different sizes throughout run +void xautotest_qdsync_set_buf_len() { // options unsigned int seq_len = 2400; // total number of sync symbols From 9b74c1cccf80fba87ad21ad458c9ee9e094ba7db Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 14:16:56 +0000 Subject: [PATCH 104/186] qdsync: adjusting logic for resizing buffer to fix memory errors --- src/framing/src/qdsync_cccf.c | 19 +++++++++++++------ src/framing/tests/qdsync_cccf_autotest.c | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 1469b4f9c..2dcf01fbf 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -229,7 +229,10 @@ int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) // buffer might not be empty, but we aren't resizing within this space; // ok to resize so long as old samples are copied _q->buf_out_len = _buf_len; - _q->buf_out = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + if (buf_new == NULL) + return liquid_error(LIQUID_EIMEM,"qdsync_cccf_set_buf_len(), could not allocate %u samples", _buf_len); + _q->buf_out = buf_new; } else { // we are shrinking the buffer below the number of samples it currently // holds; invoke the callback as many times as needed to reduce its size @@ -242,11 +245,15 @@ int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) index += _buf_len; _q->buf_out_counter -= _buf_len; } - // now perform the reallocation, but we cannot use 'realloc' here because - // we are not copying values at the front of the buffer - float complex * buf_new = (float complex*)malloc(_buf_len * sizeof(float complex)); - memmove(buf_new, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); - free(_q->buf_out); + + // copy old values to front of buffer + memmove(_q->buf_out, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); + + // now resize the buffer appropriately + _q->buf_out_len = _buf_len; + float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + if (buf_new == NULL) + return liquid_error(LIQUID_EIMEM,"qdsync_cccf_set_buf_len(), could not allocate %u samples", _buf_len); _q->buf_out = buf_new; } return LIQUID_OK; diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index a067b026c..1af01ec9c 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -152,7 +152,7 @@ void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } // test setting buffer length ot different sizes throughout run -void xautotest_qdsync_set_buf_len() +void autotest_qdsync_set_buf_len() { // options unsigned int seq_len = 2400; // total number of sync symbols From ee962b4f54fd7218e2a0d16ffed96739699f743b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 11:42:32 -0500 Subject: [PATCH 105/186] qdsync/autotest: adding config() test, extending coverage of existing --- src/framing/tests/qdsync_cccf_autotest.c | 66 ++++++++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/src/framing/tests/qdsync_cccf_autotest.c b/src/framing/tests/qdsync_cccf_autotest.c index 1af01ec9c..1e1c59eaf 100644 --- a/src/framing/tests/qdsync_cccf_autotest.c +++ b/src/framing/tests/qdsync_cccf_autotest.c @@ -65,6 +65,7 @@ void testbench_qdsync_linear(unsigned int _k, float beta = _beta; // excess bandwidth factor int ftype = LIQUID_FIRFILT_ARKAISER; float nstd = 0.001f; + float tau = 0.400f; // fractional sample timing offset // generate synchronization sequence (QPSK symbols) float complex seq_tx[seq_len]; // transmitted @@ -86,7 +87,7 @@ void testbench_qdsync_linear(unsigned int _k, // create delay object fdelay_crcf delay = fdelay_crcf_create_default(100); - fdelay_crcf_set_delay(delay, 10*k - 0.4); + fdelay_crcf_set_delay(delay, 10*k + tau); // run signal through sync object float complex buf[k]; @@ -108,8 +109,11 @@ void testbench_qdsync_linear(unsigned int _k, // run through synchronizer qdsync_cccf_execute(q, buf, k); } - float dphi_hat = qdsync_cccf_get_dphi(q); - float phi_hat = qdsync_cccf_get_phi(q); + float rxy_hat = qdsync_cccf_get_rxy (q); + float tau_hat = qdsync_cccf_get_tau (q); + float gamma_hat= qdsync_cccf_get_gamma(q); + float dphi_hat = qdsync_cccf_get_dphi (q); + float phi_hat = qdsync_cccf_get_phi (q); // compute error in terms of offset from unity; might be residual carrier phase/gain // TODO: perform residual carrier/phase error correction? @@ -119,11 +123,16 @@ void testbench_qdsync_linear(unsigned int _k, rmse += e*e; } rmse = 10*log10f( rmse / (float)seq_len ); - if (liquid_autotest_verbose) - printf("qdsync: dphi: %12.4e, phi: %12.8f, rmse: %12.3f\n", dphi_hat, phi_hat, rmse); - CONTEND_LESS_THAN( rmse, -30.0f ) - CONTEND_LESS_THAN( fabsf(dphi_hat), 1e-3f ) - CONTEND_LESS_THAN( fabsf( phi_hat), 0.4f ) + if (liquid_autotest_verbose) { + printf("qdsync: rxy:%5.3f, tau:%5.2f, gamma:%5.3f, dphi:%12.4e, phi:%8.5f, rmse:%5.2f\n", + rxy_hat, tau_hat, gamma_hat, dphi_hat, phi_hat, rmse); + } + CONTEND_LESS_THAN ( rmse, -30.0f ) + CONTEND_GREATER_THAN( rxy_hat, 0.75f ) + CONTEND_LESS_THAN ( fabsf(tau_hat-tau), 0.10f ) + CONTEND_GREATER_THAN( gamma_hat, 0.75f ) + CONTEND_LESS_THAN ( fabsf(dphi_hat), 1e-3f ) + CONTEND_LESS_THAN ( fabsf( phi_hat), 0.4f ) // clean up objects qdsync_cccf_destroy(q); @@ -147,9 +156,9 @@ void testbench_qdsync_linear(unsigned int _k, } // test specific configurations -void autotest_qdsync_k2() { testbench_qdsync_linear(2, 7, 0.3f); } -void autotest_qdsync_k3() { testbench_qdsync_linear(3, 7, 0.3f); } -void autotest_qdsync_k4() { testbench_qdsync_linear(4, 7, 0.3f); } +void autotest_qdsync_cccf_k2() { testbench_qdsync_linear(2, 7, 0.3f); } +void autotest_qdsync_cccf_k3() { testbench_qdsync_linear(3, 7, 0.3f); } +void autotest_qdsync_cccf_k4() { testbench_qdsync_linear(4, 7, 0.3f); } // test setting buffer length ot different sizes throughout run void autotest_qdsync_set_buf_len() @@ -291,3 +300,38 @@ void autotest_qdsync_cccf_copy() qdsync_cccf_destroy(q_copy); } +void autotest_qdsync_cccf_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping qdsync_cccf config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(qdsync_cccf_copy(NULL)); + CONTEND_ISNULL(qdsync_cccf_create_linear(NULL,0,LIQUID_FIRFILT_ARKAISER,4,12,0.25f,NULL,NULL)); + + // create proper object and test configurations + float complex seq[] = {+1,-1,+1,-1,-1,+1,-1,+1,-1,+1,-1,+1,+1,+1,-1,+1,-1,-1,-1,-1,}; + qdsync_cccf q = qdsync_cccf_create_linear(seq,20,LIQUID_FIRFILT_ARKAISER,4,12,0.25f,NULL,NULL); + + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_print(q)) + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_set_callback(q,autotest_qdsync_callback)) + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_set_context(q,NULL)) + + // set/get threshold + CONTEND_EQUALITY(LIQUID_OK, qdsync_cccf_set_threshold(q,0.654321f)) + CONTEND_EQUALITY(0.654321f, qdsync_cccf_get_threshold(q)) + + // set invalid buffer length + CONTEND_INEQUALITY(LIQUID_OK, qdsync_cccf_set_buf_len(q,0)) + + // query properties + CONTEND_EQUALITY(0, qdsync_cccf_is_open(q)) + + // destroy object + qdsync_cccf_destroy(q); +} + From 2e496aa80ff87f8b351b0968d9f3e3458eac8229 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 15:22:19 -0500 Subject: [PATCH 106/186] gasearch: adding (basic) autotest on simple toy problem --- examples/gasearch_example.c | 112 ++++++++-------------------- makefile.in | 1 + src/optim/tests/gasearch_autotest.c | 96 ++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 79 deletions(-) create mode 100644 src/optim/tests/gasearch_autotest.c diff --git a/examples/gasearch_example.c b/examples/gasearch_example.c index 677bb3fba..ddc903651 100644 --- a/examples/gasearch_example.c +++ b/examples/gasearch_example.c @@ -1,7 +1,4 @@ -// -// gasearch_example.c -// - +// example demonstrating performance of GA search algorithm for finding basic function peak #include #include #include @@ -10,57 +7,59 @@ #define OUTPUT_FILENAME "gasearch_example.m" -// utility callback function -float utility_callback(void * _userdata, chromosome _c); - -// peak callback function; value nearest {0.5, 0.5, 0.5, ...} -float peak_callback(void * _userdata, chromosome _c); - -// rosenbrock callback function -float rosenbrock_callback(void * _userdata, chromosome _c); +// peak callback function; value nearest {p, p, p, ...} where p = 1/sqrt(2) +float peak_callback(void * _userdata, chromosome _c) +{ + unsigned int i, n = chromosome_get_num_traits(_c); + float u_global = 1.0f; + float sig = 0.2f; + float p = M_SQRT1_2; + for (i=0; i +#include +#include +#include +#include + +#include "liquid.internal.h" + +// peak callback function; value nearest {p, p, p, ...} where p = 1/sqrt(2) +float gasearch_autotest_peak_callback(void * _userdata, chromosome _c) +{ + unsigned int i, n = chromosome_get_num_traits(_c); + float u = 1.0f; + float sig = 0.2f; + float p = M_SQRT1_2; + for (i=0; i Date: Sat, 25 Feb 2023 16:37:57 -0500 Subject: [PATCH 107/186] gasearch/autotest: adding configuration tests --- src/optim/src/chromosome.c | 46 ++++++------ src/optim/src/gasearch.c | 12 +++- src/optim/tests/gasearch_autotest.c | 105 +++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 23 deletions(-) diff --git a/src/optim/src/chromosome.c b/src/optim/src/chromosome.c index 11dcc639b..ea1147a52 100644 --- a/src/optim/src/chromosome.c +++ b/src/optim/src/chromosome.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,32 +37,33 @@ // _bits_per_trait : array of bits/trait [size: _num_traits x 1] // _num_traits : number of traits in this chromosome chromosome chromosome_create(unsigned int * _bits_per_trait, - unsigned int _num_traits) + unsigned int _num_traits) { + // validate input + unsigned int i; + if (_num_traits < 1) + return liquid_error_config("chromosome_create(), must have at least one trait"); + for (i=0; i<_num_traits; i++) { + if (_bits_per_trait[i] > LIQUID_CHROMOSOME_MAX_SIZE) + return liquid_error_config("chromosome_create(), bits/trait cannot exceed %u", LIQUID_CHROMOSOME_MAX_SIZE); + } + chromosome q; q = (chromosome) malloc( sizeof(struct chromosome_s) ); q->num_traits = _num_traits; - // validate input - if (q->num_traits < 1) - return liquid_error_config("chromosome_create(), must have at least one trait"); - // initialize internal arrays q->bits_per_trait = (unsigned int *) malloc(q->num_traits*sizeof(unsigned int)); q->max_value = (unsigned long*) malloc(q->num_traits*sizeof(unsigned long)); q->traits = (unsigned long*) malloc(q->num_traits*sizeof(unsigned long)); // copy/initialize values - unsigned int i; q->num_bits = 0; for (i=0; inum_traits; i++) { q->bits_per_trait[i] = _bits_per_trait[i]; - if (q->bits_per_trait[i] > LIQUID_CHROMOSOME_MAX_SIZE) - return liquid_error_config("chromosome_create(), bits/trait cannot exceed %u", LIQUID_CHROMOSOME_MAX_SIZE); - - q->max_value[i] = 1 << q->bits_per_trait[i]; - q->traits[i] = 0; + q->max_value[i] = 1LU << q->bits_per_trait[i]; + q->traits[i] = 0LU; q->num_bits += q->bits_per_trait[i]; } @@ -79,6 +80,8 @@ chromosome chromosome_create_basic(unsigned int _num_traits, // validate input if (_num_traits == 0) return liquid_error_config("chromosome_create_basic(), must have at least one trait"); + if (_bits_per_trait == 0 || _bits_per_trait > 64) + return liquid_error_config("chromosome_create_basic(), bits per trait out of range"); unsigned int * bpt = (unsigned int *) malloc(_num_traits*sizeof(unsigned int)); unsigned int i; @@ -175,6 +178,7 @@ int chromosome_init(chromosome _c, { unsigned int i; for (i=0; i<_c->num_traits; i++) { + //printf("===> [%3u] bits:%3u, max:%12lu, value:%12lu\n", i, _c->bits_per_trait[i], _c->max_value[i], _v[i]); if (_v[i] >= _c->max_value[i]) return liquid_error(LIQUID_EIRANGE,"chromosome_init(), value exceeds maximum"); @@ -189,12 +193,14 @@ int chromosome_initf(chromosome _c, { unsigned int i; for (i=0; i<_c->num_traits; i++) { - if (_v[i] > 1.0f || _v[i] < 0.0f) + if (_v[i] < 0.0f || _v[i] > 1.0f) return liquid_error(LIQUID_EIRANGE,"chromosome_initf(), value must be in [0,1]"); // quantize sample - unsigned int N = 1 << _c->bits_per_trait[i]; - _c->traits[i] = (unsigned int) floorf( _v[i] * N ); + unsigned long N = 1LU << _c->bits_per_trait[i]; + _c->traits[i] = (unsigned long) floorf( _v[i] * N ); + //printf("===> [%3u] quantizing %8.2f, bits:%3u, N:%12lu, trait:%12lu/%12lu => %12.8f\n", + // i, _v[i], _c->bits_per_trait[i], N, _c->traits[i], _c->max_value[i], chromosome_valuef(_c,i)); } return LIQUID_OK; } @@ -212,10 +218,10 @@ int chromosome_mutate(chromosome _q, for (i=0; i<_q->num_traits; i++) { unsigned int b = _q->bits_per_trait[i]; if (t == _index) { - _q->traits[i] ^= (unsigned long)(1 << (b-1)); + _q->traits[i] ^= (unsigned long)(1LU << (b-1)); return LIQUID_OK; } else if (t > _index) { - _q->traits[i-1] ^= (unsigned long)(1 << (t-_index-1)); + _q->traits[i-1] ^= (unsigned long)(1LU << (t-_index-1)); return LIQUID_OK; } else { t += b; @@ -289,18 +295,18 @@ int chromosome_init_random(chromosome _q) { unsigned int i; for (i=0; i<_q->num_traits; i++) - _q->traits[i] = rand() & (_q->max_value[i]-1); + _q->traits[i] = rand() & (_q->max_value[i]-1LU); return LIQUID_OK; } -float chromosome_valuef(chromosome _q, +float chromosome_valuef(chromosome _q, unsigned int _index) { if (_index > _q->num_traits) { liquid_error(LIQUID_EIRANGE,"chromosome_valuef(), trait index exceeded"); return 0.0f; } - return (float) (_q->traits[_index]) / (float)(_q->max_value[_index] - 1); + return (float) (_q->traits[_index]) / (float)(_q->max_value[_index]-1LU); } unsigned int chromosome_value(chromosome _q, diff --git a/src/optim/src/gasearch.c b/src/optim/src/gasearch.c index 9ab3fb7ee..e33d190bd 100644 --- a/src/optim/src/gasearch.c +++ b/src/optim/src/gasearch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -66,8 +66,14 @@ gasearch gasearch_create_advanced(gasearch_utility _utility, gasearch ga; ga = (gasearch) malloc( sizeof(struct gasearch_s) ); + if (_utility == NULL) + return liquid_error_config("gasearch_create(), utility function cannot be NULL") + if (_parent == NULL) + return liquid_error_config("gasearch_create(), parent cannot be NULL") + if (_population_size < 2) + return liquid_error_config("gasearch_create(), population size exceeds minimum"); if (_population_size > LIQUID_GA_SEARCH_MAX_POPULATION_SIZE) - return liquid_error_config("gasearch_create(), population size exceeds maximum"); + return liquid_error_config("gasearch_create(), population size exceeds maximum (%u)",LIQUID_GA_SEARCH_MAX_POPULATION_SIZE); if (_mutation_rate < 0.0f || _mutation_rate > 1.0f) return liquid_error_config("gasearch_create(), mutation rate must be in [0,1]"); @@ -162,6 +168,8 @@ int gasearch_set_population_size(gasearch _g, // validate input if (_population_size < 2) return liquid_error(LIQUID_EICONFIG,"gasearch_set_population_size(), population must be at least 2"); + if (_population_size > LIQUID_GA_SEARCH_MAX_POPULATION_SIZE) + return liquid_error(LIQUID_EICONFIG,"gasearch_set_population_size(), population exceeds maximum (%u)",LIQUID_GA_SEARCH_MAX_POPULATION_SIZE); if (_selection_size == 0) return liquid_error(LIQUID_EICONFIG,"gasearch_set_population_size(), selection size must be greater than zero"); if (_selection_size >= _population_size) diff --git a/src/optim/tests/gasearch_autotest.c b/src/optim/tests/gasearch_autotest.c index e95afef36..77da5f9f3 100644 --- a/src/optim/tests/gasearch_autotest.c +++ b/src/optim/tests/gasearch_autotest.c @@ -82,7 +82,7 @@ void autotest_gasearch_peak() chromosome_printf(prototype); } - // destroy gradient descent search object + // destroy search object chromosome_destroy(prototype); gasearch_destroy(ga); @@ -94,3 +94,106 @@ void autotest_gasearch_peak() CONTEND_GREATER_THAN( optimum_utility, 0.70f ) } +// test chromosome configuration +void autotest_chromosome_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping chromosome config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // test chromosome + unsigned int bits_per_trait_invalid[8] = {6,6,6,6,6,6,6,1000}; + unsigned int bits_per_trait_valid [8] = {6,6,6,6,6,6,6, 32}; + CONTEND_ISNULL(chromosome_create(bits_per_trait_invalid, 8)) + CONTEND_ISNULL(chromosome_create(bits_per_trait_valid, 0)) + CONTEND_ISNULL(chromosome_create_basic(0, 12)) // too few traits + CONTEND_ISNULL(chromosome_create_basic(8, 0)) // bits per trait too small + CONTEND_ISNULL(chromosome_create_basic(8, 99)) // bits per trait too large + + // create prototype chromosome using basic method + chromosome prototype = chromosome_create_basic(20, 5); + CONTEND_EQUALITY(LIQUID_OK, chromosome_print(prototype)) + chromosome_destroy(prototype); + + // create prototype chromosome using more specific method + prototype = chromosome_create(bits_per_trait_valid, 8); + CONTEND_EQUALITY(LIQUID_OK, chromosome_print(prototype)) + + // test initialization + unsigned int values_invalid[] = {999,12,11,13,63,17, 3,123456789}; // invalid because first trait is only 6 bits + unsigned int values_valid [] = { 0,12,11,13,63,17, 3,123456789}; + CONTEND_INEQUALITY(LIQUID_OK, chromosome_init (prototype, values_invalid)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_init (prototype, values_valid )) + + // check individual values + CONTEND_EQUALITY( chromosome_value(prototype, 0), 0) + CONTEND_EQUALITY( chromosome_value(prototype, 1), 12) + CONTEND_EQUALITY( chromosome_value(prototype, 2), 11) + CONTEND_EQUALITY( chromosome_value(prototype, 3), 13) + CONTEND_EQUALITY( chromosome_value(prototype, 4), 63) + CONTEND_EQUALITY( chromosome_value(prototype, 5), 17) + CONTEND_EQUALITY( chromosome_value(prototype, 6), 3) + CONTEND_EQUALITY( chromosome_value(prototype, 7), 123456789) + + // test initialization (float values) + float valuesf_invalid[] = {0.0,0.1,0.2,0.3,0.4,0.5,0.6,999,}; + float valuesf_valid [] = {0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,}; + CONTEND_INEQUALITY(LIQUID_OK, chromosome_initf(prototype, valuesf_invalid)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_initf(prototype, valuesf_valid )) + + // check individual values + CONTEND_DELTA( chromosome_valuef(prototype, 0), 0.0f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 1), 0.1f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 2), 0.2f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 3), 0.3f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 4), 0.4f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 5), 0.5f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 6), 0.6f, 0.02f ) + CONTEND_DELTA( chromosome_valuef(prototype, 7), 0.7f, 0.02f ) + + // destroy objects + chromosome_destroy(prototype); +} + +// test configuration +void autotest_gasearch_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping gasearch config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // create prototype chromosome + chromosome prototype = chromosome_create_basic(8, 12); + + // check invalid function calls + CONTEND_ISNULL(gasearch_create_advanced( NULL, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, 16, 0.1f)) // bad utility function + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, NULL, LIQUID_OPTIM_MAXIMIZE, 0, 0.1f)) // bad parent chromosome + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, 0, 0.1f)) // bad population size + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, -1, 0.1f)) // bad population size + CONTEND_ISNULL(gasearch_create_advanced(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE, 16,-1.0f)) // bad mutation rate + + // create proper object and test configurations + gasearch ga = gasearch_create(gasearch_autotest_peak_callback, NULL, prototype, LIQUID_OPTIM_MAXIMIZE); + CONTEND_EQUALITY(LIQUID_OK, gasearch_print(ga)) + + // test configurations + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga, 0, 8)) // population size too small + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga,-1, 8)) // population size too large + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga,24, 0)) // selection size too small + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_population_size(ga,24,24)) // selection size too large + CONTEND_EQUALITY (LIQUID_OK, gasearch_set_population_size(ga,24,12)) // ok + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_mutation_rate (ga,-1.0f)) // mutation rate out of range + CONTEND_INEQUALITY(LIQUID_OK, gasearch_set_mutation_rate (ga, 2.0f)) // mutation rate out of range + CONTEND_EQUALITY (LIQUID_OK, gasearch_set_mutation_rate (ga, 0.1f)) // ok + + // destroy objects + chromosome_destroy(prototype); + gasearch_destroy(ga); +} + From c9ef699261c50bdcbc105776461be113954f2323 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 16:46:13 -0500 Subject: [PATCH 108/186] chromosome/autotest: testing more edge cases in configuration --- src/optim/tests/gasearch_autotest.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/optim/tests/gasearch_autotest.c b/src/optim/tests/gasearch_autotest.c index 77da5f9f3..77ba6e2f8 100644 --- a/src/optim/tests/gasearch_autotest.c +++ b/src/optim/tests/gasearch_autotest.c @@ -120,13 +120,18 @@ void autotest_chromosome_config() // create prototype chromosome using more specific method prototype = chromosome_create(bits_per_trait_valid, 8); - CONTEND_EQUALITY(LIQUID_OK, chromosome_print(prototype)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_print (prototype)) + CONTEND_EQUALITY (LIQUID_OK, chromosome_reset (prototype)) // test initialization unsigned int values_invalid[] = {999,12,11,13,63,17, 3,123456789}; // invalid because first trait is only 6 bits unsigned int values_valid [] = { 0,12,11,13,63,17, 3,123456789}; CONTEND_INEQUALITY(LIQUID_OK, chromosome_init (prototype, values_invalid)) CONTEND_EQUALITY (LIQUID_OK, chromosome_init (prototype, values_valid )) + CONTEND_EQUALITY ( 0, chromosome_value (prototype,999)) + CONTEND_EQUALITY ( 0.0f, chromosome_valuef (prototype,999)) + CONTEND_INEQUALITY(LIQUID_OK, chromosome_mutate (prototype,999)) + CONTEND_INEQUALITY(LIQUID_OK, chromosome_crossover(prototype,prototype,prototype,999)) // check individual values CONTEND_EQUALITY( chromosome_value(prototype, 0), 0) From 01a78ebecf32d94240693f268d038cbd84c46011 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 18:33:59 -0500 Subject: [PATCH 109/186] qnsearch: adding basic autotest script --- makefile.in | 1 + src/optim/src/qnsearch.c | 6 +-- src/optim/tests/qnsearch_autotest.c | 63 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/optim/tests/qnsearch_autotest.c diff --git a/makefile.in b/makefile.in index 4b8787448..555c90508 100644 --- a/makefile.in +++ b/makefile.in @@ -1014,6 +1014,7 @@ $(optim_objects) : %.o : %.c $(include_headers) optim_autotests := \ src/optim/tests/gasearch_autotest.c \ src/optim/tests/gradsearch_autotest.c \ + src/optim/tests/qnsearch_autotest.c \ src/optim/tests/qs1dsearch_autotest.c \ # benchmarks diff --git a/src/optim/src/qnsearch.c b/src/optim/src/qnsearch.c index 168ba3162..bf9260df2 100644 --- a/src/optim/src/qnsearch.c +++ b/src/optim/src/qnsearch.c @@ -192,9 +192,9 @@ int qnsearch_step(qnsearch _q) return LIQUID_OK; } -float qnsearch_run(qnsearch _q, - unsigned int _max_iterations, - float _target_utility) +float qnsearch_execute(qnsearch _q, + unsigned int _max_iterations, + float _target_utility) { unsigned int i=0; do { diff --git a/src/optim/tests/qnsearch_autotest.c b/src/optim/tests/qnsearch_autotest.c new file mode 100644 index 000000000..449413a3e --- /dev/null +++ b/src/optim/tests/qnsearch_autotest.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "autotest/autotest.h" +#include "liquid.h" + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +void autotest_qnsearch_rosenbrock() +{ + float tol = 1e-2f; // error tolerance + unsigned int num_parameters = 6; // dimensionality of search (minimum 2) + unsigned int num_iterations = 4000; // number of iterations to run + + // initialize vector for optimization + float v_opt[num_parameters]; + unsigned int i; + for (i=0; i Date: Sat, 25 Feb 2023 18:54:50 -0500 Subject: [PATCH 110/186] gasearch: validating input before allocate memory in create() --- src/optim/src/gasearch.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/optim/src/gasearch.c b/src/optim/src/gasearch.c index e33d190bd..a846aabdf 100644 --- a/src/optim/src/gasearch.c +++ b/src/optim/src/gasearch.c @@ -63,9 +63,7 @@ gasearch gasearch_create_advanced(gasearch_utility _utility, unsigned int _population_size, float _mutation_rate) { - gasearch ga; - ga = (gasearch) malloc( sizeof(struct gasearch_s) ); - + // validate input if (_utility == NULL) return liquid_error_config("gasearch_create(), utility function cannot be NULL") if (_parent == NULL) @@ -77,7 +75,8 @@ gasearch gasearch_create_advanced(gasearch_utility _utility, if (_mutation_rate < 0.0f || _mutation_rate > 1.0f) return liquid_error_config("gasearch_create(), mutation rate must be in [0,1]"); - // initialize public values + // create object and initialize values + gasearch ga = (gasearch) malloc( sizeof(struct gasearch_s) ); ga->userdata = _userdata; ga->num_parameters = _parent->num_traits; ga->population_size = _population_size; From 9aa1d2913d52a9f5a1c031455aa178ed1bbc6d8d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 18:59:05 -0500 Subject: [PATCH 111/186] qnsearch: adding config() tests --- src/optim/src/qnsearch.c | 26 +++++++------------------- src/optim/tests/qnsearch_autotest.c | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/optim/src/qnsearch.c b/src/optim/src/qnsearch.c index bf9260df2..61bef0556 100644 --- a/src/optim/src/qnsearch.c +++ b/src/optim/src/qnsearch.c @@ -72,9 +72,14 @@ qnsearch qnsearch_create(void * _userdata, utility_function _u, int _minmax) { - qnsearch q = (qnsearch) malloc( sizeof(struct qnsearch_s) ); + // validate input + if (_u == NULL) + return liquid_error_config("qnsearch_create(), utility function cannot be NULL") + if (_num_parameters == 0) + return liquid_error_config("qnsearch_create(), number of parameters must be greater than zero"); - // initialize public values + // create object and initialize public values + qnsearch q = (qnsearch) malloc( sizeof(struct qnsearch_s) ); q->delta = 1e-6f; //_delta; q->gamma = 1e-3f; //_gamma; q->dgamma = 0.99f; @@ -146,7 +151,6 @@ int qnsearch_step(qnsearch _q) // compute normalized gradient vector qnsearch_compute_gradient(_q); - //qnsearch_normalize_gradient(_q); // TODO : perform line search to find optimal gamma @@ -231,22 +235,6 @@ int qnsearch_compute_gradient(qnsearch _q) return LIQUID_OK; } -// normalize gradient vector to unity -int qnsearch_normalize_gradient(qnsearch _q) -{ - // normalize gradient - float sig = 0.0f; - unsigned int i; - for (i=0; i<_q->num_parameters; i++) - sig += _q->gradient[i] * _q->gradient[i]; - - sig = 1.0f / sqrtf(sig/(float)(_q->num_parameters)); - - for (i=0; i<_q->num_parameters; i++) - _q->gradient[i] *= sig; - return LIQUID_OK; -} - // compute Hessian int qnsearch_compute_Hessian(qnsearch _q) { diff --git a/src/optim/tests/qnsearch_autotest.c b/src/optim/tests/qnsearch_autotest.c index 449413a3e..1a027c5be 100644 --- a/src/optim/tests/qnsearch_autotest.c +++ b/src/optim/tests/qnsearch_autotest.c @@ -59,5 +59,30 @@ void autotest_qnsearch_rosenbrock() // test value of utility (should be nearly 0) CONTEND_DELTA( liquid_rosenbrock(NULL, v_opt, num_parameters), 0.0f, tol ); + CONTEND_LESS_THAN( u_opt, tol ); +} + +// test configuration +void autotest_qnsearch_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping qnsearch config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + + // test configurations + float v[8] = {0,0,0,0,0,0,0,0}; + CONTEND_ISNULL(qnsearch_create(NULL, v, 0, liquid_rosenbrock, LIQUID_OPTIM_MINIMIZE)) // no parameters + CONTEND_ISNULL(qnsearch_create(NULL, v, 8, NULL, LIQUID_OPTIM_MINIMIZE)) // utility is null + + // create proper object and test configurations + qnsearch q = qnsearch_create(NULL, v, 8, liquid_rosenbrock, LIQUID_OPTIM_MINIMIZE); + CONTEND_EQUALITY(LIQUID_OK, qnsearch_print(q)) + + // destroy objects + qnsearch_destroy(q); } From 7645b1faba4dfb4893665066500cfa1b2eba3107 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 20:54:04 -0500 Subject: [PATCH 112/186] qs1dsearch/autotest: adding config test --- src/optim/tests/qs1dsearch_autotest.c | 32 ++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/optim/tests/qs1dsearch_autotest.c b/src/optim/tests/qs1dsearch_autotest.c index e293788d8..bcfc41326 100644 --- a/src/optim/tests/qs1dsearch_autotest.c +++ b/src/optim/tests/qs1dsearch_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -105,3 +105,33 @@ void autotest_qs1dsearch_max_11() { test_qs1dsearch(qs1dsearch_umax, 0, -20, 15, void autotest_qs1dsearch_max_12() { test_qs1dsearch(qs1dsearch_umax, 0, -10, 15, 1, LIQUID_OPTIM_MAXIMIZE); } void autotest_qs1dsearch_max_13() { test_qs1dsearch(qs1dsearch_umax, 0, -.1, 15, 1, LIQUID_OPTIM_MAXIMIZE); } +// test configuration +void autotest_qs1dsearch_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping qs1dsearch config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(qs1dsearch_create(NULL, NULL, LIQUID_OPTIM_MAXIMIZE)) // utility is NULL + CONTEND_ISNULL(qs1dsearch_copy (NULL)) + + // create proper object and test configurations + float v_opt = 0; + qs1dsearch q = qs1dsearch_create(qs1dsearch_umax, &v_opt, LIQUID_OPTIM_MAXIMIZE); + CONTEND_EQUALITY(LIQUID_OK, qs1dsearch_print(q)) + + // test configurations + CONTEND_INEQUALITY(LIQUID_OK, qs1dsearch_step(q)) // object not yet initialized + qs1dsearch_init(q, 20); + + CONTEND_EQUALITY(LIQUID_OK, qs1dsearch_execute(q)) + CONTEND_EQUALITY( 0, qs1dsearch_get_num_steps(q)) + + // destroy objects + qs1dsearch_destroy(q); +} + From 2ae6966d60ed0a6af84588f61e3b7bc80247ce4d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Feb 2023 21:03:42 -0500 Subject: [PATCH 113/186] optim/autotest: adding utility tests --- makefile.in | 1 + src/optim/tests/utility_autotest.c | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/optim/tests/utility_autotest.c diff --git a/makefile.in b/makefile.in index 555c90508..3fbf73e16 100644 --- a/makefile.in +++ b/makefile.in @@ -1016,6 +1016,7 @@ optim_autotests := \ src/optim/tests/gradsearch_autotest.c \ src/optim/tests/qnsearch_autotest.c \ src/optim/tests/qs1dsearch_autotest.c \ + src/optim/tests/utility_autotest.c \ # benchmarks optim_benchmarks := diff --git a/src/optim/tests/utility_autotest.c b/src/optim/tests/utility_autotest.c new file mode 100644 index 000000000..05ef49670 --- /dev/null +++ b/src/optim/tests/utility_autotest.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "autotest/autotest.h" +#include "liquid.h" + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +void autotest_optim_rosenbrock() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping rosenbrock config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + + // optimum + float v_ones[8] = {1,1,1,1,1,1,1,1}; + CONTEND_DELTA( liquid_rosenbrock(NULL, v_ones, 8), 0.0f, 1e-6f ) + CONTEND_DELTA( liquid_rosenbrock(NULL, v_ones, 1), 0.0f, 1e-6f ) + + // very far from optimum + float v_misc[8] = {0.3, 1.0, 4.5,-2.2, 6.7,-0.2, 1.1,-0.9,}; + CONTEND_GREATER_THAN( liquid_rosenbrock(NULL, v_misc, 8), 1000.0f ) + + // invalid configuration + CONTEND_EQUALITY( liquid_rosenbrock(NULL, v_misc, 0), 0.0f ) +} + From 5f7c3c8627aa36e1a422b383f141ab60c99d7377 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 26 Feb 2023 08:21:52 -0500 Subject: [PATCH 114/186] fskframesync: adding (very) basic autotest * working copy until framing structure gets better definition * tests only that frame is recovered * should provide estimates of carrier offset, SNR, etc. * needs to support different payload lengths, coding rates, etc. --- examples/fskframesync_example.c | 5 - makefile.in | 1 + src/framing/src/fskframegen.c | 6 +- src/framing/src/fskframesync.c | 20 ++-- src/framing/tests/fskframesync_autotest.c | 113 ++++++++++++++++++++++ 5 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 src/framing/tests/fskframesync_autotest.c diff --git a/examples/fskframesync_example.c b/examples/fskframesync_example.c index c50c49b9f..52445cf0b 100644 --- a/examples/fskframesync_example.c +++ b/examples/fskframesync_example.c @@ -1,13 +1,8 @@ -// -// fskframesync_example.c -// // This example demonstrates the interfaces to the fskframegen and // fskframesync objects used to completely encapsulate data for // over-the-air transmission. // // SEE ALSO: flexframesync_example.c -// - #include #include #include diff --git a/makefile.in b/makefile.in index 3fbf73e16..71e5b16f1 100644 --- a/makefile.in +++ b/makefile.in @@ -694,6 +694,7 @@ framing_autotests := \ src/framing/tests/dsssframesync_autotest.c \ src/framing/tests/flexframesync_autotest.c \ src/framing/tests/framesync64_autotest.c \ + src/framing/tests/fskframesync_autotest.c \ src/framing/tests/gmskframe_autotest.c \ src/framing/tests/msource_autotest.c \ src/framing/tests/ofdmflexframe_autotest.c \ diff --git a/src/framing/src/fskframegen.c b/src/framing/src/fskframegen.c index 8aac7cf34..90557e53c 100644 --- a/src/framing/src/fskframegen.c +++ b/src/framing/src/fskframegen.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -321,7 +321,7 @@ int fskframegen_assemble(fskframegen _q, // encode payload symbols qpacketmodem_encode_syms(_q->payload_encoder, _payload, _q->payload_sym); -#if 1 +#if 0 printf("tx payload symbols (%u)\n", _q->payload_sym_len); unsigned int i; for (i=0; i<_q->payload_sym_len; i++) @@ -404,7 +404,7 @@ int fskframegen_encode_header(fskframegen _q, // run packet encoder, encoding into symbols qpacketmodem_encode_syms(_q->header_encoder, _q->header_dec, _q->header_sym); -#if 1 +#if 0 printf("tx header symbols (%u):\n", _q->header_sym_len); unsigned int i; for (i=0; i<_q->header_sym_len; i++) diff --git a/src/framing/src/fskframesync.c b/src/framing/src/fskframesync.c index 47ec916a5..f68c4cf4c 100644 --- a/src/framing/src/fskframesync.c +++ b/src/framing/src/fskframesync.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -456,14 +456,14 @@ int fskframesync_execute_detectframe(fskframesync _q, // NOTE: because this is a ratio of energies in frequency, we don't need // to take the absolute value here; only positive values should work if (rxy > 0.5f) { - printf("### fskframe detected! ###\n"); + //printf("### fskframe detected! ###\n"); _q->frame_detected = 1; } } else { // frame has already been detected; wait for signal to peak if (_q->rxy[1] > _q->rxy[2]) { - printf("signal peaked! %12.8f %12.8f %12.8f\n", - _q->rxy[0], _q->rxy[1], _q->rxy[2]); + //printf("signal peaked! %12.8f %12.8f %12.8f\n", + // _q->rxy[0], _q->rxy[1], _q->rxy[2]); // compute estimate, apply bias compensation float gamma = (_q->rxy[2] - _q->rxy[0]) / _q->rxy[1]; @@ -472,8 +472,8 @@ int fskframesync_execute_detectframe(fskframesync _q, float xf = fabsf(gamma); float tau_hat = copysignf(p2*xf*xf + p1*xf, gamma); int num_samples = round(tau_hat * _q->k); - printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", - gamma, tau_hat, num_samples); + //printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", + // gamma, tau_hat, num_samples); // TODO: set timer and filterbank index accordingly _q->timer = 2*_q->k; @@ -481,7 +481,7 @@ int fskframesync_execute_detectframe(fskframesync _q, // update state... _q->state = STATE_RXHEADER; } else { - printf("signal not yet peaked...\n"); + //printf("signal not yet peaked...\n"); } } return LIQUID_OK; @@ -524,7 +524,7 @@ int fskframesync_execute_rxheader(fskframesync _q, int header_valid = qpacketmodem_decode_syms(_q->header_decoder, _q->header_sym, _q->header_dec); -#if 1 +#if 0 printf("rx header symbols (%u):\n", _q->header_sym_len); unsigned int i; for (i=0; i<_q->header_sym_len; i++) @@ -612,7 +612,7 @@ int fskframesync_execute_rxpayload(fskframesync _q, // decode payload if appropriate if (_q->symbol_counter == _q->payload_sym_len) { -#if 1 +#if 0 printf("rx payload symbols (%u)\n", _q->payload_sym_len); unsigned int i; for (i=0; i<_q->payload_sym_len; i++) @@ -623,7 +623,7 @@ int fskframesync_execute_rxpayload(fskframesync _q, int payload_valid = qpacketmodem_decode_syms(_q->payload_decoder, _q->payload_sym, _q->payload_dec); - printf("payload: %s\n", payload_valid ? "valid" : "INVALID"); + //printf("payload: %s\n", payload_valid ? "valid" : "INVALID"); // invoke callback if (_q->callback != NULL) { diff --git a/src/framing/tests/fskframesync_autotest.c b/src/framing/tests/fskframesync_autotest.c new file mode 100644 index 000000000..9ffbef087 --- /dev/null +++ b/src/framing/tests/fskframesync_autotest.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "autotest/autotest.h" +#include "liquid.h" + +static int callback_fskframesync_autotest( + unsigned char * _header, + int _header_valid, + unsigned char * _payload, + unsigned int _payload_len, + int _payload_valid, + framesyncstats_s _stats, + void * _userdata) +{ + //printf("callback invoked, payload valid: %s\n", _payload_valid ? "yes" : "no"); + *((int*)(_userdata)) += _header_valid && _payload_valid ? 1 : 0; + return 0; +} + +// AUTOTEST : test simple recovery of frame in noise +void autotest_fskframesync() +{ + // options + float SNRdB = 20.0f; // signal-to-noise ratio + float noise_floor = -20.0f; // noise floor + //float dphi = 0.01f; // carrier frequency offset + //float theta = 0.0f; // carrier phase offset + //float dt = -0.2f; // fractional sample timing offset + + crc_scheme check = LIQUID_CRC_32; // data validity check + fec_scheme fec0 = LIQUID_FEC_NONE; // fec (inner) + fec_scheme fec1 = LIQUID_FEC_NONE; // fec (outer) + + // derived values + float nstd = powf(10.0f, noise_floor/20.0f); // noise std. dev. + float gamma = powf(10.0f, (SNRdB+noise_floor)/20.0f); // channel gain + + unsigned int i; + int frames_recovered = 0; + + // create objects + fskframegen fg = fskframegen_create(); + fskframesync fs = fskframesync_create(callback_fskframesync_autotest, + (void*)&frames_recovered); + + // assemble the frame + unsigned char header [ 8]; + unsigned char payload[200]; + for (i=0; i< 8; i++) header [i] = i; + for (i=0; i<200; i++) payload[i] = rand() & 0xff; + fskframegen_assemble(fg, header, payload, 200, check, fec0, fec1); + + // allocate memory for the frame samples + unsigned int buf_len = 256; + float complex buf_tx[buf_len]; // receive buffer + float complex buf_rx[buf_len]; // transmit buffer + + // try to receive the frame (operate in blocks) + int frame_complete = 0; + while (!frame_complete) + { + // generate frame samples + frame_complete = fskframegen_write_samples(fg, buf_tx, buf_len); + + // add noise, channel gain + for (i=0; i Date: Sun, 26 Feb 2023 10:34:53 -0500 Subject: [PATCH 115/186] qs1dsearch: resetting 'num_steps' counter --- src/optim/src/qs1dsearch.c | 1 + src/optim/tests/qs1dsearch_autotest.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/optim/src/qs1dsearch.c b/src/optim/src/qs1dsearch.c index 627fedcc1..35d051729 100644 --- a/src/optim/src/qs1dsearch.c +++ b/src/optim/src/qs1dsearch.c @@ -78,6 +78,7 @@ int qs1dsearch_print(qs1dsearch _q) int qs1dsearch_reset(qs1dsearch _q) { _q->init = 0; + _q->num_steps = 0; return LIQUID_OK; } diff --git a/src/optim/tests/qs1dsearch_autotest.c b/src/optim/tests/qs1dsearch_autotest.c index bcfc41326..495349f06 100644 --- a/src/optim/tests/qs1dsearch_autotest.c +++ b/src/optim/tests/qs1dsearch_autotest.c @@ -129,7 +129,13 @@ void autotest_qs1dsearch_config() qs1dsearch_init(q, 20); CONTEND_EQUALITY(LIQUID_OK, qs1dsearch_execute(q)) - CONTEND_EQUALITY( 0, qs1dsearch_get_num_steps(q)) + + // run a few steps + CONTEND_EQUALITY( 0, qs1dsearch_get_num_steps(q)) + qs1dsearch_step(q); + qs1dsearch_step(q); + qs1dsearch_step(q); + CONTEND_EQUALITY( 3, qs1dsearch_get_num_steps(q)) // destroy objects qs1dsearch_destroy(q); From de293b66cbb6865c12601a703d705e10d1b77952 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 27 Feb 2023 17:49:30 -0500 Subject: [PATCH 116/186] ofdmflexframesync/autotest: testing configurations --- src/framing/tests/ofdmflexframe_autotest.c | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/framing/tests/ofdmflexframe_autotest.c b/src/framing/tests/ofdmflexframe_autotest.c index eabde9d73..cf24e6bac 100644 --- a/src/framing/tests/ofdmflexframe_autotest.c +++ b/src/framing/tests/ofdmflexframe_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -90,3 +90,27 @@ void autotest_ofdmflexframe_07() { testbench_ofdmflexframe(1200, 40, 20, 1, L void autotest_ofdmflexframe_08() { testbench_ofdmflexframe(1200, 0, 0, 800, LIQUID_MODEM_QPSK); } void autotest_ofdmflexframe_09() { testbench_ofdmflexframe(1200, 40, 20, 8217, LIQUID_MODEM_QPSK); } +void autotest_ofdmflexframe_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmflexframe config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmflexframesync_copy(NULL)); + CONTEND_ISNULL(ofdmflexframesync_create( 0, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframesync_create( 7, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframesync_create(65, 16, 4, NULL, NULL, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmflexframesync_create(64, 66, 4, NULL, NULL, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmflexframesync q = ofdmflexframesync_create(64, 16, 4, NULL, NULL, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmflexframesync_print(q)) + + ofdmflexframesync_destroy(q); +} + From f734313856cf5a234c3f7e9b2056e5a49d504cb1 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 27 Feb 2023 17:53:57 -0500 Subject: [PATCH 117/186] ofdmflexframegen/autotest: testing configurations --- src/framing/src/ofdmflexframegen.c | 8 ++++--- src/framing/src/ofdmflexframesync.c | 2 +- src/framing/tests/ofdmflexframe_autotest.c | 28 ++++++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/framing/src/ofdmflexframegen.c b/src/framing/src/ofdmflexframegen.c index 52a7a7fef..90191a814 100644 --- a/src/framing/src/ofdmflexframegen.c +++ b/src/framing/src/ofdmflexframegen.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2021 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -157,10 +157,12 @@ ofdmflexframegen ofdmflexframegen_create(unsigned int _M, ofdmflexframegenprops_s * _fgprops) { // validate input - if (_M < 2) - return liquid_error_config("ofdmflexframegen_create(), number of subcarriers must be at least 2"); + if (_M < 8) + return liquid_error_config("ofdmflexframegen_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmflexframegen_create(), number of subcarriers must be even"); + if (_cp_len > _M) + return liquid_error_config("ofdmflexframegen_create(), cyclic prefix length cannot exceed number of subcarriers"); ofdmflexframegen q = (ofdmflexframegen) malloc(sizeof(struct ofdmflexframegen_s)); q->M = _M; // number of subcarriers diff --git a/src/framing/src/ofdmflexframesync.c b/src/framing/src/ofdmflexframesync.c index a18165eed..44f9fcedc 100644 --- a/src/framing/src/ofdmflexframesync.c +++ b/src/framing/src/ofdmflexframesync.c @@ -152,7 +152,7 @@ ofdmflexframesync ofdmflexframesync_create(unsigned int _M, // validate input if (_M < 8) - return liquid_error_config("ofdmflexframesync_create(), less than 8 subcarriers"); + return liquid_error_config("ofdmflexframesync_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmflexframesync_create(), number of subcarriers must be even"); if (_cp_len > _M) diff --git a/src/framing/tests/ofdmflexframe_autotest.c b/src/framing/tests/ofdmflexframe_autotest.c index cf24e6bac..814ab0b29 100644 --- a/src/framing/tests/ofdmflexframe_autotest.c +++ b/src/framing/tests/ofdmflexframe_autotest.c @@ -90,10 +90,34 @@ void autotest_ofdmflexframe_07() { testbench_ofdmflexframe(1200, 40, 20, 1, L void autotest_ofdmflexframe_08() { testbench_ofdmflexframe(1200, 0, 0, 800, LIQUID_MODEM_QPSK); } void autotest_ofdmflexframe_09() { testbench_ofdmflexframe(1200, 40, 20, 8217, LIQUID_MODEM_QPSK); } -void autotest_ofdmflexframe_config() +void autotest_ofdmflexframegen_config() { #if LIQUID_STRICT_EXIT - AUTOTEST_WARN("skipping ofdmflexframe config test with strict exit enabled\n"); + AUTOTEST_WARN("skipping ofdmflexframegen config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmflexframegen_copy(NULL)); + CONTEND_ISNULL(ofdmflexframegen_create( 0, 16, 4, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframegen_create( 7, 16, 4, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmflexframegen_create(65, 16, 4, NULL, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmflexframegen_create(64, 66, 4, NULL, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmflexframegen q = ofdmflexframegen_create(64, 16, 4, NULL, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmflexframegen_print(q)) + + ofdmflexframegen_destroy(q); +} + +void autotest_ofdmflexframesync_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmflexframesync config test with strict exit enabled\n"); return; #endif #if !LIQUID_SUPPRESS_ERROR_OUTPUT From 21ca9473ce009e2ef7eb73f2b11686117b8ffe10 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 27 Feb 2023 18:27:38 -0500 Subject: [PATCH 118/186] ofdmframe/autotest: adding configuration tests --- makefile.in | 2 +- ...mesync_autotest.c => ofdmframe_autotest.c} | 53 ++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) rename src/multichannel/tests/{ofdmframesync_autotest.c => ofdmframe_autotest.c} (69%) diff --git a/makefile.in b/makefile.in index 71e5b16f1..7e415d7c1 100644 --- a/makefile.in +++ b/makefile.in @@ -949,7 +949,7 @@ multichannel_autotests := \ src/multichannel/tests/firpfbch2_crcf_autotest.c \ src/multichannel/tests/firpfbch_crcf_synthesizer_autotest.c \ src/multichannel/tests/firpfbch_crcf_analyzer_autotest.c \ - src/multichannel/tests/ofdmframesync_autotest.c \ + src/multichannel/tests/ofdmframe_autotest.c \ # benchmarks multichannel_benchmarks := \ diff --git a/src/multichannel/tests/ofdmframesync_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c similarity index 69% rename from src/multichannel/tests/ofdmframesync_autotest.c rename to src/multichannel/tests/ofdmframe_autotest.c index 4d43d72a0..b949a8ece 100644 --- a/src/multichannel/tests/ofdmframesync_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2019 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,8 @@ int ofdmframesync_autotest_callback(float complex * _X, unsigned int _M, void * _userdata) { - printf("******** callback invoked!\n"); + if (liquid_autotest_verbose) + printf("******** callback invoked!\n"); // type cast _userdata as complex float array float complex * X = (float complex *)_userdata; @@ -143,3 +144,51 @@ void autotest_ofdmframesync_acquire_n128() { ofdmframesync_acquire_test(128, 16 void autotest_ofdmframesync_acquire_n256() { ofdmframesync_acquire_test(256, 32, 0); } void autotest_ofdmframesync_acquire_n512() { ofdmframesync_acquire_test(512, 64, 0); } +void autotest_ofdmframegen_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmframegen config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmframegen_copy(NULL)); + CONTEND_ISNULL(ofdmframegen_create( 0, 16, 4, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframegen_create( 7, 16, 4, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframegen_create(65, 16, 4, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmframegen_create(64, 66, 4, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmframegen q = ofdmframegen_create(64, 16, 4, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmframegen_print(q)) + + ofdmframegen_destroy(q); +} + +void autotest_ofdmframesync_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmframesync config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + //CONTEND_ISNULL(ofdmframesync_copy(NULL)); + CONTEND_ISNULL(ofdmframesync_create( 0, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframesync_create( 7, 16, 4, NULL, NULL, NULL)) // too few subcarriers + CONTEND_ISNULL(ofdmframesync_create(65, 16, 4, NULL, NULL, NULL)) // odd-length subcarriers + CONTEND_ISNULL(ofdmframesync_create(64, 66, 4, NULL, NULL, NULL)) // cyclic prefix length too large + + // create proper object and test configurations + ofdmframesync q = ofdmframesync_create(64, 16, 4, NULL, NULL, NULL); + + CONTEND_EQUALITY(LIQUID_OK, ofdmframesync_print(q)) + + ofdmframesync_destroy(q); +} + From 503c232b873e37b5508ae4359de1b33484b0626e Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Tue, 28 Feb 2023 17:42:24 -0500 Subject: [PATCH 119/186] ofdmframe: moving subcarrier verification check inside common function --- src/multichannel/src/ofdmframe.common.c | 8 ++++++++ src/multichannel/src/ofdmframegen.c | 10 ++-------- src/multichannel/src/ofdmframesync.c | 12 ++++-------- src/multichannel/tests/ofdmframe_autotest.c | 2 ++ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/multichannel/src/ofdmframe.common.c b/src/multichannel/src/ofdmframe.common.c index cc30554c2..2d612eb15 100644 --- a/src/multichannel/src/ofdmframe.common.c +++ b/src/multichannel/src/ofdmframe.common.c @@ -294,6 +294,14 @@ int ofdmframe_validate_sctype(unsigned char * _p, } } + // validate subcarrier allocation + if ( (M_pilot + M_data) == 0) + return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least one enabled subcarrier"); + if (M_data == 0) + return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least one data subcarrier"); + if (M_pilot < 2) + return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least two pilot subcarriers"); + // set outputs *_M_null = M_null; *_M_pilot = M_pilot; diff --git a/src/multichannel/src/ofdmframegen.c b/src/multichannel/src/ofdmframegen.c index 8553ef538..e45719898 100644 --- a/src/multichannel/src/ofdmframegen.c +++ b/src/multichannel/src/ofdmframegen.c @@ -84,8 +84,8 @@ ofdmframegen ofdmframegen_create(unsigned int _M, unsigned char * _p) { // validate input - if (_M < 2) - return liquid_error_config("ofdmframegen_create(), number of subcarriers must be at least 2"); + if (_M < 8) + return liquid_error_config("ofdmframegen_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmframegen_create(), number of subcarriers must be even"); if (_cp_len > _M) @@ -111,12 +111,6 @@ ofdmframegen ofdmframegen_create(unsigned int _M, // validate and count subcarrier allocation if (ofdmframe_validate_sctype(q->p, q->M, &q->M_null, &q->M_pilot, &q->M_data)) return liquid_error_config("ofdmframegen_create(), invalid subcarrier allocation"); - if ( (q->M_pilot + q->M_data) == 0) - return liquid_error_config("ofdmframegen_create(), must have at least one enabled subcarrier"); - if (q->M_data == 0) - return liquid_error_config("ofdmframegen_create(), must have at least one data subcarriers"); - if (q->M_pilot < 2) - return liquid_error_config("ofdmframegen_create(), must have at least two pilot subcarriers"); unsigned int i; diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 0e7ce5448..f69e35e6e 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -149,11 +149,13 @@ ofdmframesync ofdmframesync_create(unsigned int _M, // validate input if (_M < 8) - return liquid_error_config("ofdmframesync_create(), less than 8 subcarriers"); + return liquid_error_config("ofdmframesync_create(), number of subcarriers must be at least 8"); if (_M % 2) return liquid_error_config("ofdmframesync_create(), number of subcarriers must be even"); if (_cp_len > _M) return liquid_error_config("ofdmframesync_create(), cyclic prefix length cannot exceed number of subcarriers"); + if (_taper_len > _cp_len) + return liquid_error_config("ofdmframesync_create(), taper length cannot exceed cyclic prefix"); q->M = _M; q->cp_len = _cp_len; @@ -172,12 +174,6 @@ ofdmframesync ofdmframesync_create(unsigned int _M, // validate and count subcarrier allocation if (ofdmframe_validate_sctype(q->p, q->M, &q->M_null, &q->M_pilot, &q->M_data)) return liquid_error_config("ofdmframesync_create(), invalid subcarrier allocation"); - if ( (q->M_pilot + q->M_data) == 0) - return liquid_error_config("ofdmframesync_create(), must have at least one enabled subcarrier"); - if (q->M_data == 0) - return liquid_error_config("ofdmframesync_create(), must have at least one data subcarriers"); - if (q->M_pilot < 2) - return liquid_error_config("ofdmframesync_create(), must have at least two pilot subcarriers"); // create transform object q->X = (float complex*) FFT_MALLOC((q->M)*sizeof(float complex)); diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index b949a8ece..1b1643888 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -159,6 +159,7 @@ void autotest_ofdmframegen_config() CONTEND_ISNULL(ofdmframegen_create( 7, 16, 4, NULL)) // too few subcarriers CONTEND_ISNULL(ofdmframegen_create(65, 16, 4, NULL)) // odd-length subcarriers CONTEND_ISNULL(ofdmframegen_create(64, 66, 4, NULL)) // cyclic prefix length too large + CONTEND_ISNULL(ofdmframegen_create(64, 16,24, NULL)) // taper length greater than cyclic prefix // create proper object and test configurations ofdmframegen q = ofdmframegen_create(64, 16, 4, NULL); @@ -183,6 +184,7 @@ void autotest_ofdmframesync_config() CONTEND_ISNULL(ofdmframesync_create( 7, 16, 4, NULL, NULL, NULL)) // too few subcarriers CONTEND_ISNULL(ofdmframesync_create(65, 16, 4, NULL, NULL, NULL)) // odd-length subcarriers CONTEND_ISNULL(ofdmframesync_create(64, 66, 4, NULL, NULL, NULL)) // cyclic prefix length too large + CONTEND_ISNULL(ofdmframesync_create(64, 16,24, NULL, NULL, NULL)) // taper length greater than cyclic prefix // create proper object and test configurations ofdmframesync q = ofdmframesync_create(64, 16, 4, NULL, NULL, NULL); From aeef3bf1f69c638f3b42f77094028f518232e2d9 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 18:08:47 -0500 Subject: [PATCH 120/186] ofdmframesync: using internal method to unwrap phase --- src/multichannel/src/ofdmframesync.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index f69e35e6e..2a482cd1b 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -951,12 +951,7 @@ int ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, return liquid_error(LIQUID_EINT,"ofdmframesync_estimate_eqgain_poly(), pilot subcarrier mismatch"); // try to unwrap phase - for (i=1; i M_PI) - y_arg[i] -= 2*M_PI; - while ((y_arg[i] - y_arg[i-1]) < -M_PI) - y_arg[i] += 2*M_PI; - } + liquid_unwrap_phase(y_arg, N); // fit to polynomial polyf_fit(x_freq, y_abs, N, p_abs, _order+1); @@ -1032,12 +1027,7 @@ int ofdmframesync_rxsymbol(ofdmframesync _q) return liquid_error(LIQUID_EINT,"ofdmframesync_estimate_eqgain_poly(), pilot subcarrier mismatch"); // try to unwrap phase - for (i=1; i<_q->M_pilot; i++) { - while ((y_phase[i] - y_phase[i-1]) > M_PI) - y_phase[i] -= 2*M_PI; - while ((y_phase[i] - y_phase[i-1]) < -M_PI) - y_phase[i] += 2*M_PI; - } + liquid_unwrap_phase(y_phase, _q->M_pilot); // fit phase to 1st-order polynomial (2 coefficients) polyf_fit(x_phase, y_phase, _q->M_pilot, p_phase, 2); From 5d84fa9797b0952104c152da6abb13f1d17ff85b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 18:12:27 -0500 Subject: [PATCH 121/186] ofdmframesync: adding additional configuration tests --- src/multichannel/src/ofdmframesync.c | 3 +-- src/multichannel/tests/ofdmframe_autotest.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 2a482cd1b..3fcf1d953 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -413,8 +413,7 @@ float ofdmframesync_get_cfo(ofdmframesync _q) // set receiver carrier frequency offset estimate int ofdmframesync_set_cfo(ofdmframesync _q, float _cfo) { - nco_crcf_set_frequency(_q->nco_rx, _cfo); - return LIQUID_OK; + return nco_crcf_set_frequency(_q->nco_rx, _cfo); } // diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index 1b1643888..2ca3ab93c 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -190,6 +190,8 @@ void autotest_ofdmframesync_config() ofdmframesync q = ofdmframesync_create(64, 16, 4, NULL, NULL, NULL); CONTEND_EQUALITY(LIQUID_OK, ofdmframesync_print(q)) + CONTEND_EQUALITY( 0, ofdmframesync_is_frame_open(q)) + CONTEND_EQUALITY(LIQUID_OK, ofdmframesync_set_cfo(q,0.0f)) ofdmframesync_destroy(q); } From f4b5b338fbc32f9bb3470e01c670fc76a5a64df2 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 18:17:09 -0500 Subject: [PATCH 122/186] ofdmframesync: moving internal funciton declarations from header --- include/liquid.internal.h | 45 ------------------------- src/multichannel/src/ofdmframesync.c | 49 +++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/include/liquid.internal.h b/include/liquid.internal.h index e8e2e0d90..e5f32f0a8 100644 --- a/include/liquid.internal.h +++ b/include/liquid.internal.h @@ -1486,51 +1486,6 @@ int ofdmframe_init_S1(unsigned char * _p, int ofdmframegen_gensymbol(ofdmframegen _q, float complex * _buffer); -int ofdmframesync_cpcorrelate(ofdmframesync _q); -int ofdmframesync_findrxypeak(ofdmframesync _q); -int ofdmframesync_rxpayload(ofdmframesync _q); - -int ofdmframesync_execute_seekplcp(ofdmframesync _q); -int ofdmframesync_execute_S0a(ofdmframesync _q); -int ofdmframesync_execute_S0b(ofdmframesync _q); -int ofdmframesync_execute_S1( ofdmframesync _q); -int ofdmframesync_execute_rxsymbols(ofdmframesync _q); - -int ofdmframesync_S0_metrics(ofdmframesync _q, - float complex * _G, - float complex * _s_hat); - -// estimate short sequence gain -// _q : ofdmframesync object -// _x : input array (time) -// _G : output gain (freq) -int ofdmframesync_estimate_gain_S0(ofdmframesync _q, - float complex * _x, - float complex * _G); - -// estimate long sequence gain -// _q : ofdmframesync object -// _x : input array (time) -// _G : output gain (freq) -int ofdmframesync_estimate_gain_S1(ofdmframesync _q, - float complex * _x, - float complex * _G); - -// estimate complex equalizer gain from G0 and G1 -// _q : ofdmframesync object -// _ntaps : number of time-domain taps for smoothing -int ofdmframesync_estimate_eqgain(ofdmframesync _q, - unsigned int _ntaps); - -// estimate complex equalizer gain from G0 and G1 using polynomial fit -// _q : ofdmframesync object -// _order : polynomial order -int ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, - unsigned int _order); - -// recover symbol, correcting for gain, pilot phase, etc. -int ofdmframesync_rxsymbol(ofdmframesync _q); - // // MODULE : nco (numerically-controlled oscillator) // diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 3fcf1d953..a1e2f5ecf 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -20,11 +20,7 @@ * THE SOFTWARE. */ -// -// ofdmframesync.c -// // OFDM frame synchronizer -// #include #include @@ -41,6 +37,49 @@ #define OFDMFRAMESYNC_ENABLE_SQUELCH 0 +// forward declaration of internal methods + +int ofdmframesync_execute_seekplcp(ofdmframesync _q); +int ofdmframesync_execute_S0a(ofdmframesync _q); +int ofdmframesync_execute_S0b(ofdmframesync _q); +int ofdmframesync_execute_S1( ofdmframesync _q); +int ofdmframesync_execute_rxsymbols(ofdmframesync _q); + +int ofdmframesync_S0_metrics(ofdmframesync _q, + float complex * _G, + float complex * _s_hat); + +// estimate short sequence gain +// _q : ofdmframesync object +// _x : input array (time) +// _G : output gain (freq) +int ofdmframesync_estimate_gain_S0(ofdmframesync _q, + float complex * _x, + float complex * _G); + +// estimate long sequence gain +// _q : ofdmframesync object +// _x : input array (time) +// _G : output gain (freq) +int ofdmframesync_estimate_gain_S1(ofdmframesync _q, + float complex * _x, + float complex * _G); + +// estimate complex equalizer gain from G0 and G1 +// _q : ofdmframesync object +// _ntaps : number of time-domain taps for smoothing +int ofdmframesync_estimate_eqgain(ofdmframesync _q, + unsigned int _ntaps); + +// estimate complex equalizer gain from G0 and G1 using polynomial fit +// _q : ofdmframesync object +// _order : polynomial order +int ofdmframesync_estimate_eqgain_poly(ofdmframesync _q, + unsigned int _order); + +// recover symbol, correcting for gain, pilot phase, etc. +int ofdmframesync_rxsymbol(ofdmframesync _q); + struct ofdmframesync_s { unsigned int M; // number of subcarriers unsigned int M2; // number of subcarriers (divided by 2) @@ -752,7 +791,7 @@ int ofdmframesync_execute_rxsymbols(ofdmframesync _q) } // compute S0 metrics -int ofdmframesync_S0_metrics(ofdmframesync _q, +int ofdmframesync_S0_metrics(ofdmframesync _q, float complex * _G, float complex * _s_hat) { From 9f97bee94f6aaf8d9a0cdf770f64c3ff4ff30584 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 20:40:51 -0500 Subject: [PATCH 123/186] ofdmframesync: disabling debugging (preprocessor) --- src/multichannel/src/ofdmframesync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index a1e2f5ecf..bd88ff947 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -30,7 +30,7 @@ #include "liquid.internal.h" -#define DEBUG_OFDMFRAMESYNC 1 +#define DEBUG_OFDMFRAMESYNC 0 #define DEBUG_OFDMFRAMESYNC_PRINT 0 #define DEBUG_OFDMFRAMESYNC_FILENAME "ofdmframesync_internal_debug.m" #define DEBUG_OFDMFRAMESYNC_BUFFER_LEN (2048) From 8ef84a93f56d3236d1140ce84963f9969c72211e Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 20:46:53 -0500 Subject: [PATCH 124/186] ofdmframesync: block-commenting unused method for estimating equalizer gain --- src/multichannel/src/ofdmframesync.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index bd88ff947..8a05e20b1 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -68,8 +68,7 @@ int ofdmframesync_estimate_gain_S1(ofdmframesync _q, // estimate complex equalizer gain from G0 and G1 // _q : ofdmframesync object // _ntaps : number of time-domain taps for smoothing -int ofdmframesync_estimate_eqgain(ofdmframesync _q, - unsigned int _ntaps); +//int ofdmframesync_estimate_eqgain(ofdmframesync _q, unsigned int _ntaps); // estimate complex equalizer gain from G0 and G1 using polynomial fit // _q : ofdmframesync object @@ -879,6 +878,7 @@ int ofdmframesync_estimate_gain_S1(ofdmframesync _q, return LIQUID_OK; } +#if 0 // estimate complex equalizer gain from G0 and G1 // _q : ofdmframesync object // _ntaps : number of time-domain taps for smoothing @@ -939,6 +939,7 @@ int ofdmframesync_estimate_eqgain(ofdmframesync _q, } return LIQUID_OK; } +#endif // estimate complex equalizer gain from G0 and G1 using polynomial fit // _q : ofdmframesync object From d70516f140c6d41d66b6a62a9b672858b184a1ab Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 20:51:15 -0500 Subject: [PATCH 125/186] ofdmframegen: moving internal method declarations from header --- include/liquid.internal.h | 6 +----- src/multichannel/src/ofdmframegen.c | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/liquid.internal.h b/include/liquid.internal.h index e5f32f0a8..d38648460 100644 --- a/include/liquid.internal.h +++ b/include/liquid.internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -1482,10 +1482,6 @@ int ofdmframe_init_S1(unsigned char * _p, float complex * _s1, unsigned int * _M_S1); -// generate symbol (add cyclic prefix/postfix, overlap) -int ofdmframegen_gensymbol(ofdmframegen _q, - float complex * _buffer); - // // MODULE : nco (numerically-controlled oscillator) // diff --git a/src/multichannel/src/ofdmframegen.c b/src/multichannel/src/ofdmframegen.c index e45719898..150cb8532 100644 --- a/src/multichannel/src/ofdmframegen.c +++ b/src/multichannel/src/ofdmframegen.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,10 @@ #define DEBUG_OFDMFRAMEGEN 1 +// generate symbol (add cyclic prefix/postfix, overlap) +int ofdmframegen_gensymbol(ofdmframegen _q, + float complex * _buffer); + struct ofdmframegen_s { unsigned int M; // number of subcarriers unsigned int cp_len; // cyclic prefix length From 83d67ac1adf21e4074ef24b357a16601df660fe3 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 1 Mar 2023 21:03:24 -0500 Subject: [PATCH 126/186] firpfbch2/autotest: adding configuration testing --- .../tests/firpfbch2_crcf_autotest.c | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/multichannel/tests/firpfbch2_crcf_autotest.c b/src/multichannel/tests/firpfbch2_crcf_autotest.c index c00a728b5..9c5f135ec 100644 --- a/src/multichannel/tests/firpfbch2_crcf_autotest.c +++ b/src/multichannel/tests/firpfbch2_crcf_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -97,7 +97,6 @@ void autotest_firpfbch2_crcf_n16() { firpfbch2_crcf_runtest( 16, 5, 60.0f); } void autotest_firpfbch2_crcf_n32() { firpfbch2_crcf_runtest( 32, 5, 60.0f); } void autotest_firpfbch2_crcf_n64() { firpfbch2_crcf_runtest( 64, 5, 60.0f); } - void autotest_firpfbch2_crcf_copy() { // create channelizer @@ -140,3 +139,36 @@ void autotest_firpfbch2_crcf_copy() firpfbch2_crcf_destroy(q_copy); } +void autotest_firpfbch2_crcf_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping firpfbch2_crcf config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(firpfbch2_crcf_create( 77, 76, 12, NULL)) // invalid type + CONTEND_ISNULL(firpfbch2_crcf_create(LIQUID_ANALYZER, 0, 12, NULL)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create(LIQUID_ANALYZER, 17, 12, NULL)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create(LIQUID_ANALYZER, 76, 0, NULL)) // invalid filter semi-length + + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser( 77, 76, 12, 60.0f)) // invalid type + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 0, 12, 60.0f)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 17, 12, 60.0f)) // invalid number of channels + CONTEND_ISNULL(firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 76, 0, 60.0f)) // invalid filter semi-length + + CONTEND_ISNULL(firpfbch2_crcf_copy(NULL)) + + // create proper object and test configurations + firpfbch2_crcf q = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER, 76, 12, 60.0f); + + CONTEND_EQUALITY(LIQUID_OK, firpfbch2_crcf_print(q)) + CONTEND_EQUALITY(LIQUID_ANALYZER, firpfbch2_crcf_get_type(q)) + CONTEND_EQUALITY( 76, firpfbch2_crcf_get_M(q)) + CONTEND_EQUALITY( 12, firpfbch2_crcf_get_m(q)) + + firpfbch2_crcf_destroy(q); +} + From 68983c26a300e7229d187ea10e1a87fbb7e812be Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Fri, 3 Mar 2023 08:21:20 -0500 Subject: [PATCH 127/186] firpfbch: adding configuration testing --- makefile.in | 1 + .../tests/firpfbch_crcf_autotest.c | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/multichannel/tests/firpfbch_crcf_autotest.c diff --git a/makefile.in b/makefile.in index 7e415d7c1..3c0246d9b 100644 --- a/makefile.in +++ b/makefile.in @@ -949,6 +949,7 @@ multichannel_autotests := \ src/multichannel/tests/firpfbch2_crcf_autotest.c \ src/multichannel/tests/firpfbch_crcf_synthesizer_autotest.c \ src/multichannel/tests/firpfbch_crcf_analyzer_autotest.c \ + src/multichannel/tests/firpfbch_crcf_autotest.c \ src/multichannel/tests/ofdmframe_autotest.c \ # benchmarks diff --git a/src/multichannel/tests/firpfbch_crcf_autotest.c b/src/multichannel/tests/firpfbch_crcf_autotest.c new file mode 100644 index 000000000..fb9deb967 --- /dev/null +++ b/src/multichannel/tests/firpfbch_crcf_autotest.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "autotest/autotest.h" +#include "liquid.h" + +void autotest_firpfbch_crcf_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping firpfbch_crcf config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_ISNULL(firpfbch_crcf_create( 77, 76, 12, NULL)) // invalid type + CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 0, 12, NULL)) // invalid number of channels + CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 76, 0, NULL)) // invalid filter semi-length + + //CONTEND_ISNULL(firpfbch_crcf_copy(NULL)) + + // create proper object and test configurations + firpfbch_crcf q = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, 76, 12, 60.0f); + + CONTEND_EQUALITY(LIQUID_OK, firpfbch_crcf_print(q)) + + firpfbch_crcf_destroy(q); +} + From 158a0252aefeb43e646cd1f05d4809f7ffbf85c3 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 07:48:33 -0500 Subject: [PATCH 128/186] firpfbch/autotest: adding more configuration tests --- src/multichannel/src/firpfbch.proto.c | 14 ++++++-------- src/multichannel/tests/firpfbch_crcf_autotest.c | 8 ++++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/multichannel/src/firpfbch.proto.c b/src/multichannel/src/firpfbch.proto.c index 28826a2b5..1f147e795 100644 --- a/src/multichannel/src/firpfbch.proto.c +++ b/src/multichannel/src/firpfbch.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,7 @@ * THE SOFTWARE. */ -// -// firpfbch.c -// -// finite impulse response polyphase filterbank channelizer -// +// firpfbch : finite impulse response polyphase filterbank channelizer #include #include @@ -77,7 +73,7 @@ FIRPFBCH() FIRPFBCH(_create)(int _type, { // validate input if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) - return liquid_error_config("firpfbch_%s_create(), invalid type %d", EXTENSION_FULL, _type); + return liquid_error_config("firpfbch_%s_create(), invalid type: %d", EXTENSION_FULL, _type); if (_M == 0) return liquid_error_config("firpfbch_%s_create(), number of channels must be greater than 0", EXTENSION_FULL); if (_p == 0) @@ -148,6 +144,8 @@ FIRPFBCH() FIRPFBCH(_create_kaiser)(int _type, float _as) { // validate input + if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) + return liquid_error_config("firpfbch_%s_create_kaiser(), invalid type: %d", EXTENSION_FULL, _type); if (_M == 0) return liquid_error_config("firpfbch_%s_create_kaiser(), number of channels must be greater than 0", EXTENSION_FULL); if (_m == 0) @@ -190,7 +188,7 @@ FIRPFBCH() FIRPFBCH(_create_rnyquist)(int _type, { // validate input if (_type != LIQUID_ANALYZER && _type != LIQUID_SYNTHESIZER) - return liquid_error_config("firpfbch_%s_create_rnyquist(), invalid type %d", EXTENSION_FULL, _type); + return liquid_error_config("firpfbch_%s_create_rnyquist(), invalid type: %d", EXTENSION_FULL, _type); if (_M == 0) return liquid_error_config("firpfbch_%s_create_rnyquist(), number of channels must be greater than 0", EXTENSION_FULL); if (_m == 0) diff --git a/src/multichannel/tests/firpfbch_crcf_autotest.c b/src/multichannel/tests/firpfbch_crcf_autotest.c index fb9deb967..55204b882 100644 --- a/src/multichannel/tests/firpfbch_crcf_autotest.c +++ b/src/multichannel/tests/firpfbch_crcf_autotest.c @@ -38,6 +38,14 @@ void autotest_firpfbch_crcf_config() CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 0, 12, NULL)) // invalid number of channels CONTEND_ISNULL(firpfbch_crcf_create(LIQUID_ANALYZER, 76, 0, NULL)) // invalid filter semi-length + CONTEND_ISNULL(firpfbch_crcf_create_kaiser( 77, 76, 12, 60.0f)) // invalid type + CONTEND_ISNULL(firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, 0, 12, 60.0f)) // invalid number of channels + CONTEND_ISNULL(firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, 76, 0, 60.0f)) // invalid filter semi-length + + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist( 77, 76, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid type + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 0, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid number of channels + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 0, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid filter semi-length + //CONTEND_ISNULL(firpfbch_crcf_copy(NULL)) // create proper object and test configurations From 40fab43ccb045b76f67b704c708ac1637ac455f7 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 08:10:57 -0500 Subject: [PATCH 129/186] firpfbch: using prototype filter generation for create_rnyquist() --- src/multichannel/src/firpfbch.proto.c | 22 ++----------------- .../tests/firpfbch_crcf_autotest.c | 2 ++ 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/multichannel/src/firpfbch.proto.c b/src/multichannel/src/firpfbch.proto.c index 1f147e795..0683c3a1a 100644 --- a/src/multichannel/src/firpfbch.proto.c +++ b/src/multichannel/src/firpfbch.proto.c @@ -197,26 +197,8 @@ FIRPFBCH() FIRPFBCH(_create_rnyquist)(int _type, // design filter based on requested prototype unsigned int h_len = 2*_M*_m + 1; float h[h_len]; - switch (_ftype) { - case LIQUID_FIRFILT_ARKAISER: - // root-Nyquist Kaiser (approximate optimum) - liquid_firdes_arkaiser(_M, _m, _beta, 0.0f, h); - break; - case LIQUID_FIRFILT_RKAISER: - // root-Nyquist Kaiser (true optimum) - liquid_firdes_rkaiser(_M, _m, _beta, 0.0f, h); - break; - case LIQUID_FIRFILT_RRC: - // root raised-cosine - liquid_firdes_rrcos(_M, _m, _beta, 0.0f, h); - break; - case LIQUID_FIRFILT_hM3: - // harris-Moerder-3 filter - liquid_firdes_hM3(_M, _m, _beta, 0.0f, h); - break; - default: - return liquid_error_config("firpfbch_%s_create_rnyquist(), unknown/invalid prototype (%d)", EXTENSION_FULL, _ftype); - } + if (liquid_firdes_prototype(_ftype, _M, _m, _beta, 0.0f, h) != LIQUID_OK) + return liquid_error_config("firpfbch_%s_create_rnyquist(), invalid filter type/configuration", EXTENSION_FULL); // copy coefficients to type-specfic array, reversing order if // channelizer is an analyzer, matched filter: g(-t) diff --git a/src/multichannel/tests/firpfbch_crcf_autotest.c b/src/multichannel/tests/firpfbch_crcf_autotest.c index 55204b882..7ada5fd56 100644 --- a/src/multichannel/tests/firpfbch_crcf_autotest.c +++ b/src/multichannel/tests/firpfbch_crcf_autotest.c @@ -45,6 +45,8 @@ void autotest_firpfbch_crcf_config() CONTEND_ISNULL(firpfbch_crcf_create_rnyquist( 77, 76, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid type CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 0, 12, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid number of channels CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 0, 0.2f, LIQUID_FIRFILT_ARKAISER)) // invalid filter semi-length + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 12, 77.f, LIQUID_FIRFILT_ARKAISER)) // invalid filter excess bandwidth + CONTEND_ISNULL(firpfbch_crcf_create_rnyquist(LIQUID_ANALYZER, 76, 12, 0.2f, LIQUID_FIRFILT_UNKNOWN )) // invalid filter type //CONTEND_ISNULL(firpfbch_crcf_copy(NULL)) From 8c7cc6009fa0127f816185ded3503904c7842c84 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 08:38:20 -0500 Subject: [PATCH 130/186] ofdmframe/autotest: including configuration tests for common functions --- src/multichannel/src/ofdmframe.common.c | 8 +++--- src/multichannel/tests/ofdmframe_autotest.c | 32 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/multichannel/src/ofdmframe.common.c b/src/multichannel/src/ofdmframe.common.c index 2d612eb15..ab7e4d5b1 100644 --- a/src/multichannel/src/ofdmframe.common.c +++ b/src/multichannel/src/ofdmframe.common.c @@ -302,10 +302,10 @@ int ofdmframe_validate_sctype(unsigned char * _p, if (M_pilot < 2) return liquid_error(LIQUID_EICONFIG,"ofdmframe_validate_sctype(), must have at least two pilot subcarriers"); - // set outputs - *_M_null = M_null; - *_M_pilot = M_pilot; - *_M_data = M_data; + // set outputs if requested + if (_M_null != NULL) *_M_null = M_null; + if (_M_pilot != NULL) *_M_pilot = M_pilot; + if (_M_data != NULL) *_M_data = M_data; return LIQUID_OK; } diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index 2ca3ab93c..bcb369d45 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -144,6 +144,38 @@ void autotest_ofdmframesync_acquire_n128() { ofdmframesync_acquire_test(128, 16 void autotest_ofdmframesync_acquire_n256() { ofdmframesync_acquire_test(256, 32, 0); } void autotest_ofdmframesync_acquire_n512() { ofdmframesync_acquire_test(512, 64, 0); } +void autotest_ofdmframe_common_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping ofdmframe common config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid function calls + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_default_sctype(0, NULL)) // too few subcarriers + + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range( 0, -0.4f, +0.4f, NULL)) // too few subcarriers + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.7f, +0.4f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.4f, +0.7f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.2f, -0.3f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, 0.3f, 0.2f, NULL)) // frequency out of range + CONTEND_INEQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(64, -0.02f,+0.02f,NULL)) // too few effective subcarriers + + // generate valid subcarrier allocation + unsigned int M = 120; + unsigned char p[M]; + + // default subcarrier allocation + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_init_default_sctype(M, p)) + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_validate_sctype(p, M, NULL, NULL, NULL)) + + // subcarrier allocation within an occupied frequency range + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(M, -0.4f,+0.4f,p)) + CONTEND_EQUALITY(LIQUID_OK, ofdmframe_validate_sctype(p, M, NULL, NULL, NULL)) +} + void autotest_ofdmframegen_config() { #if LIQUID_STRICT_EXIT From 60afb3c2ef6f0f9a2c7421a427b14bbc7f87c127 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 4 Mar 2023 09:01:21 -0500 Subject: [PATCH 131/186] ofdmframe/autotest: testing edge cases for subcarrier allocations --- src/multichannel/tests/ofdmframe_autotest.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/multichannel/tests/ofdmframe_autotest.c b/src/multichannel/tests/ofdmframe_autotest.c index bcb369d45..2e0aed52e 100644 --- a/src/multichannel/tests/ofdmframe_autotest.c +++ b/src/multichannel/tests/ofdmframe_autotest.c @@ -174,6 +174,18 @@ void autotest_ofdmframe_common_config() // subcarrier allocation within an occupied frequency range CONTEND_EQUALITY(LIQUID_OK, ofdmframe_init_sctype_range(M, -0.4f,+0.4f,p)) CONTEND_EQUALITY(LIQUID_OK, ofdmframe_validate_sctype(p, M, NULL, NULL, NULL)) + + // invalid subcarrier allocations + unsigned int i; + for (i=0; i Date: Sat, 11 Mar 2023 07:15:02 -0500 Subject: [PATCH 132/186] qpacketmodem/example: adjusting number of trials based on error performance --- examples/qpacketmodem_performance_example.c | 181 ++++++++------------ 1 file changed, 76 insertions(+), 105 deletions(-) diff --git a/examples/qpacketmodem_performance_example.c b/examples/qpacketmodem_performance_example.c index 300c516f3..607d36ab9 100644 --- a/examples/qpacketmodem_performance_example.c +++ b/examples/qpacketmodem_performance_example.c @@ -1,11 +1,6 @@ -// -// qpacketmodem_performance_example.c -// // This example demonstrates the performance of the qpacket modem // object to combine forward error-correction and modulation in one // simple interface. -// - #include #include #include @@ -21,39 +16,41 @@ void usage() { printf("qpacketmodem_performance_example [options]\n"); - printf(" h : print usage\n"); - printf(" p : payload length [bytes], default: 400\n"); - printf(" m : modulation scheme (qpsk default)\n"); + printf(" -h : print usage\n"); + printf(" -p : payload length [bytes], default: 400\n"); + printf(" -m : modulation scheme (qpsk default)\n"); liquid_print_modulation_schemes(); - printf(" v : data integrity check: crc32 default\n"); + printf(" -v : data integrity check: crc32 default\n"); liquid_print_crc_schemes(); - printf(" c : coding scheme (inner): g2412 default\n"); - printf(" k : coding scheme (outer): none default\n"); + printf(" -c : coding scheme (inner): g2412 default\n"); + printf(" -k : coding scheme (outer): none default\n"); liquid_print_fec_schemes(); - printf(" s : SNR start [dB], default: -2\n"); - printf(" x : SNR max [dB], default: 13\n"); - printf(" n : number of SNR steps, default: 16\n"); - printf(" t : number of trials, default: 800\n"); + printf(" -s : SNR start [dB], default: -5\n"); + printf(" -x : SNR max [dB], default: 20\n"); + printf(" -d : SNR step [dB], default: 0.5\n"); + printf(" -e : minimum number of packet errors per step, default: 50\n"); + printf(" -t : minimum number of packet trials per step, default: 2000\n"); + printf(" -T : maximum number of packet trials per step, default: 40000\n"); } int main(int argc, char *argv[]) { - //srand( time(NULL) ); - - // options + // options (defaults to frame64 parameters) modulation_scheme ms = LIQUID_MODEM_QPSK; // mod. scheme - crc_scheme check = LIQUID_CRC_32; // data validity check - fec_scheme fec0 = LIQUID_FEC_GOLAY2412; // fec (inner) - fec_scheme fec1 = LIQUID_FEC_NONE; // fec (outer) - unsigned int payload_len = 400; // payload length + crc_scheme check = LIQUID_CRC_24; // data validity check + fec_scheme fec0 = LIQUID_FEC_NONE; // fec (inner) + fec_scheme fec1 = LIQUID_FEC_GOLAY2412; // fec (outer) + unsigned int payload_len = 72; // payload length float SNRdB_min = -5.0f; // signal-to-noise ratio (minimum) - float SNRdB_max = 10.0f; // signal-to-noise ratio (maximum) - unsigned int num_snr = 31; // number of SNR steps - unsigned int num_packet_trials = 800; // number of trials + float SNRdB_max = 20.0f; // signal-to-noise ratio (maximum) + float SNRdB_step = 0.5f; // signal-to-noise ratio (maximum) + unsigned int min_packet_errors = 50; // minimum errors to observe for each step + unsigned int min_packet_trials = 2000; // minimum number of packets for each step + unsigned int max_packet_trials =40000; // maximum number of packets for each step // get options int dopt; - while((dopt = getopt(argc,argv,"hp:m:v:c:k:s:x:n:t:")) != EOF){ + while((dopt = getopt(argc,argv,"hp:m:v:c:k:s:x:d:e:t:T:")) != EOF){ switch (dopt) { case 'h': usage(); return 0; case 'p': payload_len = atol(optarg); break; @@ -63,18 +60,16 @@ int main(int argc, char *argv[]) case 'k': fec1 = liquid_getopt_str2fec(optarg); break; case 's': SNRdB_min = atof(optarg); break; case 'x': SNRdB_max = atof(optarg); break; - case 'n': num_snr = atoi(optarg); break; - case 't': num_packet_trials = atoi(optarg); break; + case 'd': SNRdB_step = atof(optarg); break; + case 'e': min_packet_errors = atoi(optarg); break; + case 't': min_packet_trials = atoi(optarg); break; + case 'T': max_packet_trials = atoi(optarg); break; default: exit(-1); } } - unsigned int i; - // derived values - float SNRdB_step = (SNRdB_max - SNRdB_min) / (num_snr-1); - // create and configure packet encoder/decoder object qpacketmodem q = qpacketmodem_create(); qpacketmodem_configure(q, payload_len, check, fec0, fec1, ms); @@ -82,32 +77,27 @@ int main(int argc, char *argv[]) // get frame length unsigned int frame_len = qpacketmodem_get_frame_len(q); - unsigned int num_bit_trials = 8*num_packet_trials*payload_len; // initialize payload unsigned char payload_tx [payload_len]; // payload (transmitted) unsigned char payload_rx [payload_len]; // payload (received) float complex frame_tx [frame_len]; // frame samples (transmitted) float complex frame_rx [frame_len]; // frame samples (received) - unsigned int num_bit_errors [num_snr]; // bit errors for each SNR point - unsigned int num_packet_errors[num_snr]; // packet errors for each SNR point - float BER [num_snr]; // bit error rate - float PER [num_snr]; // packet error rate + + // output file + FILE* fid = fopen(OUTPUT_FILENAME, "w"); + fprintf(fid,"%% %s: auto-generated file\n", OUTPUT_FILENAME); + fprintf(fid,"clear all; close all; SNR=[]; ber=[]; per=[];\n"); printf(" %8s %8s %8s %12s %8s %8s %6s\n", "SNR [dB]", "errors", "bits", "BER", "errors", "packets", "PER"); - unsigned int s; - for (s=0; s= min_packet_errors) + break; + if (num_packet_trials >= max_packet_trials) + break; } - BER[s] = (float)num_bit_errors[s] / (float)num_bit_trials; - PER[s] = (float)num_packet_errors[s] / (float)num_packet_trials; + float BER = (float)num_bit_errors / (float)num_bit_trials; + float PER = (float)num_packet_errors / (float)num_packet_trials; printf(" %8.2f %8u %8u %12.4e %8u %8u %6.2f%%\n", SNRdB, - num_bit_errors[s], num_bit_trials, BER[s], - num_packet_errors[s], num_packet_trials, PER[s]*100.0f); + num_bit_errors, num_bit_trials, BER, + num_packet_errors, num_packet_trials, PER*100.0f); + fprintf(fid,"SNR(end+1)=%g; ber(end+1)=%g; per(end+1)=%g;\n", SNRdB, BER, PER); + if (num_packet_errors < min_packet_errors) + break; + SNRdB += SNRdB_step; } // destroy allocated objects qpacketmodem_destroy(q); - // - // export output file - // - FILE * fid = fopen(OUTPUT_FILENAME, "w"); - fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME); - fprintf(fid,"\n\n"); - fprintf(fid,"clear all\n"); - fprintf(fid,"close all\n"); - fprintf(fid,"payload_len = %u; %% payload length [bytes]\n", payload_len); - fprintf(fid,"frame_len = %u; %% frame length [symbols]\n", frame_len); - fprintf(fid,"num_snr = %u;\n", num_snr); - fprintf(fid,"num_packet_trials = %u;\n", num_packet_trials); - fprintf(fid,"num_bit_trials = %u;\n", num_bit_trials); - fprintf(fid,"SNRdB_min = %8.2f;\n", SNRdB_min); - fprintf(fid,"SNRdB_max = %8.2f;\n", SNRdB_max); - fprintf(fid,"scheme = '%s/%s/%s';\n", - fec_scheme_str[fec0][1], - fec_scheme_str[fec1][1], - modulation_types[ms].name); - fprintf(fid,"rate = 8*payload_len / frame_len; %% true rate [bits/symbol]\n"); - for (i=0; i Date: Sat, 11 Mar 2023 08:33:53 -0500 Subject: [PATCH 133/186] setting noise correctly in framesync64 performance example --- examples/framesync64_performance_example.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/framesync64_performance_example.c b/examples/framesync64_performance_example.c index fc43291c3..c50234f36 100644 --- a/examples/framesync64_performance_example.c +++ b/examples/framesync64_performance_example.c @@ -10,10 +10,11 @@ // add noise to channel void frame64_add_noise(float complex * _buf, float _SNRdB) { - float nstd = powf(10.0f, -_SNRdB/20.0f) * M_SQRT1_2; + float nstd = powf(10.0f, -_SNRdB/20.0f); + nstd *= M_SQRT2; // scale noise to account for signal being over-sampled by 2 unsigned int i; for (i=0; i Date: Sat, 11 Mar 2023 08:37:23 -0500 Subject: [PATCH 134/186] framesync64: adding interpolated SNR estimate for target PER --- examples/framesync64_performance_example.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/framesync64_performance_example.c b/examples/framesync64_performance_example.c index c50234f36..7242ca563 100644 --- a/examples/framesync64_performance_example.c +++ b/examples/framesync64_performance_example.c @@ -25,15 +25,18 @@ int main(int argc, char*argv[]) unsigned int min_errors = 50; unsigned int min_trials = 500; unsigned int max_trials = 5000; + float per_target = 1e-2f; // create buffer for the frame samples float complex frame[LIQUID_FRAME64_LEN]; - float SNRdB = -6.0f; + float SNRdB = -3.0f; + float per_0 =-1.0f, per_1 = -1.0f; + float snr_0 = 0.0f, snr_1 = 0.0f; FILE* fid = fopen(OUTPUT_FILENAME, "w"); fprintf(fid,"%% %s: auto-generated file\n", OUTPUT_FILENAME); fprintf(fid,"clear all; close all;\n"); fprintf(fid,"SNR=[]; pdetect=[]; pvalid=[];\n"); - printf("# %8s %6s %6s %6s\n", "SNR", "detect", "valid", "trials"); + printf("# %8s %6s %6s %6s %12s\n", "SNR", "detect", "valid", "trials", "PER"); while (SNRdB < 10.0f) { framesync64_reset_framedatastats(fs); unsigned int num_trials = 0, num_errors = 0; @@ -61,8 +64,11 @@ int main(int argc, char*argv[]) } // print results framedatastats_s stats = framesync64_get_framedatastats(fs); - printf(" %8.3f %6u %6u %6u\n", - SNRdB,stats.num_frames_detected,stats.num_payloads_valid,num_trials); + float per = (float)(num_trials - stats.num_payloads_valid)/(float)num_trials; + if (per >= per_target) { per_0 = per; snr_0 = SNRdB; } + else if (per_1 < 0.0f ) { per_1 = per; snr_1 = SNRdB; } + printf(" %8.3f %6u %6u %6u %12.4e\n", + SNRdB,stats.num_frames_detected,stats.num_payloads_valid,num_trials,per); fprintf(fid,"SNR(end+1)=%g; pdetect(end+1)=%g; pvalid(end+1)=%g;\n", SNRdB, (float)stats.num_frames_detected / (float)num_trials, @@ -71,6 +77,10 @@ int main(int argc, char*argv[]) break; SNRdB += 0.5f; } + float m = (logf(per_1) - logf(per_0)) / (snr_1 - snr_0); + float snr_target = snr_0 + (logf(per_target) - logf(per_0)) / m; + printf("per:{%12.4e,%12.4e}, snr:{%5.2f,%5.2f} => %.3f dB for PER %12.4e\n", + per_0, per_1, snr_0, snr_1, snr_target, per_target); fprintf(fid,"figure;\n"); fprintf(fid,"hold on;\n"); fprintf(fid," semilogy(SNR, 1-pdetect+eps,'-o', 'LineWidth',2, 'MarkerSize',2);\n"); @@ -89,3 +99,4 @@ int main(int argc, char*argv[]) framesync64_destroy(fs); return 0; } + From 321093248e74dba332c1a3ec39817b39fd78495d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 11 Mar 2023 12:41:57 -0500 Subject: [PATCH 135/186] ofdmframesync: returning appropriate value with debugging macros --- src/multichannel/src/ofdmframesync.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/multichannel/src/ofdmframesync.c b/src/multichannel/src/ofdmframesync.c index 8a05e20b1..7f008b054 100644 --- a/src/multichannel/src/ofdmframesync.c +++ b/src/multichannel/src/ofdmframesync.c @@ -1152,7 +1152,7 @@ int ofdmframesync_debug_enable(ofdmframesync _q) _q->debug_objects_created = 1; return LIQUID_OK; #else - fprintf(stderr,"ofdmframesync_debug_enable(): compile-time debugging disabled\n"); + return liquid_error(LIQUID_EICONFIG,"ofdmframesync_debug_enable(): compile-time debugging disabled"); #endif } @@ -1163,7 +1163,7 @@ int ofdmframesync_debug_disable(ofdmframesync _q) _q->debug_enabled = 0; return LIQUID_OK; #else - fprintf(stderr,"ofdmframesync_debug_disable(): compile-time debugging disabled\n"); + return liquid_error(LIQUID_EICONFIG,"ofdmframesync_debug_disable(): compile-time debugging disabled"); #endif } @@ -1325,7 +1325,7 @@ int ofdmframesync_debug_print(ofdmframesync _q, printf("ofdmframesync/debug: results written to '%s'\n", _filename); return LIQUID_OK; #else - fprintf(stderr,"ofdmframesync_debug_print(): compile-time debugging disabled\n"); + return liquid_error(LIQUID_EICONFIG,"ofdmframesync_debug_print(): compile-time debugging disabled"); #endif } From 3f9c9de99d4dcaf18c114755a009a5e34c401888 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 12 Mar 2023 18:52:04 -0400 Subject: [PATCH 136/186] framesync64: respecting arguments with CLI, printing minimal info to screen --- scripts/framesync64_debug.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/framesync64_debug.py b/scripts/framesync64_debug.py index 27c09c0aa..47c455f4f 100755 --- a/scripts/framesync64_debug.py +++ b/scripts/framesync64_debug.py @@ -11,9 +11,9 @@ def main(argv=None): args = p.parse_args() for fname in args.sources: - filename = framesync64_plot(fname,args.export) + filename = framesync64_plot(fname,args.export,args.nodisplay) -def framesync64_plot(filename,export=None): +def framesync64_plot(filename,export=None,nodisplay=True): # open file and read values fid = open(filename,'rb') buf = np.fromfile(fid, count=1440, dtype=np.csingle) @@ -26,7 +26,7 @@ def framesync64_plot(filename,export=None): payload_sym = np.fromfile(fid, count= 600, dtype=np.csingle) payload_dec = np.fromfile(fid, count= 72, dtype=np.int8) - # compute filter response in dB + # compute smooth spectral response in dB nfft = 2400 f = np.arange(nfft)/nfft-0.5 psd = np.abs(np.fft.fftshift(np.fft.fft(buf, nfft)))**2 @@ -35,7 +35,6 @@ def framesync64_plot(filename,export=None): h = np.concatenate((w[m:], np.zeros(nfft-2*m-1), w[:m])) / (sum(w) * nfft) H = np.fft.fft(h) psd = 10*np.log10( np.real(np.fft.ifft(H * np.fft.fft(psd))) ) - #psd = 20*np.log10(np.abs(np.fft.fftshift(np.fft.fft(buf, nfft)))) # plot impulse and spectral responses fig, _ax = plt.subplots(2,2,figsize=(12,12)) @@ -58,11 +57,13 @@ def framesync64_plot(filename,export=None): _ax.set_ylabel('Imag') for _ax in ax: _ax.grid(True) - fig.suptitle('frame64, tau:%.6f, dphi:%.6f, phi:%.6f, rssi:%.3f dB, evm:%.3f' % \ - (tau_hat, dphi_hat, phi_hat, 20*np.log10(gamma_hat), evm)) - if export==None: + title = '%s, tau:%9.6f, dphi:%9.6f, phi:%9.6f, rssi:%6.3f dB, evm:%6.3f' % \ + (filename, tau_hat, dphi_hat, phi_hat, 20*np.log10(gamma_hat), evm) + print(title) + fig.suptitle(title) + if not nodisplay: plt.show() - else: + if export is not None: fig.savefig(os.path.splitext(filename)[0]+'.png',bbox_inches='tight') plt.close() From fdd737ec65511fee87b084ff3c944e0fced2f43b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 16 Mar 2023 17:44:13 -0400 Subject: [PATCH 137/186] crc: adding simple tests for data validity checks --- include/liquid.h | 2 +- src/fec/src/crc.c | 10 ++++---- src/fec/tests/crc_autotest.c | 47 +++++++++++++++++++++++------------- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 38f575cd9..a5927130d 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -1194,7 +1194,7 @@ typedef enum { extern const char * crc_scheme_str[LIQUID_CRC_NUM_SCHEMES][2]; // Print compact list of existing and available CRC schemes -void liquid_print_crc_schemes(); +int liquid_print_crc_schemes(); // returns crc_scheme based on input string crc_scheme liquid_getopt_str2crc(const char * _str); diff --git a/src/fec/src/crc.c b/src/fec/src/crc.c index b3ee388ab..0b7ed7fa7 100644 --- a/src/fec/src/crc.c +++ b/src/fec/src/crc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -46,12 +46,12 @@ const char * crc_scheme_str[LIQUID_CRC_NUM_SCHEMES][2] = { // Print compact list of existing and available crc schemes -void liquid_print_crc_schemes() +int liquid_print_crc_schemes() { unsigned int i; unsigned int len = 10; - // print all available MOD schemes + // print all available CRC schemes printf(" "); for (i=0; i Date: Sat, 18 Mar 2023 09:59:20 -0400 Subject: [PATCH 138/186] build: stripping version number off archive --- makefile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makefile.in b/makefile.in index 3c0246d9b..90decdbb6 100644 --- a/makefile.in +++ b/makefile.in @@ -1,4 +1,4 @@ -# Copyright (c) 2007 - 2022 Joseph Gaeddert +# Copyright (c) 2007 - 2023 Joseph Gaeddert # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -1294,7 +1294,7 @@ install: all @echo "" mkdir -p $(DESTDIR)$(prefix)/include/liquid mkdir -p $(DESTDIR)$(libdir) - install -m 644 -p libliquid.${AR_LIB} $(DESTDIR)$(libdir)/libliquid.${AR_LIB}.${VERSION} + install -m 644 -p libliquid.${AR_LIB} $(DESTDIR)$(libdir)/libliquid.${AR_LIB} install -m 644 -p libliquid.${SH_LIB} $(DESTDIR)$(libdir)/libliquid.${SH_LIB}.${VERSION} install -m 644 -p include/liquid.h $(DESTDIR)$(prefix)/include/liquid/liquid.h ln -sf libliquid.${SH_LIB}.${VERSION} $(DESTDIR)$(libdir)/libliquid.${SH_LIB} @@ -1321,7 +1321,7 @@ install: all uninstall: @echo "uninstalling..." $(RM) $(DESTDIR)$(prefix)/include/liquid/liquid.h - $(RM) $(DESTDIR)$(libdir)/libliquid.${AR_LIB}.${VERSION} + $(RM) $(DESTDIR)$(libdir)/libliquid.${AR_LIB} $(RM) $(DESTDIR)$(libdir)/libliquid.${SH_LIB}.${VERSION} $(RM) $(DESTDIR)$(libdir)/libliquid.${SH_LIB}.1 $(RM) $(DESTDIR)$(libdir)/libliquid.${SH_LIB} From 1625eff62f1915625e0b31c3b8609189ce5f33a1 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 23 Mar 2023 08:09:54 -0400 Subject: [PATCH 139/186] fec/hamming(31,26): checking symbol size edge cases --- src/fec/tests/fec_hamming3126_autotest.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/fec/tests/fec_hamming3126_autotest.c b/src/fec/tests/fec_hamming3126_autotest.c index a3731c0f4..7713eaa9d 100644 --- a/src/fec/tests/fec_hamming3126_autotest.c +++ b/src/fec/tests/fec_hamming3126_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -65,3 +65,16 @@ void autotest_hamming3126_codec() } } +void autotest_fec_hamming3126_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping fec_hamming3126 config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // symbols too large + CONTEND_EQUALITY(fec_hamming3126_encode_symbol(1u<<26), 0) + CONTEND_EQUALITY(fec_hamming3126_decode_symbol(1u<<31), 0) +} From c004a4659a1121d62885ef9549ca0e0e78bf9151 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Fri, 24 Mar 2023 07:56:07 -0400 Subject: [PATCH 140/186] fec: moving configuration tests to separte file --- makefile.in | 1 + src/fec/tests/fec_config_autotest.c | 51 ++++++++++++++++++++++++ src/fec/tests/fec_hamming3126_autotest.c | 13 ------ 3 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 src/fec/tests/fec_config_autotest.c diff --git a/makefile.in b/makefile.in index 90decdbb6..d92ca74c8 100644 --- a/makefile.in +++ b/makefile.in @@ -303,6 +303,7 @@ $(fec_objects) : %.o : %.c $(include_headers) fec_autotests := \ src/fec/tests/crc_autotest.c \ src/fec/tests/fec_autotest.c \ + src/fec/tests/fec_config_autotest.c \ src/fec/tests/fec_copy_autotest.c \ src/fec/tests/fec_soft_autotest.c \ src/fec/tests/fec_golay2412_autotest.c \ diff --git a/src/fec/tests/fec_config_autotest.c b/src/fec/tests/fec_config_autotest.c new file mode 100644 index 000000000..729a5725e --- /dev/null +++ b/src/fec/tests/fec_config_autotest.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "autotest/autotest.h" +#include "liquid.internal.h" + +void autotest_fec_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping fec_hamming3126 config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // symbols too large + CONTEND_EQUALITY(fec_golay2412_encode_symbol(1u<<12), 0) + CONTEND_EQUALITY(fec_golay2412_decode_symbol(1u<<24), 0) + + CONTEND_EQUALITY(fec_hamming3126_encode_symbol(1u<<26), 0) + CONTEND_EQUALITY(fec_hamming3126_decode_symbol(1u<<31), 0) + + CONTEND_EQUALITY(fec_hamming1511_encode_symbol(1u<<11), 0) + CONTEND_EQUALITY(fec_hamming1511_decode_symbol(1u<<15), 0) + + CONTEND_EQUALITY(fec_hamming128_encode_symbol(1u<<8), 0) + CONTEND_EQUALITY(fec_hamming128_decode_symbol(1u<<12), 0) +} + diff --git a/src/fec/tests/fec_hamming3126_autotest.c b/src/fec/tests/fec_hamming3126_autotest.c index 7713eaa9d..4e0090fe9 100644 --- a/src/fec/tests/fec_hamming3126_autotest.c +++ b/src/fec/tests/fec_hamming3126_autotest.c @@ -65,16 +65,3 @@ void autotest_hamming3126_codec() } } -void autotest_fec_hamming3126_config() -{ -#if LIQUID_STRICT_EXIT - AUTOTEST_WARN("skipping fec_hamming3126 config test with strict exit enabled\n"); - return; -#endif -#if !LIQUID_SUPPRESS_ERROR_OUTPUT - fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); -#endif - // symbols too large - CONTEND_EQUALITY(fec_hamming3126_encode_symbol(1u<<26), 0) - CONTEND_EQUALITY(fec_hamming3126_decode_symbol(1u<<31), 0) -} From dc7e3f020012e3aa3ef26bdff7801a2b43256776 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Mar 2023 13:17:12 +0000 Subject: [PATCH 141/186] asgram: filling entire ASCII buffer with zeros; avoid compiler warning --- src/fft/src/asgram.proto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fft/src/asgram.proto.c b/src/fft/src/asgram.proto.c index d8adcbc31..c4d62c596 100644 --- a/src/fft/src/asgram.proto.c +++ b/src/fft/src/asgram.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -256,7 +256,7 @@ int ASGRAM(_print)(ASGRAM() _q) float maxval; float maxfreq; char ascii[_q->nfft+1]; - ascii[_q->nfft] = '\0'; // append null character to end of string + memset(ascii, '\0', _q->nfft+1); // fill buffer with null characters // execute the spectrogram ASGRAM(_execute)(_q, ascii, &maxval, &maxfreq); From f51d83b8e703c50d8958b4a4c098ffa135c68b01 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Mar 2023 13:19:33 +0000 Subject: [PATCH 142/186] fskframesync: block-commenting unused section to avoid compiler warnings --- src/framing/src/fskframesync.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/framing/src/fskframesync.c b/src/framing/src/fskframesync.c index f68c4cf4c..8d30274e6 100644 --- a/src/framing/src/fskframesync.c +++ b/src/framing/src/fskframesync.c @@ -465,6 +465,7 @@ int fskframesync_execute_detectframe(fskframesync _q, //printf("signal peaked! %12.8f %12.8f %12.8f\n", // _q->rxy[0], _q->rxy[1], _q->rxy[2]); +#if 0 // compute estimate, apply bias compensation float gamma = (_q->rxy[2] - _q->rxy[0]) / _q->rxy[1]; float p2 = 9.54907046918287e-01f; @@ -472,8 +473,9 @@ int fskframesync_execute_detectframe(fskframesync _q, float xf = fabsf(gamma); float tau_hat = copysignf(p2*xf*xf + p1*xf, gamma); int num_samples = round(tau_hat * _q->k); - //printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", - // gamma, tau_hat, num_samples); + printf("timing offset estimate : %12.8f -> %12.8f (%d samples)\n", + gamma, tau_hat, num_samples); +#endif // TODO: set timer and filterbank index accordingly _q->timer = 2*_q->k; From fb94b233df8e00b4fba65c7afb7f1ac4de66bb7e Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 25 Mar 2023 13:26:41 +0000 Subject: [PATCH 143/186] autoscript: using sprintf to avoid compiler warning --- scripts/autoscript.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/autoscript.c b/scripts/autoscript.c index fd669808e..14ca83be0 100644 --- a/scripts/autoscript.c +++ b/scripts/autoscript.c @@ -297,11 +297,7 @@ void autoscript_parsefile(autoscript _q, // generate tag (e.g. "void benchmark_"); unsigned int tag_len = 5 + strlen(_q->type) + 2; char tag[tag_len]; - strcpy(tag, "void "); - strcpy(tag+5, _q->type); - tag[tag_len-2] = '_'; - tag[tag_len-1] = '\0'; - //printf("// tag : '%s'\n", tag); + sprintf(tag, "void %s_", _q->type); // parse file, looking for key char buffer[1024]; // line buffer From 0c8aa65ca612cfcbdba269d9373eb191acc0c7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Sat, 19 Nov 2022 06:31:52 +0000 Subject: [PATCH 144/186] Stop using SIMD dot product instructions as they are way slower than mul and add Removing the dedicated dp instruction effectively makes the dotprod_rrrf SSE4 implementation redundant, remove it Rename MMX implementations to SSE since they do not use MMX instructions anyway Add loop unrolled implementation of sumsq Prepare for AVX-512 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 35 +- library.json | 3 +- ...{dotprod_cccf.mmx.c => dotprod_cccf.sse.c} | 30 +- src/dotprod/src/dotprod_crcf.avx.c | 20 +- ...{dotprod_crcf.mmx.c => dotprod_crcf.sse.c} | 61 ++-- src/dotprod/src/dotprod_rrrf.avx.c | 23 +- ...{dotprod_rrrf.mmx.c => dotprod_rrrf.sse.c} | 58 ++-- src/dotprod/src/dotprod_rrrf.sse4.c | 307 ------------------ src/dotprod/src/sumsq.avx.c | 85 ++++- src/dotprod/src/{sumsq.mmx.c => sumsq.sse.c} | 87 ++++- 10 files changed, 270 insertions(+), 439 deletions(-) rename src/dotprod/src/{dotprod_cccf.mmx.c => dotprod_cccf.sse.c} (95%) rename src/dotprod/src/{dotprod_crcf.mmx.c => dotprod_crcf.sse.c} (88%) rename src/dotprod/src/{dotprod_rrrf.mmx.c => dotprod_rrrf.sse.c} (86%) delete mode 100644 src/dotprod/src/dotprod_rrrf.sse4.c rename src/dotprod/src/{sumsq.mmx.c => sumsq.sse.c} (60%) diff --git a/configure.ac b/configure.ac index 12d4b7cd8..33742fcee 100644 --- a/configure.ac +++ b/configure.ac @@ -170,9 +170,18 @@ else # SSSE3 : tmmintrin.h # SSE4.1/2: smmintrin.h # AVX : immintrin.h + # AVX2 : immintrin.h + # AVX512 : immintrin.h AX_EXT - if [ test "$ax_cv_have_avx2_ext" = yes ]; then + if [ test "$ax_cv_have_avx512f_ext" = yes ]; then + # AVX512 extensions + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ + src/dotprod/src/dotprod_crcf.avx.o \ + src/dotprod/src/dotprod_rrrf.avx.o \ + src/dotprod/src/sumsq.avx.o" + ARCH_OPTION='-mavx512f' + elif [ test "$ax_cv_have_avx2_ext" = yes ]; then # AVX2 extensions MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ src/dotprod/src/dotprod_crcf.avx.o \ @@ -188,24 +197,24 @@ else ARCH_OPTION='-mavx' elif [ test "$ax_cv_have_sse41_ext" = yes ]; then # SSE4.1/2 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.sse4.o \ - src/dotprod/src/sumsq.mmx.o" + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.sse.o \ + src/dotprod/src/dotprod_crcf.sse.o \ + src/dotprod/src/dotprod_rrrf.sse.o \ + src/dotprod/src/sumsq.sse.o" ARCH_OPTION='-msse4.1' elif [ test "$ax_cv_have_sse3_ext" = yes ]; then # SSE3 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.mmx.o \ - src/dotprod/src/sumsq.mmx.o" + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.sse.o \ + src/dotprod/src/dotprod_crcf.sse.o \ + src/dotprod/src/dotprod_rrrf.sse.o \ + src/dotprod/src/sumsq.sse.o" ARCH_OPTION='-msse3' elif [ test "$ax_cv_have_sse2_ext" = yes ]; then # SSE2 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.mmx.o \ - src/dotprod/src/dotprod_crcf.mmx.o \ - src/dotprod/src/dotprod_rrrf.mmx.o \ - src/dotprod/src/sumsq.mmx.o" + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.sse.o \ + src/dotprod/src/dotprod_crcf.sse.o \ + src/dotprod/src/dotprod_rrrf.sse.o \ + src/dotprod/src/sumsq.sse.o" ARCH_OPTION='-msse2' else # portable C version diff --git a/library.json b/library.json index 6f14be837..753bb1a00 100644 --- a/library.json +++ b/library.json @@ -39,9 +39,8 @@ "-<.git/>", "-<*/src/*.proto.c>", "-<*/src/*.av.c>", - "-<*/src/*.mmx.c>", "-<*/src/*.neon.c>", - "-<*/src/*.sse4.c>", + "-<*/src/*.sse.c>", "-<*/src/*.avx.c>", "-<*/src/*.x86.s>" ] diff --git a/src/dotprod/src/dotprod_cccf.mmx.c b/src/dotprod/src/dotprod_cccf.sse.c similarity index 95% rename from src/dotprod/src/dotprod_cccf.mmx.c rename to src/dotprod/src/dotprod_cccf.sse.c index 6794bfcfe..6bfc10053 100644 --- a/src/dotprod/src/dotprod_cccf.mmx.c +++ b/src/dotprod/src/dotprod_cccf.sse.c @@ -21,7 +21,7 @@ */ // -// Floating-point dot product (MMX) +// Floating-point dot product (SSE) // #include @@ -33,10 +33,6 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX -#include // MMX -#endif - #if HAVE_SSE #include // SSE #endif @@ -49,14 +45,14 @@ #include // SSE3 #endif -#define DEBUG_DOTPROD_CCCF_MMX 0 +#define DEBUG_DOTPROD_CCCF_sse 0 // forward declaration of internal methods -int dotprod_cccf_execute_mmx(dotprod_cccf _q, +int dotprod_cccf_execute_sse(dotprod_cccf _q, float complex * _x, float complex * _y); -int dotprod_cccf_execute_mmx4(dotprod_cccf _q, +int dotprod_cccf_execute_sse4(dotprod_cccf _q, float complex * _x, float complex * _y); @@ -104,7 +100,7 @@ int dotprod_cccf_run4(float complex * _h, // -// structured MMX dot product +// structured sse dot product // struct dotprod_cccf_s { @@ -177,7 +173,7 @@ dotprod_cccf dotprod_cccf_copy(dotprod_cccf q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("dotprod_cccf_copy().mmx, object cannot be NULL"); + return liquid_error_config("dotprod_cccf_copy().sse, object cannot be NULL"); dotprod_cccf q_copy = (dotprod_cccf)malloc(sizeof(struct dotprod_cccf_s)); q_copy->n = q_orig->n; @@ -206,7 +202,7 @@ int dotprod_cccf_destroy(dotprod_cccf _q) int dotprod_cccf_print(dotprod_cccf _q) { - printf("dotprod_cccf [mmx, %u coefficients]\n", _q->n); + printf("dotprod_cccf [sse, %u coefficients]\n", _q->n); unsigned int i; for (i=0; i<_q->n; i++) printf(" %3u : %12.9f +j%12.9f\n", i, _q->hi[i], _q->hq[i]); @@ -223,12 +219,12 @@ int dotprod_cccf_execute(dotprod_cccf _q, { // switch based on size if (_q->n < 32) { - return dotprod_cccf_execute_mmx(_q, _x, _y); + return dotprod_cccf_execute_sse(_q, _x, _y); } - return dotprod_cccf_execute_mmx4(_q, _x, _y); + return dotprod_cccf_execute_sse4(_q, _x, _y); } -// use MMX/SSE extensions +// use SSE extensions // // (a + jb)(c + jd) = (ac - bd) + j(ad + bc) // @@ -248,7 +244,7 @@ int dotprod_cccf_execute(dotprod_cccf _q, // x[1].real * h[1].imag, // x[1].imag * h[1].imag }; // -int dotprod_cccf_execute_mmx(dotprod_cccf _q, +int dotprod_cccf_execute_sse(dotprod_cccf _q, float complex * _x, float complex * _y) { @@ -341,8 +337,8 @@ int dotprod_cccf_execute_mmx(dotprod_cccf _q, return LIQUID_OK; } -// use MMX/SSE extensions -int dotprod_cccf_execute_mmx4(dotprod_cccf _q, +// use SSE extensions +int dotprod_cccf_execute_sse4(dotprod_cccf _q, float complex * _x, float complex * _y) { diff --git a/src/dotprod/src/dotprod_crcf.avx.c b/src/dotprod/src/dotprod_crcf.avx.c index 6e14af3ad..525148210 100644 --- a/src/dotprod/src/dotprod_crcf.avx.c +++ b/src/dotprod/src/dotprod_crcf.avx.c @@ -274,10 +274,7 @@ int dotprod_crcf_execute_avx4(dotprod_crcf _q, __m256 s0, s1, s2, s3; // dot products [re, im, re, im] // load zeros into sum registers - __m256 sum0 = _mm256_setzero_ps(); - __m256 sum1 = _mm256_setzero_ps(); - __m256 sum2 = _mm256_setzero_ps(); - __m256 sum3 = _mm256_setzero_ps(); + __m256 sum = _mm256_setzero_ps(); // r = 8*floor(n/32) unsigned int r = (n >> 5) << 3; @@ -304,22 +301,17 @@ int dotprod_crcf_execute_avx4(dotprod_crcf _q, s3 = _mm256_mul_ps(v3, h3); // parallel addition - sum0 = _mm256_add_ps( sum0, s0 ); - sum1 = _mm256_add_ps( sum1, s1 ); - sum2 = _mm256_add_ps( sum2, s2 ); - sum3 = _mm256_add_ps( sum3, s3 ); + sum = _mm256_add_ps( sum, s0 ); + sum = _mm256_add_ps( sum, s1 ); + sum = _mm256_add_ps( sum, s2 ); + sum = _mm256_add_ps( sum, s3 ); } - // fold down - sum0 = _mm256_add_ps( sum0, sum1 ); - sum2 = _mm256_add_ps( sum2, sum3 ); - sum0 = _mm256_add_ps( sum0, sum2 ); - // aligned output array float w[8] __attribute__((aligned(32))); // unload packed array and perform manual sum - _mm256_store_ps(w, sum0); + _mm256_store_ps(w, sum); w[0] += w[2] + w[4] + w[6]; w[1] += w[3] + w[5] + w[7]; diff --git a/src/dotprod/src/dotprod_crcf.mmx.c b/src/dotprod/src/dotprod_crcf.sse.c similarity index 88% rename from src/dotprod/src/dotprod_crcf.mmx.c rename to src/dotprod/src/dotprod_crcf.sse.c index 4ba9d320d..182133d22 100644 --- a/src/dotprod/src/dotprod_crcf.mmx.c +++ b/src/dotprod/src/dotprod_crcf.sse.c @@ -21,7 +21,7 @@ */ // -// Floating-point dot product (MMX) +// Floating-point dot product (SSE) // #include @@ -32,13 +32,28 @@ #include "liquid.internal.h" -#define DEBUG_DOTPROD_CRCF_MMX 0 +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#if HAVE_SSE +#include // SSE +#endif + +#if HAVE_SSE2 +#include // SSE2 +#endif + +#if HAVE_SSE3 +#include // SSE3 +#endif + +#define DEBUG_DOTPROD_CRCF_SSE 0 // forward declaration of internal methods -int dotprod_crcf_execute_mmx(dotprod_crcf _q, +int dotprod_crcf_execute_sse(dotprod_crcf _q, float complex * _x, float complex * _y); -int dotprod_crcf_execute_mmx4(dotprod_crcf _q, +int dotprod_crcf_execute_sse4(dotprod_crcf _q, float complex * _x, float complex * _y); @@ -86,7 +101,7 @@ int dotprod_crcf_run4(float * _h, // -// structured MMX dot product +// structured SSE dot product // struct dotprod_crcf_s { @@ -153,7 +168,7 @@ dotprod_crcf dotprod_crcf_copy(dotprod_crcf q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("dotprod_crcf_copy().mmx, object cannot be NULL"); + return liquid_error_config("dotprod_crcf_copy().sse, object cannot be NULL"); dotprod_crcf q_copy = (dotprod_crcf)malloc(sizeof(struct dotprod_crcf_s)); q_copy->n = q_orig->n; @@ -181,7 +196,7 @@ int dotprod_crcf_print(dotprod_crcf _q) { // print coefficients to screen, skipping odd entries (due // to repeated coefficients) - printf("dotprod_crcf [mmx, %u coefficients]\n", _q->n); + printf("dotprod_crcf [sse, %u coefficients]\n", _q->n); unsigned int i; for (i=0; i<_q->n; i++) printf(" %3u : %12.9f\n", i, _q->h[2*i]); @@ -195,13 +210,13 @@ int dotprod_crcf_execute(dotprod_crcf _q, { // switch based on size if (_q->n < 32) { - return dotprod_crcf_execute_mmx(_q, _x, _y); + return dotprod_crcf_execute_sse(_q, _x, _y); } - return dotprod_crcf_execute_mmx4(_q, _x, _y); + return dotprod_crcf_execute_sse4(_q, _x, _y); } -// use MMX/SSE extensions -int dotprod_crcf_execute_mmx(dotprod_crcf _q, +// use SSE extensions +int dotprod_crcf_execute_sse(dotprod_crcf _q, float complex * _x, float complex * _y) { @@ -257,8 +272,8 @@ int dotprod_crcf_execute_mmx(dotprod_crcf _q, return LIQUID_OK; } -// use MMX/SSE extensions -int dotprod_crcf_execute_mmx4(dotprod_crcf _q, +// use SSE extensions +int dotprod_crcf_execute_sse4(dotprod_crcf _q, float complex * _x, float complex * _y) { @@ -274,10 +289,7 @@ int dotprod_crcf_execute_mmx4(dotprod_crcf _q, __m128 s0, s1, s2, s3; // dot products [re, im, re, im] // load zeros into sum registers - __m128 sum0 = _mm_setzero_ps(); - __m128 sum1 = _mm_setzero_ps(); - __m128 sum2 = _mm_setzero_ps(); - __m128 sum3 = _mm_setzero_ps(); + __m128 sum = _mm_setzero_ps(); // r = 4*floor(n/16) unsigned int r = (n >> 4) << 2; @@ -304,22 +316,17 @@ int dotprod_crcf_execute_mmx4(dotprod_crcf _q, s3 = _mm_mul_ps(v3, h3); // parallel addition - sum0 = _mm_add_ps( sum0, s0 ); - sum1 = _mm_add_ps( sum1, s1 ); - sum2 = _mm_add_ps( sum2, s2 ); - sum3 = _mm_add_ps( sum3, s3 ); + sum = _mm_add_ps( sum, s0 ); + sum = _mm_add_ps( sum, s1 ); + sum = _mm_add_ps( sum, s2 ); + sum = _mm_add_ps( sum, s3 ); } - // fold down - sum0 = _mm_add_ps( sum0, sum1 ); - sum2 = _mm_add_ps( sum2, sum3 ); - sum0 = _mm_add_ps( sum0, sum2 ); - // aligned output array float w[4] __attribute__((aligned(16))); // unload packed array and perform manual sum - _mm_store_ps(w, sum0); + _mm_store_ps(w, sum); w[0] += w[2]; w[1] += w[3]; diff --git a/src/dotprod/src/dotprod_rrrf.avx.c b/src/dotprod/src/dotprod_rrrf.avx.c index 2af478375..08a87539c 100644 --- a/src/dotprod/src/dotprod_rrrf.avx.c +++ b/src/dotprod/src/dotprod_rrrf.avx.c @@ -219,12 +219,17 @@ int dotprod_rrrf_execute_avx(dotprod_rrrf _q, h = _mm256_load_ps(&_q->h[i]); // compute dot product - s = _mm256_dp_ps(v, h, 0xff); - + s = _mm256_mul_ps(v, h); + // parallel addition sum = _mm256_add_ps( sum, s ); } + // fold down into single value + __m256 z = _mm256_setzero_ps(); + sum = _mm256_hadd_ps(sum, z); + sum = _mm256_hadd_ps(sum, z); + // aligned output array float w[8] __attribute__((aligned(32))); @@ -270,19 +275,23 @@ int dotprod_rrrf_execute_avxu(dotprod_rrrf _q, h3 = _mm256_load_ps(&_q->h[4*i+24]); // compute dot products - s0 = _mm256_dp_ps(v0, h0, 0xff); - s1 = _mm256_dp_ps(v1, h1, 0xff); - s2 = _mm256_dp_ps(v2, h2, 0xff); - s3 = _mm256_dp_ps(v3, h3, 0xff); + s0 = _mm256_mul_ps(v0, h0); + s1 = _mm256_mul_ps(v1, h1); + s2 = _mm256_mul_ps(v2, h2); + s3 = _mm256_mul_ps(v3, h3); // parallel addition - // FIXME: these additions are by far the limiting factor sum = _mm256_add_ps( sum, s0 ); sum = _mm256_add_ps( sum, s1 ); sum = _mm256_add_ps( sum, s2 ); sum = _mm256_add_ps( sum, s3 ); } + // fold down into single value + __m256 z = _mm256_setzero_ps(); + sum = _mm256_hadd_ps(sum, z); + sum = _mm256_hadd_ps(sum, z); + // aligned output array float w[8] __attribute__((aligned(32))); diff --git a/src/dotprod/src/dotprod_rrrf.mmx.c b/src/dotprod/src/dotprod_rrrf.sse.c similarity index 86% rename from src/dotprod/src/dotprod_rrrf.mmx.c rename to src/dotprod/src/dotprod_rrrf.sse.c index 613eb707a..a9f4c58f1 100644 --- a/src/dotprod/src/dotprod_rrrf.mmx.c +++ b/src/dotprod/src/dotprod_rrrf.sse.c @@ -21,7 +21,7 @@ */ // -// Floating-point dot product (MMX) +// Floating-point dot product (SSE) // #include @@ -34,10 +34,6 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX -#include // MMX -#endif - #if HAVE_SSE #include // SSE #endif @@ -50,13 +46,13 @@ #include // SSE3 #endif -#define DEBUG_DOTPROD_RRRF_MMX 0 +#define DEBUG_DOTPROD_RRRF_SSE 0 // internal methods -int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, +int dotprod_rrrf_execute_sse(dotprod_rrrf _q, float * _x, float * _y); -int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, +int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, float * _x, float * _y); @@ -104,7 +100,7 @@ int dotprod_rrrf_run4(float * _h, // -// structured MMX dot product +// structured SSE dot product // struct dotprod_rrrf_s { @@ -167,7 +163,7 @@ dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("dotprod_rrrf_copy().mmx, object cannot be NULL"); + return liquid_error_config("dotprod_rrrf_copy().sse, object cannot be NULL"); dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); q_copy->n = q_orig->n; @@ -191,7 +187,7 @@ int dotprod_rrrf_destroy(dotprod_rrrf _q) int dotprod_rrrf_print(dotprod_rrrf _q) { - printf("dotprod_rrrf [mmx, %u coefficients]\n", _q->n); + printf("dotprod_rrrf [sse, %u coefficients]\n", _q->n); unsigned int i; for (i=0; i<_q->n; i++) printf("%3u : %12.9f\n", i, _q->h[i]); @@ -205,13 +201,13 @@ int dotprod_rrrf_execute(dotprod_rrrf _q, { // switch based on size if (_q->n < 16) { - return dotprod_rrrf_execute_mmx(_q, _x, _y); + return dotprod_rrrf_execute_sse(_q, _x, _y); } - return dotprod_rrrf_execute_mmx4(_q, _x, _y); + return dotprod_rrrf_execute_sse4(_q, _x, _y); } -// use MMX/SSE extensions -int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, +// use SSE extensions +int dotprod_rrrf_execute_sse(dotprod_rrrf _q, float * _x, float * _y) { @@ -235,7 +231,7 @@ int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, // compute multiplication s = _mm_mul_ps(v, h); - + // parallel addition sum = _mm_add_ps( sum, s ); } @@ -267,8 +263,8 @@ int dotprod_rrrf_execute_mmx(dotprod_rrrf _q, return LIQUID_OK; } -// use MMX/SSE extensions, unrolled loop -int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, +// use SSE extensions, unrolled loop +int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, float * _x, float * _y) { @@ -278,10 +274,7 @@ int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, __m128 s0, s1, s2, s3; // load zeros into sum registers - __m128 sum0 = _mm_setzero_ps(); - __m128 sum1 = _mm_setzero_ps(); - __m128 sum2 = _mm_setzero_ps(); - __m128 sum3 = _mm_setzero_ps(); + __m128 sum = _mm_setzero_ps(); // r = 4*floor(n/16) unsigned int r = (_q->n >> 4) << 2; @@ -308,32 +301,27 @@ int dotprod_rrrf_execute_mmx4(dotprod_rrrf _q, s3 = _mm_mul_ps(v3, h3); // parallel addition - sum0 = _mm_add_ps( sum0, s0 ); - sum1 = _mm_add_ps( sum1, s1 ); - sum2 = _mm_add_ps( sum2, s2 ); - sum3 = _mm_add_ps( sum3, s3 ); + sum = _mm_add_ps( sum, s0 ); + sum = _mm_add_ps( sum, s1 ); + sum = _mm_add_ps( sum, s2 ); + sum = _mm_add_ps( sum, s3 ); } - // fold down into single 4-element register - sum0 = _mm_add_ps( sum0, sum1 ); - sum2 = _mm_add_ps( sum2, sum3 ); - sum0 = _mm_add_ps( sum0, sum2); - // aligned output array float w[4] __attribute__((aligned(16))); #if HAVE_SSE3 // SSE3: fold down to single value using _mm_hadd_ps() __m128 z = _mm_setzero_ps(); - sum0 = _mm_hadd_ps(sum0, z); - sum0 = _mm_hadd_ps(sum0, z); + sum = _mm_hadd_ps(sum, z); + sum = _mm_hadd_ps(sum, z); // unload single (lower value) - _mm_store_ss(w, sum0); + _mm_store_ss(w, sum); float total = w[0]; #else // SSE2 and below: unload packed array and perform manual sum - _mm_store_ps(w, sum0); + _mm_store_ps(w, sum); float total = w[0] + w[1] + w[2] + w[3]; #endif diff --git a/src/dotprod/src/dotprod_rrrf.sse4.c b/src/dotprod/src/dotprod_rrrf.sse4.c deleted file mode 100644 index 5bddc12d5..000000000 --- a/src/dotprod/src/dotprod_rrrf.sse4.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// Floating-point dot product (SSE4.1/2) -// - -#include -#include -#include -#include - -#include "liquid.internal.h" - -// include proper SIMD extensions for x86 platforms -// NOTE: these pre-processor macros are defined in config.h - -#if 0 -#include // MMX -#include // SSE -#include // SSE2 -#include // SSE3 -#endif -#include // SSE4.1/2 - -#define DEBUG_DOTPROD_RRRF_SSE4 0 - -// internal methods -int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, - float * _x, - float * _y); -int dotprod_rrrf_execute_sse4u(dotprod_rrrf _q, - float * _x, - float * _y); - -// basic dot product (ordinal calculation) -int dotprod_rrrf_run(float * _h, - float * _x, - unsigned int _n, - float * _y) -{ - float r=0; - unsigned int i; - for (i=0; i<_n; i++) - r += _h[i] * _x[i]; - *_y = r; - return LIQUID_OK; -} - -// basic dot product (ordinal calculation) with loop unrolled -int dotprod_rrrf_run4(float * _h, - float * _x, - unsigned int _n, - float * _y) -{ - float r=0; - - // t = 4*(floor(_n/4)) - unsigned int t=(_n>>2)<<2; - - // compute dotprod in groups of 4 - unsigned int i; - for (i=0; in = _n; - - // allocate memory for coefficients, 16-byte aligned - q->h = (float*) _mm_malloc( q->n*sizeof(float), 16); - - // set coefficients - unsigned int i; - for (i=0; in; i++) - q->h[i] = _h[_rev ? q->n-i-1 : i]; - - // return object - return q; -} - -dotprod_rrrf dotprod_rrrf_create(float * _h, - unsigned int _n) -{ - return dotprod_rrrf_create_opt(_h, _n, 0); -} - -dotprod_rrrf dotprod_rrrf_create_rev(float * _h, - unsigned int _n) -{ - return dotprod_rrrf_create_opt(_h, _n, 1); -} - -// re-create the structured dotprod object -dotprod_rrrf dotprod_rrrf_recreate(dotprod_rrrf _q, - float * _h, - unsigned int _n) -{ - // completely destroy and re-create dotprod object - dotprod_rrrf_destroy(_q); - return dotprod_rrrf_create(_h,_n); -} - -// re-create the structured dotprod object, coefficients reversed -dotprod_rrrf dotprod_rrrf_recreate_rev(dotprod_rrrf _q, - float * _h, - unsigned int _n) -{ - // completely destroy and re-create dotprod object - dotprod_rrrf_destroy(_q); - return dotprod_rrrf_create_rev(_h,_n); -} - -dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) -{ - // validate input - if (q_orig == NULL) - return liquid_error_config("dotprod_rrrf_copy().sse4, object cannot be NULL"); - - dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); - q_copy->n = q_orig->n; - - // allocate memory for coefficients, 16-byte aligned - q_copy->h = (float*) _mm_malloc( q_copy->n*sizeof(float), 16 ); - - // copy coefficients array - memmove(q_copy->h, q_orig->h, q_orig->n*sizeof(float)); - - // return object - return q_copy; -} - -int dotprod_rrrf_destroy(dotprod_rrrf _q) -{ - _mm_free(_q->h); - free(_q); - return LIQUID_OK; -} - -int dotprod_rrrf_print(dotprod_rrrf _q) -{ - printf("dotprod_rrrf [sse4.1/4.2, %u coefficients]\n", _q->n); - unsigned int i; - for (i=0; i<_q->n; i++) - printf("%3u : %12.9f\n", i, _q->h[i]); - return LIQUID_OK; -} - -// -int dotprod_rrrf_execute(dotprod_rrrf _q, - float * _x, - float * _y) -{ - // switch based on size - if (_q->n < 16) { - return dotprod_rrrf_execute_sse4(_q, _x, _y); - } - return dotprod_rrrf_execute_sse4u(_q, _x, _y); -} - -// use MMX/SSE extensions -int dotprod_rrrf_execute_sse4(dotprod_rrrf _q, - float * _x, - float * _y) -{ - __m128 v; // input vector - __m128 h; // coefficients vector - __m128 s; // dot product - __m128 sum = _mm_setzero_ps(); // load zeros into sum register - - // t = 4*(floor(_n/4)) - unsigned int t = (_q->n >> 2) << 2; - - // - unsigned int i; - for (i=0; ih[i]); - - // compute dot product - s = _mm_dp_ps(v, h, 0xff); - - // parallel addition - sum = _mm_add_ps( sum, s ); - } - - // aligned output array - float w[4] __attribute__((aligned(16))); - - // unload packed array - _mm_store_ps(w, sum); - float total = w[0]; - - // cleanup - for (; i<_q->n; i++) - total += _x[i] * _q->h[i]; - - // set return value - *_y = total; - return LIQUID_OK; -} - -// use MMX/SSE extensions (unrolled) -int dotprod_rrrf_execute_sse4u(dotprod_rrrf _q, - float * _x, - float * _y) -{ - __m128 v0, v1, v2, v3; - __m128 h0, h1, h2, h3; - __m128 s0, s1, s2, s3; - __m128 sum = _mm_setzero_ps(); // load zeros into sum register - - // t = 4*(floor(_n/16)) - unsigned int r = (_q->n >> 4) << 2; - - // - unsigned int i; - for (i=0; ih[4*i+ 0]); - h1 = _mm_load_ps(&_q->h[4*i+ 4]); - h2 = _mm_load_ps(&_q->h[4*i+ 8]); - h3 = _mm_load_ps(&_q->h[4*i+12]); - - // compute dot products - s0 = _mm_dp_ps(v0, h0, 0xff); - s1 = _mm_dp_ps(v1, h1, 0xff); - s2 = _mm_dp_ps(v2, h2, 0xff); - s3 = _mm_dp_ps(v3, h3, 0xff); - - // parallel addition - // FIXME: these additions are by far the limiting factor - sum = _mm_add_ps( sum, s0 ); - sum = _mm_add_ps( sum, s1 ); - sum = _mm_add_ps( sum, s2 ); - sum = _mm_add_ps( sum, s3 ); - } - - // aligned output array - float w[4] __attribute__((aligned(16))); - - // unload packed array - _mm_store_ps(w, sum); - float total = w[0]; - - // cleanup - for (i=4*r; i<_q->n; i++) - total += _x[i] * _q->h[i]; - - // set return value - *_y = total; - return LIQUID_OK; -} - diff --git a/src/dotprod/src/sumsq.avx.c b/src/dotprod/src/sumsq.avx.c index 76e3c59de..032903179 100644 --- a/src/dotprod/src/sumsq.avx.c +++ b/src/dotprod/src/sumsq.avx.c @@ -56,12 +56,12 @@ // sum squares, basic loop // _v : input array [size: 1 x _n] // _n : input length -float liquid_sumsqf(float * _v, - unsigned int _n) +float liquid_sumsqf_avx(float * _v, + unsigned int _n) { // first cut: ... __m256 v; // input vector - __m256 s; // dot product + __m256 s; // product __m256 sum = _mm256_setzero_ps(); // load zeros into sum register // t = 8*(floor(_n/8)) @@ -80,27 +80,94 @@ float liquid_sumsqf(float * _v, sum = _mm256_add_ps( sum, s ); } + // fold down into single value + __m256 z = _mm256_setzero_ps(); + sum = _mm256_hadd_ps(sum, z); + sum = _mm256_hadd_ps(sum, z); + // aligned output array - float total; + float w[8] __attribute__((aligned(32))); + + _mm256_store_ps(w, sum); + float total = w[0] + w[4]; + + // cleanup + for (; i<_n; i++) + total += _v[i] * _v[i]; + + // set return value + return total; +} + +// sum squares, unrolled loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf_avxu(float * _v, + unsigned int _n) +{ + // first cut: ... + __m256 v0, v1, v2, v3; // input vector + __m256 s0, s1, s2, s3; // product + __m256 sum = _mm256_setzero_ps(); // load zeros into sum register + + // t = 8*(floor(_n/32)) + unsigned int t = (_n >> 5) << 3; + + // + unsigned int i; + for (i=0; i @@ -33,10 +33,6 @@ // include proper SIMD extensions for x86 platforms // NOTE: these pre-processor macros are defined in config.h -#if HAVE_MMX -#include // MMX -#endif - #if HAVE_SSE #include // SSE #endif @@ -52,8 +48,8 @@ // sum squares, basic loop // _v : input array [size: 1 x _n] // _n : input length -float liquid_sumsqf(float * _v, - unsigned int _n) +float liquid_sumsqf_sse(float * _v, + unsigned int _n) { // first cut: ... __m128 v; // input vector @@ -102,7 +98,82 @@ float liquid_sumsqf(float * _v, return total; } -// sum squares, basic loop +// sum squares, unrolled loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf_sseu(float * _v, + unsigned int _n) +{ + // first cut: ... + __m128 v0, v1, v2, v3; // input vector + __m128 s0, s1, s2, s3; // product + __m128 sum = _mm_setzero_ps(); // load zeros into sum register + + // t = 4*(floor(_n/16)) + unsigned int t = (_n >> 4) << 2; + + // + unsigned int i; + for (i=0; i Date: Sat, 25 Mar 2023 21:42:21 +0000 Subject: [PATCH 145/186] Fix typo in sumsq.avx.c header Add sumsq AVX512-F implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 2 +- src/dotprod/src/sumsq.avx.c | 2 +- src/dotprod/src/sumsq.avx512f.c | 164 ++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/dotprod/src/sumsq.avx512f.c diff --git a/configure.ac b/configure.ac index 33742fcee..d9c1286ed 100644 --- a/configure.ac +++ b/configure.ac @@ -179,7 +179,7 @@ else MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ src/dotprod/src/dotprod_crcf.avx.o \ src/dotprod/src/dotprod_rrrf.avx.o \ - src/dotprod/src/sumsq.avx.o" + src/dotprod/src/sumsq.avx512f.o" ARCH_OPTION='-mavx512f' elif [ test "$ax_cv_have_avx2_ext" = yes ]; then # AVX2 extensions diff --git a/src/dotprod/src/sumsq.avx.c b/src/dotprod/src/sumsq.avx.c index 032903179..88685fca7 100644 --- a/src/dotprod/src/sumsq.avx.c +++ b/src/dotprod/src/sumsq.avx.c @@ -21,7 +21,7 @@ */ // -// sumsq.mmx.c : floating-point sum of squares (MMX) +// sumsq.avx.c : floating-point sum of squares (AVX) // #include diff --git a/src/dotprod/src/sumsq.avx512f.c b/src/dotprod/src/sumsq.avx512f.c new file mode 100644 index 000000000..ad9c75305 --- /dev/null +++ b/src/dotprod/src/sumsq.avx512f.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2007 - 2015 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// sumsq.avx512f.c : floating-point sum of squares (AVX512-F) +// + +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#if HAVE_MMX +#include // MMX +#endif + +#if HAVE_SSE +#include // SSE +#endif + +#if HAVE_SSE2 +#include // SSE2 +#endif + +#if HAVE_SSE3 +#include // SSE3 +#endif + +#if HAVE_AVX +#include // AVX +#endif + +// sum squares, basic loop +// _v : input array [size: 1 x _n] +// _n : input length +float liquid_sumsqf_avx(float * _v, + unsigned int _n) +{ + // first cut: ... + __m512 v; // input vector + __m512 s; // product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/16)) + unsigned int t = (_n >> 4) << 4; + + // + unsigned int i; + for (i=0; i> 6) << 4; + + // + unsigned int i; + for (i=0; i Date: Sat, 25 Mar 2023 22:57:43 +0000 Subject: [PATCH 146/186] Add AVX512-F dotprod implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Silva --- configure.ac | 6 +- library.json | 1 + src/dotprod/src/dotprod_cccf.avx512f.c | 406 +++++++++++++++++++++++++ src/dotprod/src/dotprod_crcf.avx512f.c | 324 ++++++++++++++++++++ src/dotprod/src/dotprod_rrrf.avx512f.c | 292 ++++++++++++++++++ 5 files changed, 1026 insertions(+), 3 deletions(-) create mode 100644 src/dotprod/src/dotprod_cccf.avx512f.c create mode 100644 src/dotprod/src/dotprod_crcf.avx512f.c create mode 100644 src/dotprod/src/dotprod_rrrf.avx512f.c diff --git a/configure.ac b/configure.ac index d9c1286ed..34ab3dfc3 100644 --- a/configure.ac +++ b/configure.ac @@ -176,9 +176,9 @@ else if [ test "$ax_cv_have_avx512f_ext" = yes ]; then # AVX512 extensions - MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx.o \ - src/dotprod/src/dotprod_crcf.avx.o \ - src/dotprod/src/dotprod_rrrf.avx.o \ + MLIBS_DOTPROD="src/dotprod/src/dotprod_cccf.avx512f.o \ + src/dotprod/src/dotprod_crcf.avx512f.o \ + src/dotprod/src/dotprod_rrrf.avx512f.o \ src/dotprod/src/sumsq.avx512f.o" ARCH_OPTION='-mavx512f' elif [ test "$ax_cv_have_avx2_ext" = yes ]; then diff --git a/library.json b/library.json index 753bb1a00..5a05e3128 100644 --- a/library.json +++ b/library.json @@ -42,6 +42,7 @@ "-<*/src/*.neon.c>", "-<*/src/*.sse.c>", "-<*/src/*.avx.c>", + "-<*/src/*.avx512f.c>", "-<*/src/*.x86.s>" ] }, diff --git a/src/dotprod/src/dotprod_cccf.avx512f.c b/src/dotprod/src/dotprod_cccf.avx512f.c new file mode 100644 index 000000000..abb7768c6 --- /dev/null +++ b/src/dotprod/src/dotprod_cccf.avx512f.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX512-F) +// + +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#include // AVX + +#define DEBUG_DOTPROD_CCCF_AVX 0 + +// forward declaration of internal methods +int dotprod_cccf_execute_avx512f(dotprod_cccf _q, + float complex * _x, + float complex * _y); + +int dotprod_cccf_execute_avx512f4(dotprod_cccf _q, + float complex * _x, + float complex * _y); + +// basic dot product (ordinal calculation) +int dotprod_cccf_run(float complex * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_cccf_run4(float complex * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 64-byte aligned + q->hi = (float*) _mm_malloc( 2*q->n*sizeof(float), 64 ); + q->hq = (float*) _mm_malloc( 2*q->n*sizeof(float), 64 ); + + // set coefficients, repeated + // hi = { crealf(_h[0]), crealf(_h[0]), ... crealf(_h[n-1]), crealf(_h[n-1])} + // hq = { cimagf(_h[0]), cimagf(_h[0]), ... cimagf(_h[n-1]), cimagf(_h[n-1])} + unsigned int i; + for (i=0; in; i++) { + unsigned int k = _rev ? q->n-i-1 : i; + q->hi[2*i+0] = crealf(_h[k]); + q->hi[2*i+1] = crealf(_h[k]); + + q->hq[2*i+0] = cimagf(_h[k]); + q->hq[2*i+1] = cimagf(_h[k]); + } + + // return object + return q; +} + +dotprod_cccf dotprod_cccf_create(float complex * _h, + unsigned int _n) +{ + return dotprod_cccf_create_opt(_h, _n, 0); +} + +dotprod_cccf dotprod_cccf_create_rev(float complex * _h, + unsigned int _n) +{ + return dotprod_cccf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_cccf dotprod_cccf_recreate(dotprod_cccf _q, + float complex * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_cccf_destroy(_q); + return dotprod_cccf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_cccf dotprod_cccf_recreate_rev(dotprod_cccf _q, + float complex * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_cccf_destroy(_q); + return dotprod_cccf_create_rev(_h,_n); +} + +dotprod_cccf dotprod_cccf_copy(dotprod_cccf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_cccf_copy().avx512f, object cannot be NULL"); + + dotprod_cccf q_copy = (dotprod_cccf)malloc(sizeof(struct dotprod_cccf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 64-byte aligned (repeated) + q_copy->hi = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 64 ); + q_copy->hq = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 64 ); + + // copy coefficients array (repeated) + // hi = { crealf(_h[0]), crealf(_h[0]), ... crealf(_h[n-1]), crealf(_h[n-1])} + // hq = { cimagf(_h[0]), cimagf(_h[0]), ... cimagf(_h[n-1]), cimagf(_h[n-1])} + memmove(q_copy->hi, q_orig->hi, 2*q_orig->n*sizeof(float)); + memmove(q_copy->hq, q_orig->hq, 2*q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + +int dotprod_cccf_destroy(dotprod_cccf _q) +{ + _mm_free(_q->hi); + _mm_free(_q->hq); + free(_q); + return LIQUID_OK; +} + +int dotprod_cccf_print(dotprod_cccf _q) +{ + printf("dotprod_cccf [avx512f, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf(" %3u : %12.9f +j%12.9f\n", i, _q->hi[i], _q->hq[i]); + return LIQUID_OK; +} + +// execute structured dot product +// _q : dotprod object +// _x : input array +// _y : output sample +int dotprod_cccf_execute(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // switch based on size + if (_q->n < 128) { + return dotprod_cccf_execute_avx512f(_q, _x, _y); + } + return dotprod_cccf_execute_avx512f4(_q, _x, _y); +} + +// use AVX512-F extensions +// +// (a + jb)(c + jd) = (ac - bd) + j(ad + bc) +// +// mm_x = { x[0].real, x[0].imag, x[1].real, x[1].imag, x[2].real, x[2].imag, x[3].real, x[3].imag } +// mm_hi = { h[0].real, h[0].real, h[1].real, h[1].real, h[2].real, h[2].real, h[3].real, h[3].real } +// mm_hq = { h[0].imag, h[0].imag, h[1].imag, h[1].imag, h[2].imag, h[2].imag, h[3].imag, h[3].imag } +// +// mm_y0 = mm_x * mm_hi +// = { x[0].real * h[0].real, +// x[0].imag * h[0].real, +// x[1].real * h[1].real, +// x[1].imag * h[1].real, +// x[2].real * h[2].real, +// x[2].imag * h[2].real, +// x[3].real * h[3].real, +// x[3].imag * h[3].real }; +// +// mm_y1 = mm_x * mm_hq +// = { x[0].real * h[0].imag, +// x[0].imag * h[0].imag, +// x[1].real * h[1].imag, +// x[1].imag * h[1].imag, +// x[2].real * h[2].imag, +// x[2].imag * h[2].imag, +// x[3].real * h[3].imag, +// x[3].imag * h[3].imag }; +// +int dotprod_cccf_execute_avx512f(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // temporary buffers + __m512 v; // input vector + __m512 hi; // coefficients vector (real) + __m512 hq; // coefficients vector (imag) + __m512 ci; // output multiplication (v * hi) + __m512 cq; // output multiplication (v * hq) + + __m512 s; // dot product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + __m512 one = _mm512_set1_ps(1.0f); // load ones into register + + // t = 16*(floor(_n/16)) + unsigned int t = (n >> 4) << 4; + + // + unsigned int i; + for (i=0; ihi[i]); + hq = _mm512_load_ps(&_q->hq[i]); + + // compute parallel multiplications + ci = _mm512_mul_ps(v, hi); + cq = _mm512_mul_ps(v, hq); + + // shuffle values + cq = _mm512_shuffle_ps( cq, cq, _MM_SHUFFLE(2,3,0,1) ); + + // combine using addsub_ps() + s = _mm512_fmaddsub_ps( ci, one, cq ); + + // accumulate + sum = _mm512_add_ps(sum, s); + } + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sum); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sum); + + //float complex total = *((float complex*)w); + float complex total = w[0] + w[1] * _Complex_I; + + // cleanup + for (i=t/2; i<_q->n; i++) + total += _x[i] * ( _q->hi[2*i] + _q->hq[2*i]*_Complex_I ); + + // set return value + *_y = total; + return LIQUID_OK; +} + +// use AVX512-F extensions +int dotprod_cccf_execute_avx512f4(dotprod_cccf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m512 v0, v1, v2, v3; // input vectors + __m512 hi0, hi1, hi2, hi3; // coefficients vectors (real) + __m512 hq0, hq1, hq2, hq3; // coefficients vectors (imag) + __m512 ci0, ci1, ci2, ci3; // output multiplications (v * hi) + __m512 cq0, cq1, cq2, cq3; // output multiplications (v * hq) + + // load zeros into sum registers + __m512 sumi = _mm512_setzero_ps(); + __m512 sumq = _mm512_setzero_ps(); + + + __m512 one = _mm512_set1_ps(1.0f); // load ones into register + + // r = 16*floor(n/64) + unsigned int r = (n >> 6) << 4; + + // + unsigned int i; + for (i=0; ihi[4*i+0]); + hi1 = _mm512_load_ps(&_q->hi[4*i+16]); + hi2 = _mm512_load_ps(&_q->hi[4*i+32]); + hi3 = _mm512_load_ps(&_q->hi[4*i+48]); + + // load real coefficients into registers (aligned) + hq0 = _mm512_load_ps(&_q->hq[4*i+0]); + hq1 = _mm512_load_ps(&_q->hq[4*i+16]); + hq2 = _mm512_load_ps(&_q->hq[4*i+32]); + hq3 = _mm512_load_ps(&_q->hq[4*i+48]); + + // compute parallel multiplications (real) + ci0 = _mm512_mul_ps(v0, hi0); + ci1 = _mm512_mul_ps(v1, hi1); + ci2 = _mm512_mul_ps(v2, hi2); + ci3 = _mm512_mul_ps(v3, hi3); + + // compute parallel multiplications (imag) + cq0 = _mm512_mul_ps(v0, hq0); + cq1 = _mm512_mul_ps(v1, hq1); + cq2 = _mm512_mul_ps(v2, hq2); + cq3 = _mm512_mul_ps(v3, hq3); + + // accumulate + sumi = _mm512_add_ps(sumi, ci0); sumq = _mm512_add_ps(sumq, cq0); + sumi = _mm512_add_ps(sumi, ci1); sumq = _mm512_add_ps(sumq, cq1); + sumi = _mm512_add_ps(sumi, ci2); sumq = _mm512_add_ps(sumq, cq2); + sumi = _mm512_add_ps(sumi, ci3); sumq = _mm512_add_ps(sumq, cq3); + } + + // shuffle values + sumq = _mm512_shuffle_ps( sumq, sumq, _MM_SHUFFLE(2,3,0,1) ); + + // combine using addsub_ps() + sumi = _mm512_fmaddsub_ps( sumi, one, sumq ); + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sumi); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sumi); + + float complex total = w[0] + w[1] * _Complex_I; + + // cleanup (note: n _must_ be even) + // TODO : clean this method up + for (i=2*r; i<_q->n; i++) { + total += _x[i] * ( _q->hi[2*i] + _q->hq[2*i]*_Complex_I ); + } + + // set return value + *_y = total; + return LIQUID_OK; +} + diff --git a/src/dotprod/src/dotprod_crcf.avx512f.c b/src/dotprod/src/dotprod_crcf.avx512f.c new file mode 100644 index 000000000..fae077f3f --- /dev/null +++ b/src/dotprod/src/dotprod_crcf.avx512f.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX512-F) +// + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +#define DEBUG_DOTPROD_CRCF_AVX 0 + +// forward declaration of internal methods +int dotprod_crcf_execute_avx512f(dotprod_crcf _q, + float complex * _x, + float complex * _y); +int dotprod_crcf_execute_avx512f4(dotprod_crcf _q, + float complex * _x, + float complex * _y); + +// basic dot product (ordinal calculation) +int dotprod_crcf_run(float * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_crcf_run4(float * _h, + float complex * _x, + unsigned int _n, + float complex * _y) +{ + float complex r = 0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 64-byte aligned + q->h = (float*) _mm_malloc( 2*q->n*sizeof(float), 64 ); + + // set coefficients, repeated + // h = { _h[0], _h[0], _h[1], _h[1], ... _h[n-1], _h[n-1]} + unsigned int i; + for (i=0; in; i++) { + unsigned int k = _rev ? q->n-i-1 : i; + q->h[2*i+0] = _h[k]; + q->h[2*i+1] = _h[k]; + } + + // return object + return q; +} + +dotprod_crcf dotprod_crcf_create(float * _h, + unsigned int _n) +{ + return dotprod_crcf_create_opt(_h, _n, 0); +} + +dotprod_crcf dotprod_crcf_create_rev(float * _h, + unsigned int _n) +{ + return dotprod_crcf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_crcf dotprod_crcf_recreate(dotprod_crcf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_crcf_destroy(_q); + return dotprod_crcf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_crcf dotprod_crcf_recreate_rev(dotprod_crcf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_crcf_destroy(_q); + return dotprod_crcf_create_rev(_h,_n); +} + +dotprod_crcf dotprod_crcf_copy(dotprod_crcf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_crcf_copy().avx512f, object cannot be NULL"); + + dotprod_crcf q_copy = (dotprod_crcf)malloc(sizeof(struct dotprod_crcf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 64-byte aligned (repeated) + q_copy->h = (float*) _mm_malloc( 2*q_copy->n*sizeof(float), 64 ); + + // copy coefficients array (repeated) + // h = { _h[0], _h[0], _h[1], _h[1], ... _h[n-1], _h[n-1]} + memmove(q_copy->h, q_orig->h, 2*q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + + +int dotprod_crcf_destroy(dotprod_crcf _q) +{ + _mm_free(_q->h); + free(_q); + return LIQUID_OK; +} + +int dotprod_crcf_print(dotprod_crcf _q) +{ + // print coefficients to screen, skipping odd entries (due + // to repeated coefficients) + printf("dotprod_crcf [avx512f, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf(" %3u : %12.9f\n", i, _q->h[2*i]); + return LIQUID_OK; +} + +// +int dotprod_crcf_execute(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // switch based on size + if (_q->n < 128) { + return dotprod_crcf_execute_avx512f(_q, _x, _y); + } + return dotprod_crcf_execute_avx512f4(_q, _x, _y); +} + +// use AVX512-F extensions +int dotprod_crcf_execute_avx512f(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m512 v; // input vector + __m512 h; // coefficients vector + __m512 s; // dot product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/16)) + unsigned int t = (n >> 4) << 4; + + // + unsigned int i; + for (i=0; ih[i]); + + // compute multiplication + s = _mm512_mul_ps(v, h); + + // accumulate + sum = _mm512_add_ps(sum, s); + } + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sum); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sum); + + // cleanup (note: n _must_ be even) + for (; ih[i ]; + w[1] += x[i+1] * _q->h[i+1]; + } + + // set return value + *_y = w[0] + _Complex_I*w[1]; + return LIQUID_OK; +} + +// use AVX512-F extensions +int dotprod_crcf_execute_avx512f4(dotprod_crcf _q, + float complex * _x, + float complex * _y) +{ + // type cast input as floating point array + float * x = (float*) _x; + + // double effective length + unsigned int n = 2*_q->n; + + // first cut: ... + __m512 v0, v1, v2, v3; // input vectors + __m512 h0, h1, h2, h3; // coefficients vectors + __m512 s0, s1, s2, s3; // dot products [re, im, re, im] + + // load zeros into sum registers + __m512 sum = _mm512_setzero_ps(); + + // r = 16*floor(n/64) + unsigned int r = (n >> 6) << 4; + + // + unsigned int i; + for (i=0; ih[4*i+0]); + h1 = _mm512_load_ps(&_q->h[4*i+16]); + h2 = _mm512_load_ps(&_q->h[4*i+32]); + h3 = _mm512_load_ps(&_q->h[4*i+48]); + + // compute multiplication + s0 = _mm512_mul_ps(v0, h0); + s1 = _mm512_mul_ps(v1, h1); + s2 = _mm512_mul_ps(v2, h2); + s3 = _mm512_mul_ps(v3, h3); + + // parallel addition + sum = _mm512_add_ps( sum, s0 ); + sum = _mm512_add_ps( sum, s1 ); + sum = _mm512_add_ps( sum, s2 ); + sum = _mm512_add_ps( sum, s3 ); + } + + // output array + float w[2]; + + // fold down I/Q components into single value + w[0] = _mm512_mask_reduce_add_ps(0x5555, sum); + w[1] = _mm512_mask_reduce_add_ps(0xAAAA, sum); + + // cleanup (note: n _must_ be even) + for (i=4*r; ih[i ]; + w[1] += x[i+1] * _q->h[i+1]; + } + + // set return value + *_y = w[0] + w[1]*_Complex_I; + return LIQUID_OK; +} + diff --git a/src/dotprod/src/dotprod_rrrf.avx512f.c b/src/dotprod/src/dotprod_rrrf.avx512f.c new file mode 100644 index 000000000..c11a744a9 --- /dev/null +++ b/src/dotprod/src/dotprod_rrrf.avx512f.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2007 - 2022 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Floating-point dot product (AVX512-F) +// + +#include +#include +#include +#include + +#include "liquid.internal.h" + +// include proper SIMD extensions for x86 platforms +// NOTE: these pre-processor macros are defined in config.h + +#include // AVX + +#define DEBUG_DOTPROD_RRRF_AVX 0 + +// internal methods +int dotprod_rrrf_execute_avx512f(dotprod_rrrf _q, + float * _x, + float * _y); +int dotprod_rrrf_execute_avx512fu(dotprod_rrrf _q, + float * _x, + float * _y); + +// basic dot product (ordinal calculation) +int dotprod_rrrf_run(float * _h, + float * _x, + unsigned int _n, + float * _y) +{ + float r=0; + unsigned int i; + for (i=0; i<_n; i++) + r += _h[i] * _x[i]; + *_y = r; + return LIQUID_OK; +} + +// basic dot product (ordinal calculation) with loop unrolled +int dotprod_rrrf_run4(float * _h, + float * _x, + unsigned int _n, + float * _y) +{ + float r=0; + + // t = 4*(floor(_n/4)) + unsigned int t=(_n>>2)<<2; + + // compute dotprod in groups of 4 + unsigned int i; + for (i=0; in = _n; + + // allocate memory for coefficients, 64-byte aligned + q->h = (float*) _mm_malloc( q->n*sizeof(float), 64); + + // set coefficients + unsigned int i; + for (i=0; in; i++) + q->h[i] = _h[_rev ? q->n-i-1 : i]; + + // return object + return q; +} + +dotprod_rrrf dotprod_rrrf_create(float * _h, + unsigned int _n) +{ + return dotprod_rrrf_create_opt(_h, _n, 0); +} + +dotprod_rrrf dotprod_rrrf_create_rev(float * _h, + unsigned int _n) +{ + return dotprod_rrrf_create_opt(_h, _n, 1); +} + +// re-create the structured dotprod object +dotprod_rrrf dotprod_rrrf_recreate(dotprod_rrrf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_rrrf_destroy(_q); + return dotprod_rrrf_create(_h,_n); +} + +// re-create the structured dotprod object, coefficients reversed +dotprod_rrrf dotprod_rrrf_recreate_rev(dotprod_rrrf _q, + float * _h, + unsigned int _n) +{ + // completely destroy and re-create dotprod object + dotprod_rrrf_destroy(_q); + return dotprod_rrrf_create_rev(_h,_n); +} + +dotprod_rrrf dotprod_rrrf_copy(dotprod_rrrf q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("dotprod_rrrf_copy().avx512f, object cannot be NULL"); + + dotprod_rrrf q_copy = (dotprod_rrrf)malloc(sizeof(struct dotprod_rrrf_s)); + q_copy->n = q_orig->n; + + // allocate memory for coefficients, 64-byte aligned + q_copy->h = (float*) _mm_malloc( q_copy->n*sizeof(float), 64 ); + + // copy coefficients array + memmove(q_copy->h, q_orig->h, q_orig->n*sizeof(float)); + + // return object + return q_copy; +} + +int dotprod_rrrf_destroy(dotprod_rrrf _q) +{ + _mm_free(_q->h); + free(_q); + return LIQUID_OK; +} + +int dotprod_rrrf_print(dotprod_rrrf _q) +{ + printf("dotprod_rrrf [avx512f, %u coefficients]\n", _q->n); + unsigned int i; + for (i=0; i<_q->n; i++) + printf("%3u : %12.9f\n", i, _q->h[i]); + return LIQUID_OK; +} + +// +int dotprod_rrrf_execute(dotprod_rrrf _q, + float * _x, + float * _y) +{ + // switch based on size + if (_q->n < 64) { + return dotprod_rrrf_execute_avx512f(_q, _x, _y); + } + return dotprod_rrrf_execute_avx512fu(_q, _x, _y); +} + +// use AVX512-F extensions +int dotprod_rrrf_execute_avx512f(dotprod_rrrf _q, + float * _x, + float * _y) +{ + __m512 v; // input vector + __m512 h; // coefficients vector + __m512 s; // dot product + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/16)) + unsigned int t = (_q->n >> 4) << 4; + + // + unsigned int i; + for (i=0; ih[i]); + + // compute dot product + s = _mm512_mul_ps(v, h); + + // parallel addition + sum = _mm512_add_ps( sum, s ); + } + + // fold down into single value + float total = _mm512_reduce_add_ps(sum); + + // cleanup + for (; i<_q->n; i++) + total += _x[i] * _q->h[i]; + + // set return value + *_y = total; + return LIQUID_OK; +} + +// use AVX512-F extensions (unrolled) +int dotprod_rrrf_execute_avx512fu(dotprod_rrrf _q, + float * _x, + float * _y) +{ + __m512 v0, v1, v2, v3; + __m512 h0, h1, h2, h3; + __m512 s0, s1, s2, s3; + __m512 sum = _mm512_setzero_ps(); // load zeros into sum register + + // t = 16*(floor(_n/64)) + unsigned int r = (_q->n >> 6) << 4; + + // + unsigned int i; + for (i=0; ih[4*i+ 0]); + h1 = _mm512_load_ps(&_q->h[4*i+16]); + h2 = _mm512_load_ps(&_q->h[4*i+32]); + h3 = _mm512_load_ps(&_q->h[4*i+48]); + + // compute dot products + s0 = _mm512_mul_ps(v0, h0); + s1 = _mm512_mul_ps(v1, h1); + s2 = _mm512_mul_ps(v2, h2); + s3 = _mm512_mul_ps(v3, h3); + + // parallel addition + sum = _mm512_add_ps( sum, s0 ); + sum = _mm512_add_ps( sum, s1 ); + sum = _mm512_add_ps( sum, s2 ); + sum = _mm512_add_ps( sum, s3 ); + } + + // fold down into single value + float total = _mm512_reduce_add_ps(sum); + + // cleanup + for (i=4*r; i<_q->n; i++) + total += _x[i] * _q->h[i]; + + // set return value + *_y = total; + return LIQUID_OK; +} + From 2b27de22f957e2284b39f9c4c60d42d7d97fbcf5 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 30 Mar 2023 17:17:14 -0400 Subject: [PATCH 147/186] benchmark compare: specifying argument type as float --- scripts/benchmark_compare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/benchmark_compare.py b/scripts/benchmark_compare.py index 328e134c1..0fdc7295f 100755 --- a/scripts/benchmark_compare.py +++ b/scripts/benchmark_compare.py @@ -6,7 +6,7 @@ p = argparse.ArgumentParser(description=__doc__) p.add_argument('old', help='old benchmark file (.json)') p.add_argument('new', help='new benchmark file (.json)') -p.add_argument('-thresh', default=1.5, help='threshold for displaying deltas') +p.add_argument('-thresh', default=1.5, type=float, help='threshold for displaying deltas') args = p.parse_args() # load json files From 05d93374ae4957d6da01168cb5f7dd4017c4efe9 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 15 Apr 2023 16:03:03 -0400 Subject: [PATCH 148/186] fec/autotest: cleaning up warning when libfec is not installed --- src/fec/tests/fec_autotest.c | 40 +++++++++++++++---------------- src/fec/tests/fec_copy_autotest.c | 40 +++++++++++++++---------------- src/fec/tests/fec_soft_autotest.c | 40 +++++++++++++++---------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/fec/tests/fec_autotest.c b/src/fec/tests/fec_autotest.c index df1bdfffa..d03712ce1 100644 --- a/src/fec/tests/fec_autotest.c +++ b/src/fec/tests/fec_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,25 +29,25 @@ void fec_test_codec(fec_scheme _fs, unsigned int _n, void * _opts) { #if !LIBFEC_ENABLED - if ( _fs == LIQUID_FEC_CONV_V27 || - _fs == LIQUID_FEC_CONV_V29 || - _fs == LIQUID_FEC_CONV_V39 || - _fs == LIQUID_FEC_CONV_V615 || - _fs == LIQUID_FEC_CONV_V27P23 || - _fs == LIQUID_FEC_CONV_V27P34 || - _fs == LIQUID_FEC_CONV_V27P45 || - _fs == LIQUID_FEC_CONV_V27P56 || - _fs == LIQUID_FEC_CONV_V27P67 || - _fs == LIQUID_FEC_CONV_V27P78 || - _fs == LIQUID_FEC_CONV_V29P23 || - _fs == LIQUID_FEC_CONV_V29P34 || - _fs == LIQUID_FEC_CONV_V29P45 || - _fs == LIQUID_FEC_CONV_V29P56 || - _fs == LIQUID_FEC_CONV_V29P67 || - _fs == LIQUID_FEC_CONV_V29P78 || - _fs == LIQUID_FEC_RS_M8) - { - AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)\n"); + switch (_fs) { + case LIQUID_FEC_CONV_V27: + case LIQUID_FEC_CONV_V29: + case LIQUID_FEC_CONV_V39: + case LIQUID_FEC_CONV_V615: + case LIQUID_FEC_CONV_V27P23: + case LIQUID_FEC_CONV_V27P34: + case LIQUID_FEC_CONV_V27P45: + case LIQUID_FEC_CONV_V27P56: + case LIQUID_FEC_CONV_V27P67: + case LIQUID_FEC_CONV_V27P78: + case LIQUID_FEC_CONV_V29P23: + case LIQUID_FEC_CONV_V29P34: + case LIQUID_FEC_CONV_V29P45: + case LIQUID_FEC_CONV_V29P56: + case LIQUID_FEC_CONV_V29P67: + case LIQUID_FEC_CONV_V29P78: + case LIQUID_FEC_RS_M8: + AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; } #endif diff --git a/src/fec/tests/fec_copy_autotest.c b/src/fec/tests/fec_copy_autotest.c index 34344eb5b..bcb2d1ca3 100644 --- a/src/fec/tests/fec_copy_autotest.c +++ b/src/fec/tests/fec_copy_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,25 +29,25 @@ void fec_test_copy(fec_scheme _fs) { #if !LIBFEC_ENABLED - if ( _fs == LIQUID_FEC_CONV_V27 || - _fs == LIQUID_FEC_CONV_V29 || - _fs == LIQUID_FEC_CONV_V39 || - _fs == LIQUID_FEC_CONV_V615 || - _fs == LIQUID_FEC_CONV_V27P23 || - _fs == LIQUID_FEC_CONV_V27P34 || - _fs == LIQUID_FEC_CONV_V27P45 || - _fs == LIQUID_FEC_CONV_V27P56 || - _fs == LIQUID_FEC_CONV_V27P67 || - _fs == LIQUID_FEC_CONV_V27P78 || - _fs == LIQUID_FEC_CONV_V29P23 || - _fs == LIQUID_FEC_CONV_V29P34 || - _fs == LIQUID_FEC_CONV_V29P45 || - _fs == LIQUID_FEC_CONV_V29P56 || - _fs == LIQUID_FEC_CONV_V29P67 || - _fs == LIQUID_FEC_CONV_V29P78 || - _fs == LIQUID_FEC_RS_M8) - { - AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)\n"); + switch (_fs) { + case LIQUID_FEC_CONV_V27: + case LIQUID_FEC_CONV_V29: + case LIQUID_FEC_CONV_V39: + case LIQUID_FEC_CONV_V615: + case LIQUID_FEC_CONV_V27P23: + case LIQUID_FEC_CONV_V27P34: + case LIQUID_FEC_CONV_V27P45: + case LIQUID_FEC_CONV_V27P56: + case LIQUID_FEC_CONV_V27P67: + case LIQUID_FEC_CONV_V27P78: + case LIQUID_FEC_CONV_V29P23: + case LIQUID_FEC_CONV_V29P34: + case LIQUID_FEC_CONV_V29P45: + case LIQUID_FEC_CONV_V29P56: + case LIQUID_FEC_CONV_V29P67: + case LIQUID_FEC_CONV_V29P78: + case LIQUID_FEC_RS_M8: + AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; } #endif diff --git a/src/fec/tests/fec_soft_autotest.c b/src/fec/tests/fec_soft_autotest.c index 34c92803d..0ef350493 100644 --- a/src/fec/tests/fec_soft_autotest.c +++ b/src/fec/tests/fec_soft_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,25 +32,25 @@ void fec_test_soft_codec(fec_scheme _fs, void * _opts) { #if !LIBFEC_ENABLED - if ( _fs == LIQUID_FEC_CONV_V27 || - _fs == LIQUID_FEC_CONV_V29 || - _fs == LIQUID_FEC_CONV_V39 || - _fs == LIQUID_FEC_CONV_V615 || - _fs == LIQUID_FEC_CONV_V27P23 || - _fs == LIQUID_FEC_CONV_V27P34 || - _fs == LIQUID_FEC_CONV_V27P45 || - _fs == LIQUID_FEC_CONV_V27P56 || - _fs == LIQUID_FEC_CONV_V27P67 || - _fs == LIQUID_FEC_CONV_V27P78 || - _fs == LIQUID_FEC_CONV_V29P23 || - _fs == LIQUID_FEC_CONV_V29P34 || - _fs == LIQUID_FEC_CONV_V29P45 || - _fs == LIQUID_FEC_CONV_V29P56 || - _fs == LIQUID_FEC_CONV_V29P67 || - _fs == LIQUID_FEC_CONV_V29P78 || - _fs == LIQUID_FEC_RS_M8) - { - AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)\n"); + switch (_fs) { + case LIQUID_FEC_CONV_V27: + case LIQUID_FEC_CONV_V29: + case LIQUID_FEC_CONV_V39: + case LIQUID_FEC_CONV_V615: + case LIQUID_FEC_CONV_V27P23: + case LIQUID_FEC_CONV_V27P34: + case LIQUID_FEC_CONV_V27P45: + case LIQUID_FEC_CONV_V27P56: + case LIQUID_FEC_CONV_V27P67: + case LIQUID_FEC_CONV_V27P78: + case LIQUID_FEC_CONV_V29P23: + case LIQUID_FEC_CONV_V29P34: + case LIQUID_FEC_CONV_V29P45: + case LIQUID_FEC_CONV_V29P56: + case LIQUID_FEC_CONV_V29P67: + case LIQUID_FEC_CONV_V29P78: + case LIQUID_FEC_RS_M8: + AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; } #endif From fed7a0178de8a2501ef213a76d0da3c4f13682e8 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 14 May 2023 16:06:29 -0400 Subject: [PATCH 149/186] migrating to macro/prototype pattern --- makefile.in | 9 +- src/framing/src/qdsync.proto.c | 423 +++++++++++++++++++++++++++++++++ src/framing/src/qdsync_cccf.c | 415 ++------------------------------ 3 files changed, 442 insertions(+), 405 deletions(-) create mode 100644 src/framing/src/qdsync.proto.c diff --git a/makefile.in b/makefile.in index d92ca74c8..12e6b5496 100644 --- a/makefile.in +++ b/makefile.in @@ -651,14 +651,14 @@ framing_objects := \ src/framing/src/ofdmflexframegen.o \ src/framing/src/ofdmflexframesync.o \ src/framing/src/presync_cccf.o \ - src/framing/src/symstreamcf.o \ - src/framing/src/symstreamrcf.o \ - src/framing/src/symtrack_cccf.o \ src/framing/src/qdetector_cccf.o \ src/framing/src/qdsync_cccf.o \ src/framing/src/qpacketmodem.o \ src/framing/src/qpilotgen.o \ src/framing/src/qpilotsync.o \ + src/framing/src/symstreamcf.o \ + src/framing/src/symstreamrcf.o \ + src/framing/src/symtrack_cccf.o \ # list explicit targets and dependencies here @@ -682,10 +682,11 @@ src/framing/src/msourcecf.o : %.o : %.c $(include_headers) src/framing/s src/framing/src/ofdmflexframegen.o : %.o : %.c $(include_headers) src/framing/src/ofdmflexframesync.o : %.o : %.c $(include_headers) src/framing/src/presync_cccf.o : %.o : %.c $(include_headers) src/framing/src/presync.proto.c -src/framing/src/qpacketmodem.o : %.o : %.c $(include_headers) src/framing/src/symstreamcf.o : %.o : %.c $(include_headers) src/framing/src/symstream.proto.c src/framing/src/symstreamrcf.o : %.o : %.c $(include_headers) src/framing/src/symstreamr.proto.c src/framing/src/symtrack_cccf.o : %.o : %.c $(include_headers) src/framing/src/symtrack.proto.c +src/framing/src/qdsync_cccf.o : %.o : %.c $(include_headers) src/framing/src/qdsync.proto.c +src/framing/src/qpacketmodem.o : %.o : %.c $(include_headers) framing_autotests := \ diff --git a/src/framing/src/qdsync.proto.c b/src/framing/src/qdsync.proto.c new file mode 100644 index 000000000..12bbee5c0 --- /dev/null +++ b/src/framing/src/qdsync.proto.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2007 - 2023 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Frame detector and synchronizer; uses a novel correlation method to +// detect a synchronization pattern, estimate carrier frequency and +// phase offsets as well as timing phase, then correct for these +// impairments in a simple interface suitable for custom frame recovery. + +#include +#include +#include +#include +#include + +#include "liquid.internal.h" + +// push samples through detection stage +int QDSYNC(_execute_detect)(QDSYNC() _q, float complex _x); + +// step receiver mixer, matched filter, decimator +// _q : frame synchronizer +// _x : input sample +int QDSYNC(_step)(QDSYNC() _q, float complex _x); + +// append sample to output buffer +int QDSYNC(_buf_append)(QDSYNC() _q, float complex _x); + +// main object definition +struct QDSYNC(_s) { + unsigned int seq_len; // preamble sequence length + int ftype; // filter type + unsigned int k; // samples per symbol + unsigned int m; // filter semi-length + float beta; // excess bandwidth factor + + QDSYNC(_callback) callback; // user-defined callback function + void * context; // user-defined context object + QDETECTOR() detector; // detector + + // status variables + enum { + QDSYNC_STATE_DETECT=0, // detect frame + QDSYNC_STATE_SYNC, // apply carrier offset correction and matched filter + } state; // frame synchronization state + unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence + + nco_crcf mixer; // coarse carrier frequency recovery + + // timing recovery objects, states + firpfb_crcf mf; // matched filter/decimator + unsigned int npfb; // number of filters in symsync + int mf_counter; // matched filter output timer + unsigned int pfb_index; // filterbank index + + // symbol buffer + unsigned int buf_out_len;// output buffer length + float complex * buf_out; // output buffer + unsigned int buf_out_counter; // output counter +}; + +// create detector with generic sequence +QDSYNC() QDSYNC(_create_linear)(TI * _seq, + unsigned int _seq_len, + int _ftype, + unsigned int _k, + unsigned int _m, + float _beta, + QDSYNC(_callback) _callback, + void * _context) +{ + // validate input + if (_seq_len == 0) + return liquid_error_config("QDSYNC(_create)(), sequence length cannot be zero"); + + // allocate memory for main object and set internal properties + QDSYNC() q = (QDSYNC()) malloc(sizeof(struct QDSYNC(_s))); + q->seq_len = _seq_len; + q->ftype = _ftype; + q->k = _k; + q->m = _m; + q->beta = _beta; + + // create detector + q->detector = QDETECTOR(_create_linear)(_seq, _seq_len, _ftype, _k, _m, _beta); + + // create down-coverters for carrier phase tracking + q->mixer = nco_crcf_create(LIQUID_NCO); + + // create symbol timing recovery filters + q->npfb = 256; // number of filters in the bank + q->mf = firpfb_crcf_create_rnyquist(q->ftype, q->npfb, q->k, q->m, q->beta); + + // allocate buffer for storing output samples + q->buf_out_len = 64; // user can re-size this later + q->buf_out = (float complex*) malloc(q->buf_out_len*sizeof(float complex)); + + // set callback and context values + QDSYNC(_set_callback)(q, _callback); + QDSYNC(_set_context )(q, _context ); + + // reset and return object + QDSYNC(_reset)(q); + return q; +} + +// copy object +QDSYNC() QDSYNC(_copy)(QDSYNC() q_orig) +{ + // validate input + if (q_orig == NULL) + return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); + + // create new object and copy base parameters + QDSYNC() q_copy = (QDSYNC())malloc(sizeof(struct QDSYNC(_s))); + memmove(q_copy, q_orig, sizeof(struct QDSYNC(_s))); + + // set callback and userdata fields + q_copy->callback = q_orig->callback; + q_copy->context = q_orig->context; + + // copy sub-objects + q_copy->detector = QDETECTOR(_copy)(q_orig->detector); + q_copy->mixer = nco_crcf_copy (q_orig->mixer); + q_copy->mf = firpfb_crcf_copy (q_orig->mf); + + // copy memory in new allocation + q_copy->buf_out = (float complex*)liquid_malloc_copy(q_orig->buf_out, q_orig->buf_out_len, sizeof(float complex)); + + // return new object + return q_copy; +} + +int QDSYNC(_destroy)(QDSYNC() _q) +{ + // destroy internal objects + QDETECTOR(_destroy)(_q->detector); + nco_crcf_destroy(_q->mixer); + firpfb_crcf_destroy(_q->mf); + + // free output buffer + free(_q->buf_out); + + // free main object memory + free(_q); + return LIQUID_OK; +} + +int QDSYNC(_print)(QDSYNC() _q) +{ + printf("\n"); + return LIQUID_OK; +} + +int QDSYNC(_reset)(QDSYNC() _q) +{ + QDETECTOR(_reset)(_q->detector); + _q->state = QDSYNC_STATE_DETECT; + _q->symbol_counter = 0; + _q->buf_out_counter = 0; + firpfb_crcf_reset(_q->mf); + return LIQUID_OK; +} + +// get detection threshold +float QDSYNC(_get_threshold)(QDSYNC() _q) +{ + return QDETECTOR(_get_threshold)(_q->detector); +} + +// set detection threshold +int QDSYNC(_set_threshold)(QDSYNC() _q, float _threshold) +{ + return QDETECTOR(_set_threshold)(_q->detector, _threshold); +} + +// set carrier offset search range +int QDSYNC(_set_range)(QDSYNC() _q, float _dphi_max) +{ + return QDETECTOR(_set_range)(_q->detector, _dphi_max); +} + +// set callback method +int QDSYNC(_set_callback)(QDSYNC() _q, QDSYNC(_callback) _callback) +{ + _q->callback = _callback; + return LIQUID_OK; +} + +// set context value +int QDSYNC(_set_context)(QDSYNC() _q, void * _context) +{ + _q->context = _context; + return LIQUID_OK; +} + +// Set callback buffer size (the number of symbol provided to the callback +// whenever it is invoked). +int QDSYNC(_set_buf_len)(QDSYNC() _q, unsigned int _buf_len) +{ + if (_buf_len == 0) + return liquid_error(LIQUID_EICONFIG,"QDSYNC(_set_buf_len)(), buffer length must be greater than 0"); + + // check current state + if (_q->buf_out_counter < _buf_len) { + // buffer might not be empty, but we aren't resizing within this space; + // ok to resize so long as old samples are copied + _q->buf_out_len = _buf_len; + float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + if (buf_new == NULL) + return liquid_error(LIQUID_EIMEM,"QDSYNC(_set_buf_len)(), could not allocate %u samples", _buf_len); + _q->buf_out = buf_new; + } else { + // we are shrinking the buffer below the number of samples it currently + // holds; invoke the callback as many times as needed to reduce its size + unsigned int index = 0; + while (_q->buf_out_counter >= _buf_len) { + if (_q->callback != NULL) + _q->callback(_q->buf_out + index, _buf_len, _q->context); + + // adjust counters + index += _buf_len; + _q->buf_out_counter -= _buf_len; + } + + // copy old values to front of buffer + memmove(_q->buf_out, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); + + // now resize the buffer appropriately + _q->buf_out_len = _buf_len; + float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); + if (buf_new == NULL) + return liquid_error(LIQUID_EIMEM,"QDSYNC(_set_buf_len)(), could not allocate %u samples", _buf_len); + _q->buf_out = buf_new; + } + return LIQUID_OK; +} + +int QDSYNC(_execute)(QDSYNC() _q, + TI * _buf, + unsigned int _buf_len) +{ + unsigned int i; + for (i=0; i<_buf_len; i++) { + switch (_q->state) { + case QDSYNC_STATE_DETECT: + // detect frame (look for p/n sequence) + QDSYNC(_execute_detect)(_q, _buf[i]); + break; + case QDSYNC_STATE_SYNC: + // receive preamble sequence symbols + QDSYNC(_step)(_q, _buf[i]); + break; + default: + return liquid_error(LIQUID_EINT,"QDSYNC(_exeucte)(), unknown/unsupported state"); + } + } + return LIQUID_OK; +} + +int QDSYNC(_is_open)(QDSYNC() _q) +{ + return _q->state == QDSYNC_STATE_DETECT ? 0 : 1; +} + +// correlator output +float QDSYNC(_get_rxy)(QDSYNC() _q) +{ + return QDETECTOR(_get_rxy)(_q->detector); +} + +// fractional timing offset estimate +float QDSYNC(_get_tau)(QDSYNC() _q) +{ + return QDETECTOR(_get_tau)(_q->detector); +} + +// channel gain +float QDSYNC(_get_gamma)(QDSYNC() _q) +{ + return QDETECTOR(_get_gamma)(_q->detector); +} + +// carrier frequency offset estimate +float QDSYNC(_get_dphi)(QDSYNC() _q) +{ + return QDETECTOR(_get_dphi)(_q->detector); +} + +// carrier phase offset estimate +float QDSYNC(_get_phi)(QDSYNC() _q) +{ + return QDETECTOR(_get_phi)(_q->detector); +} + +// +// internal methods +// + +// execute synchronizer, seeking preamble sequence +// _q : frame synchronizer object +// _x : input sample +// _sym : demodulated symbol +int QDSYNC(_execute_detect)(QDSYNC() _q, + TI _x) +{ + // push through pre-demod synchronizer + float complex * v = QDETECTOR(_execute)(_q->detector, _x); + + // check if frame has been detected + if (v != NULL) { + // get estimates + float tau_hat = QDETECTOR(_get_tau) (_q->detector); + float gamma_hat = QDETECTOR(_get_gamma)(_q->detector); + float dphi_hat = QDETECTOR(_get_dphi) (_q->detector); + float phi_hat = QDETECTOR(_get_phi) (_q->detector); + + // set appropriate filterbank index + _q->mf_counter = _q->k - 2; + _q->pfb_index = 0; + int index = (int)(tau_hat * _q->npfb); + if (index < 0) { + _q->mf_counter++; + index += _q->npfb; + } + _q->pfb_index = index; + //printf("* qdsync detected! tau:%6.3f, dphi:%12.4e, phi:%6.3f, gamma:%6.2f dB, mf:%u, pfb idx:%u\n", + // tau_hat, dphi_hat, phi_hat, 20*log10f(gamma_hat), _q->mf_counter, _q->pfb_index); + + // output filter scale + firpfb_crcf_set_scale(_q->mf, 1.0f / (_q->k * gamma_hat)); + + // set frequency/phase of mixer + nco_crcf_set_frequency(_q->mixer, dphi_hat); + nco_crcf_set_phase (_q->mixer, phi_hat ); + + // update state + _q->state = QDSYNC_STATE_SYNC; + + // run buffered samples through synchronizer + unsigned int buf_len = QDETECTOR(_get_buf_len)(_q->detector); + QDSYNC(_execute)(_q, v, buf_len); + } + return LIQUID_OK; +} + +// step receiver mixer, matched filter, decimator +// _q : frame synchronizer +// _x : input sample +// _y : output symbol +int QDSYNC(_step)(QDSYNC() _q, TI _x) +{ + // mix sample down + float complex v; + nco_crcf_mix_down(_q->mixer, _x, &v); + nco_crcf_step (_q->mixer); + + // push sample into filterbank + firpfb_crcf_push (_q->mf, v); + firpfb_crcf_execute(_q->mf, _q->pfb_index, &v); + + // increment counter to determine if sample is available + _q->mf_counter++; + int sample_available = (_q->mf_counter >= _q->k-1) ? 1 : 0; + + // set output sample if available + if (sample_available) { + // decrement counter by k=2 samples/symbol + _q->mf_counter -= _q->k; + + // append to output + QDSYNC(_buf_append)(_q, v); + } + + // return flag + return LIQUID_OK; +} + +// append sample to output buffer +int QDSYNC(_buf_append)(QDSYNC() _q, TO _x) +{ + // account for filter delay + _q->symbol_counter++; + if (_q->symbol_counter <= 2*_q->m) + return LIQUID_OK; + + // append sample to end of buffer + _q->buf_out[_q->buf_out_counter] = _x; + _q->buf_out_counter++; + + // check if buffer is full + if (_q->buf_out_counter == _q->buf_out_len) { + // reset counter + _q->buf_out_counter = 0; + + // invoke callback + if (_q->callback != NULL) { + int rc = _q->callback(_q->buf_out, _q->buf_out_len, _q->context); + if (rc) + return QDSYNC(_reset)(_q); + } + } + return LIQUID_OK; +} + diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c index 2dcf01fbf..28104f7a9 100644 --- a/src/framing/src/qdsync_cccf.c +++ b/src/framing/src/qdsync_cccf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,411 +20,24 @@ * THE SOFTWARE. */ -// Frame detector and synchronizer; uses a novel correlation method to -// detect a synchronization pattern, estimate carrier frequency and -// phase offsets as well as timing phase, then correct for these -// impairments in a simple interface suitable for custom frame recovery. - -#include -#include -#include -#include -#include - -#include "liquid.internal.h" - -// push samples through detection stage -int qdsync_cccf_execute_detect(qdsync_cccf _q, - float complex _x); - -// step receiver mixer, matched filter, decimator -// _q : frame synchronizer -// _x : input sample -int qdsync_cccf_step(qdsync_cccf _q, - float complex _x); - -// append sample to output buffer -int qdsync_cccf_buf_append(qdsync_cccf _q, - float complex _x); - -// main object definition -struct qdsync_cccf_s { - unsigned int seq_len; // preamble sequence length - int ftype; // filter type - unsigned int k; // samples per symbol - unsigned int m; // filter semi-length - float beta; // excess bandwidth factor - - qdsync_callback callback; // user-defined callback function - void * context; // user-defined context object - qdetector_cccf detector; // detector - - // status variables - enum { - QDSYNC_STATE_DETECT=0, // detect frame - QDSYNC_STATE_SYNC, // apply carrier offset correction and matched filter - } state; // frame synchronization state - unsigned int symbol_counter;// counter: total number of symbols received including preamble sequence - - nco_crcf mixer; // coarse carrier frequency recovery - - // timing recovery objects, states - firpfb_crcf mf; // matched filter/decimator - unsigned int npfb; // number of filters in symsync - int mf_counter; // matched filter output timer - unsigned int pfb_index; // filterbank index - - // symbol buffer - unsigned int buf_out_len;// output buffer length - float complex * buf_out; // output buffer - unsigned int buf_out_counter; // output counter -}; - -// create detector with generic sequence -qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _seq, - unsigned int _seq_len, - int _ftype, - unsigned int _k, - unsigned int _m, - float _beta, - qdsync_callback _callback, - void * _context) -{ - // validate input - if (_seq_len == 0) - return liquid_error_config("qdsync_cccf_create(), sequence length cannot be zero"); - - // allocate memory for main object and set internal properties - qdsync_cccf q = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); - q->seq_len = _seq_len; - q->ftype = _ftype; - q->k = _k; - q->m = _m; - q->beta = _beta; - - // create detector - q->detector = qdetector_cccf_create_linear(_seq, _seq_len, _ftype, _k, _m, _beta); - - // create down-coverters for carrier phase tracking - q->mixer = nco_crcf_create(LIQUID_NCO); - - // create symbol timing recovery filters - q->npfb = 256; // number of filters in the bank - q->mf = firpfb_crcf_create_rnyquist(q->ftype, q->npfb, q->k, q->m, q->beta); - - // allocate buffer for storing output samples - q->buf_out_len = 64; // user can re-size this later - q->buf_out = (float complex*) malloc(q->buf_out_len*sizeof(float complex)); - - // set callback and context values - qdsync_cccf_set_callback(q, _callback); - qdsync_cccf_set_context (q, _context ); - - // reset and return object - qdsync_cccf_reset(q); - return q; -} - -// copy object -qdsync_cccf qdsync_cccf_copy(qdsync_cccf q_orig) -{ - // validate input - if (q_orig == NULL) - return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); - - // create new object and copy base parameters - qdsync_cccf q_copy = (qdsync_cccf) malloc(sizeof(struct qdsync_cccf_s)); - memmove(q_copy, q_orig, sizeof(struct qdsync_cccf_s)); - - // set callback and userdata fields - q_copy->callback = q_orig->callback; - q_copy->context = q_orig->context; - - // copy sub-objects - q_copy->detector = qdetector_cccf_copy(q_orig->detector); - q_copy->mixer = nco_crcf_copy (q_orig->mixer); - q_copy->mf = firpfb_crcf_copy (q_orig->mf); - - // copy memory in new allocation - q_copy->buf_out = (float complex*)liquid_malloc_copy(q_orig->buf_out, q_orig->buf_out_len, sizeof(float complex)); - - // return new object - return q_copy; -} - -int qdsync_cccf_destroy(qdsync_cccf _q) -{ - // destroy internal objects - qdetector_cccf_destroy(_q->detector); - nco_crcf_destroy(_q->mixer); - firpfb_crcf_destroy(_q->mf); - - // free output buffer - free(_q->buf_out); - - // free main object memory - free(_q); - return LIQUID_OK; -} - -int qdsync_cccf_print(qdsync_cccf _q) -{ - printf("\n"); - return LIQUID_OK; -} - -int qdsync_cccf_reset(qdsync_cccf _q) -{ - qdetector_cccf_reset(_q->detector); - _q->state = QDSYNC_STATE_DETECT; - _q->symbol_counter = 0; - _q->buf_out_counter = 0; - firpfb_crcf_reset(_q->mf); - return LIQUID_OK; -} - -// get detection threshold -float qdsync_cccf_get_threshold(qdsync_cccf _q) -{ - return qdetector_cccf_get_threshold(_q->detector); -} - -// set detection threshold -int qdsync_cccf_set_threshold(qdsync_cccf _q, - float _threshold) -{ - return qdetector_cccf_set_threshold(_q->detector, _threshold); -} - -// set carrier offset search range -int qdsync_cccf_set_range(qdsync_cccf _q, - float _dphi_max) -{ - return qdetector_cccf_set_range(_q->detector, _dphi_max); -} - -// set callback method -int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback) -{ - _q->callback = _callback; - return LIQUID_OK; -} - -// set context value -int qdsync_cccf_set_context (qdsync_cccf _q, void * _context) -{ - _q->context = _context; - return LIQUID_OK; -} - -// Set callback buffer size (the number of symbol provided to the callback -// whenever it is invoked). -int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len) -{ - if (_buf_len == 0) - return liquid_error(LIQUID_EICONFIG,"qdsync_cccf_set_buf_len(), buffer length must be greater than 0"); - - // check current state - if (_q->buf_out_counter < _buf_len) { - // buffer might not be empty, but we aren't resizing within this space; - // ok to resize so long as old samples are copied - _q->buf_out_len = _buf_len; - float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); - if (buf_new == NULL) - return liquid_error(LIQUID_EIMEM,"qdsync_cccf_set_buf_len(), could not allocate %u samples", _buf_len); - _q->buf_out = buf_new; - } else { - // we are shrinking the buffer below the number of samples it currently - // holds; invoke the callback as many times as needed to reduce its size - unsigned int index = 0; - while (_q->buf_out_counter >= _buf_len) { - if (_q->callback != NULL) - _q->callback(_q->buf_out + index, _buf_len, _q->context); - - // adjust counters - index += _buf_len; - _q->buf_out_counter -= _buf_len; - } - - // copy old values to front of buffer - memmove(_q->buf_out, _q->buf_out + index, _q->buf_out_counter*sizeof(float complex)); - - // now resize the buffer appropriately - _q->buf_out_len = _buf_len; - float complex * buf_new = (float complex*)realloc(_q->buf_out, _q->buf_out_len*sizeof(float complex)); - if (buf_new == NULL) - return liquid_error(LIQUID_EIMEM,"qdsync_cccf_set_buf_len(), could not allocate %u samples", _buf_len); - _q->buf_out = buf_new; - } - return LIQUID_OK; -} - -int qdsync_cccf_execute(qdsync_cccf _q, - liquid_float_complex * _buf, - unsigned int _buf_len) -{ - unsigned int i; - for (i=0; i<_buf_len; i++) { - switch (_q->state) { - case QDSYNC_STATE_DETECT: - // detect frame (look for p/n sequence) - qdsync_cccf_execute_detect(_q, _buf[i]); - break; - case QDSYNC_STATE_SYNC: - // receive preamble sequence symbols - qdsync_cccf_step(_q, _buf[i]); - break; - default: - return liquid_error(LIQUID_EINT,"qdsync_cccf_exeucte(), unknown/unsupported state"); - } - } - return LIQUID_OK; -} - -int qdsync_cccf_is_open(qdsync_cccf _q) -{ - return _q->state == QDSYNC_STATE_DETECT ? 0 : 1; -} - -// correlator output -float qdsync_cccf_get_rxy(qdsync_cccf _q) -{ - return qdetector_cccf_get_rxy(_q->detector); -} - -// fractional timing offset estimate -float qdsync_cccf_get_tau(qdsync_cccf _q) -{ - return qdetector_cccf_get_tau(_q->detector); -} - -// channel gain -float qdsync_cccf_get_gamma(qdsync_cccf _q) -{ - return qdetector_cccf_get_gamma(_q->detector); -} - -// carrier frequency offset estimate -float qdsync_cccf_get_dphi(qdsync_cccf _q) -{ - return qdetector_cccf_get_dphi(_q->detector); -} - -// carrier phase offset estimate -float qdsync_cccf_get_phi(qdsync_cccf _q) -{ - return qdetector_cccf_get_phi(_q->detector); -} - // -// internal methods +// API: floating-point // -// execute synchronizer, seeking preamble sequence -// _q : frame synchronizer object -// _x : input sample -// _sym : demodulated symbol -int qdsync_cccf_execute_detect(qdsync_cccf _q, - float complex _x) -{ - // push through pre-demod synchronizer - float complex * v = qdetector_cccf_execute(_q->detector, _x); - - // check if frame has been detected - if (v != NULL) { - // get estimates - float tau_hat = qdetector_cccf_get_tau (_q->detector); - float gamma_hat = qdetector_cccf_get_gamma(_q->detector); - float dphi_hat = qdetector_cccf_get_dphi (_q->detector); - float phi_hat = qdetector_cccf_get_phi (_q->detector); - - // set appropriate filterbank index - _q->mf_counter = _q->k - 2; - _q->pfb_index = 0; - int index = (int)(tau_hat * _q->npfb); - if (index < 0) { - _q->mf_counter++; - index += _q->npfb; - } - _q->pfb_index = index; - //printf("* qdsync detected! tau:%6.3f, dphi:%12.4e, phi:%6.3f, gamma:%6.2f dB, mf:%u, pfb idx:%u\n", - // tau_hat, dphi_hat, phi_hat, 20*log10f(gamma_hat), _q->mf_counter, _q->pfb_index); - - // output filter scale - firpfb_crcf_set_scale(_q->mf, 1.0f / (_q->k * gamma_hat)); - - // set frequency/phase of mixer - nco_crcf_set_frequency(_q->mixer, dphi_hat); - nco_crcf_set_phase (_q->mixer, phi_hat ); - - // update state - _q->state = QDSYNC_STATE_SYNC; - - // run buffered samples through synchronizer - unsigned int buf_len = qdetector_cccf_get_buf_len(_q->detector); - qdsync_cccf_execute(_q, v, buf_len); - } - return LIQUID_OK; -} - -// step receiver mixer, matched filter, decimator -// _q : frame synchronizer -// _x : input sample -// _y : output symbol -int qdsync_cccf_step(qdsync_cccf _q, - float complex _x) -{ - // mix sample down - float complex v; - nco_crcf_mix_down(_q->mixer, _x, &v); - nco_crcf_step (_q->mixer); - - // push sample into filterbank - firpfb_crcf_push (_q->mf, v); - firpfb_crcf_execute(_q->mf, _q->pfb_index, &v); - - // increment counter to determine if sample is available - _q->mf_counter++; - int sample_available = (_q->mf_counter >= _q->k-1) ? 1 : 0; - - // set output sample if available - if (sample_available) { - // decrement counter by k=2 samples/symbol - _q->mf_counter -= _q->k; - - // append to output - qdsync_cccf_buf_append(_q, v); - } - - // return flag - return LIQUID_OK; -} +#include "liquid.internal.h" -// append sample to output buffer -int qdsync_cccf_buf_append(qdsync_cccf _q, - float complex _x) -{ - // account for filter delay - _q->symbol_counter++; - if (_q->symbol_counter <= 2*_q->m) - return LIQUID_OK; +// naming extensions (useful for print statements) +#define EXTENSION "cccf" - // append sample to end of buffer - _q->buf_out[_q->buf_out_counter] = _x; - _q->buf_out_counter++; +#define TO float complex // output type +#define TC float complex // coefficients type +#define TI float complex // input type +#define T float // primitive type - // check if buffer is full - if (_q->buf_out_counter == _q->buf_out_len) { - // reset counter - _q->buf_out_counter = 0; +// object references +#define QDSYNC(name) LIQUID_CONCAT(qdsync_cccf,name) +#define QDETECTOR(name) LIQUID_CONCAT(qdetector_cccf,name) - // invoke callback - if (_q->callback != NULL) { - int rc = _q->callback(_q->buf_out, _q->buf_out_len, _q->context); - if (rc) - return qdsync_cccf_reset(_q); - } - } - return LIQUID_OK; -} +// prototypes +#include "qdsync.proto.c" From f47e9cf6c949f39cfec548490d9150e84467be6f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 4 Jun 2023 14:57:58 -0400 Subject: [PATCH 150/186] cpfskmod: ensuring phase does not grow and lose precision --- src/modem/src/cpfskmod.c | 34 +++++++++-------- src/modem/tests/cpfskmodem_autotest.c | 53 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/modem/src/cpfskmod.c b/src/modem/src/cpfskmod.c index 51f553adb..843022c75 100644 --- a/src/modem/src/cpfskmod.c +++ b/src/modem/src/cpfskmod.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,7 @@ * THE SOFTWARE. */ -// // continuous phase frequency-shift keying modulator -// #include #include @@ -61,7 +59,7 @@ struct cpfskmod_s { // phase integrator float * phase_interp; // phase interpolation buffer - iirfilt_rrrf integrator; // integrator + float b0, b1, a1, v0, v1; // integrator }; // create cpfskmod object (frequency modulator) @@ -105,8 +103,9 @@ cpfskmod cpfskmod_create(unsigned int _bps, q->M = 1 << q->bps; // constellation size // create object depending upon input type - float b[2] = {0.5f, 0.5f}; // integrator feed-forward coefficients - float a[2] = {1.0f, -1.0f}; // integrator feed-back coefficients + q->b0 = 0.5f; + q->b1 = 0.5f; + q->a1 = -1.0f; q->ht_len = 0; q->ht = NULL; unsigned int i; @@ -115,8 +114,8 @@ cpfskmod cpfskmod_create(unsigned int _bps, q->ht_len = q->k; q->symbol_delay = 1; // modify integrator - b[0] = 0.0f; - b[1] = 1.0f; + q->b0 = 0.0f; + q->b1 = 1.0f; break; case LIQUID_CPFSK_RCOS_FULL: q->ht_len = q->k; @@ -142,9 +141,8 @@ cpfskmod cpfskmod_create(unsigned int _bps, q->ht[i] *= M_PI * q->h; q->interp = firinterp_rrrf_create(q->k, q->ht, q->ht_len); - // create phase integrator + // allocate buffer for phase interpolation q->phase_interp = (float*) malloc(q->k*sizeof(float)); - q->integrator = iirfilt_rrrf_create(b,2,a,2); // reset modem object cpfskmod_reset(q); @@ -160,9 +158,6 @@ int cpfskmod_destroy(cpfskmod _q) free(_q->phase_interp); firinterp_rrrf_destroy(_q->interp); - // destroy phase integrator - iirfilt_rrrf_destroy(_q->integrator); - // free main object memory free(_q); return LIQUID_OK; @@ -200,7 +195,8 @@ int cpfskmod_reset(cpfskmod _q) firinterp_rrrf_reset(_q->interp); // reset phase integrator - iirfilt_rrrf_reset(_q->integrator); + _q->v0 = 0.0f; + _q->v1 = 0.0f; return LIQUID_OK; } @@ -227,7 +223,15 @@ int cpfskmod_modulate(cpfskmod _q, float theta; for (i=0; i<_q->k; i++) { // push phase through integrator - iirfilt_rrrf_execute(_q->integrator, _q->phase_interp[i], &theta); + _q->v0 = _q->phase_interp[i] - _q->v1*_q->a1; + theta = _q->v0*_q->b0 + _q->v1*_q->b1; + _q->v1 = _q->v0; + + // constrain state + if (_q->v1 > 2*M_PI) + _q->v1 -= 2*M_PI; + if (_q->v1 < -2*M_PI) + _q->v1 += 2*M_PI; // compute output _y[i] = liquid_cexpjf(theta); diff --git a/src/modem/tests/cpfskmodem_autotest.c b/src/modem/tests/cpfskmodem_autotest.c index 354144338..27f57c073 100644 --- a/src/modem/tests/cpfskmodem_autotest.c +++ b/src/modem/tests/cpfskmodem_autotest.c @@ -118,3 +118,56 @@ void autotest_cpfskmodem_bps2_h0p0250_k4_m3_square() { cpfskmodem_test_mod_de void autotest_cpfskmodem_bps3_h0p1250_k4_m3_square() { cpfskmodem_test_mod_demod( 3, 0.1250f, 4, 3, 0.25f, LIQUID_CPFSK_SQUARE ); } void autotest_cpfskmodem_bps4_h0p0625_k4_m3_square() { cpfskmodem_test_mod_demod( 4, 0.0625f, 4, 3, 0.25f, LIQUID_CPFSK_SQUARE ); } +// test spectral response +void autotest_cpfskmodem_spectrum() +{ + // create modulator + unsigned int bps = 1; + float h = 0.5f; + unsigned int k = 4; + unsigned int m = 3; + float beta = 0.35f; + int type = LIQUID_CPFSK_RCOS_PARTIAL; + cpfskmod mod = cpfskmod_create(bps, h, k, m, beta, type); + + // spectral periodogram options + unsigned int nfft = 2400; // spectral periodogram FFT size + unsigned int num_symbols = 192000; // number of symbols to generate + float complex buf[k]; + unsigned int i; + + // modulate many, many symbols + for (i=0; i<(1U<<24U); i++) + cpfskmod_modulate(mod, 0, buf); + + // modulate several symbols and run result through spectral estimate + spgramcf periodogram = spgramcf_create_default(nfft); + for (i=0; i Date: Sun, 4 Jun 2023 17:11:43 -0400 Subject: [PATCH 151/186] build: removing unused variables --- sandbox/fec_spc2216_test.c | 17 ++++++++++------- scripts/autoscript.c | 1 + src/random/src/randgamma.c | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/sandbox/fec_spc2216_test.c b/sandbox/fec_spc2216_test.c index edd5f27a1..9a66df79a 100644 --- a/sandbox/fec_spc2216_test.c +++ b/sandbox/fec_spc2216_test.c @@ -253,9 +253,9 @@ int main(int argc, char*argv[]) // // run SNR trials // - float SNRdB_min = -1.0f; // signal-to-noise ratio (minimum) - float SNRdB_max = 7.0f; // signal-to-noise ratio (maximum) - unsigned int num_snr = 33; // number of SNR steps + float SNRdB_min = -5.0f; // signal-to-noise ratio (minimum) + float SNRdB_max = 10.0f; // signal-to-noise ratio (maximum) + unsigned int num_snr = 31; // number of SNR steps unsigned int num_trials=10000; // number of trials // arrays @@ -484,14 +484,15 @@ void spc2216_decode(unsigned char * _msg_rec, spc2216_transpose_col(w, m_hat, parity_row); // compute syndromes on rows and decode +#if DEBUG_SPC2216 unsigned int num_uncorrected_errors = 0; +#endif for (i=0; i<16; i++) { sym_enc[0] = parity_row[i]; sym_enc[1] = m_hat[2*i+0]; sym_enc[2] = m_hat[2*i+1]; - int syndrome_flag = fec_secded2216_estimate_ehat(sym_enc, e_hat); - #if DEBUG_SPC2216 + int syndrome_flag = fec_secded2216_estimate_ehat(sym_enc, e_hat); if (syndrome_flag == 0) { printf("%3u : no errors detected\n", i); } else if (syndrome_flag == 1) { @@ -499,10 +500,10 @@ void spc2216_decode(unsigned char * _msg_rec, } else { printf("%3u : multiple errors detected\n", i); } -#endif if (syndrome_flag == 2) num_uncorrected_errors++; +#endif // apply error vector estimate to appropriate arrays parity_col[i] ^= e_hat[0]; @@ -510,7 +511,9 @@ void spc2216_decode(unsigned char * _msg_rec, m_hat[2*i+1] ^= e_hat[2]; } - //printf("number of uncorrected errors: %u\n", num_uncorrected_errors); +#if DEBUG_SPC2216 + printf("number of uncorrected errors: %u\n", num_uncorrected_errors); +#endif // copy decoded message to output memmove(_msg_dec, m_hat, 32*sizeof(unsigned char)); diff --git a/scripts/autoscript.c b/scripts/autoscript.c index 14ca83be0..f71533af8 100644 --- a/scripts/autoscript.c +++ b/scripts/autoscript.c @@ -339,6 +339,7 @@ void autoscript_parsefile(autoscript _q, package_added = 1; } autoscript_addscript(_q, _package_name, basename); + printf("// adding %s to package %s from %s:%u\n", basename, _package_name, _filename, n); } } while (!feof(fid)); diff --git a/src/random/src/randgamma.c b/src/random/src/randgamma.c index 43f819798..2faa8c18f 100644 --- a/src/random/src/randgamma.c +++ b/src/random/src/randgamma.c @@ -145,7 +145,7 @@ float randgammaf_delta(float _delta) float V1 = 0.0f; float V2 = 0.0f; - unsigned int m = 1; + //unsigned int m = 1; float xi = 0.0f; float eta = 0.0f; @@ -168,7 +168,7 @@ float randgammaf_delta(float _delta) // step 6 if ( eta > powf(xi,_delta-1.0f)*expf(-xi) ) { - m++; + //m++; } else { break; } From 2e76e501bd3576c686fb3c72fa0ba014af96241f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 10 Jun 2023 14:45:49 -0400 Subject: [PATCH 152/186] qpacketmodem: moving to pre-processor macro --- include/liquid.h | 186 +++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 88 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index ea16f3fb3..08c168373 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -5217,96 +5217,106 @@ typedef int (*framesync_callback)(unsigned char * _header, // _userdata : user-defined data pointer typedef void (*framesync_csma_callback)(void * _userdata); -// -// packet encoder/decoder -// -typedef struct qpacketmodem_s * qpacketmodem; +#define LIQUID_QPACKETMODEM_MANGLE_RRRF(name) LIQUID_CONCAT(qpacketmodem,name) -// create packet encoder -qpacketmodem qpacketmodem_create (); -qpacketmodem qpacketmodem_copy (qpacketmodem _q); -int qpacketmodem_destroy(qpacketmodem _q); -int qpacketmodem_reset (qpacketmodem _q); -int qpacketmodem_print (qpacketmodem _q); - -int qpacketmodem_configure(qpacketmodem _q, - unsigned int _payload_len, - crc_scheme _check, - fec_scheme _fec0, - fec_scheme _fec1, - int _ms); - -// get length of encoded frame in symbols -unsigned int qpacketmodem_get_frame_len(qpacketmodem _q); - -// get unencoded/decoded payload length (bytes) -unsigned int qpacketmodem_get_payload_len(qpacketmodem _q); - -// regular access methods -unsigned int qpacketmodem_get_crc (qpacketmodem _q); -unsigned int qpacketmodem_get_fec0 (qpacketmodem _q); -unsigned int qpacketmodem_get_fec1 (qpacketmodem _q); -unsigned int qpacketmodem_get_modscheme(qpacketmodem _q); - -float qpacketmodem_get_demodulator_phase_error(qpacketmodem _q); -float qpacketmodem_get_demodulator_evm(qpacketmodem _q); - -// encode packet into un-modulated frame symbol indices -// _q : qpacketmodem object -// _payload : unencoded payload bytes -// _syms : encoded but un-modulated payload symbol indices -int qpacketmodem_encode_syms(qpacketmodem _q, - const unsigned char * _payload, - unsigned char * _syms); - -// decode packet from demodulated frame symbol indices (hard-decision decoding) -// _q : qpacketmodem object -// _syms : received hard-decision symbol indices, [size: frame_len x 1] -// _payload : recovered decoded payload bytes -int qpacketmodem_decode_syms(qpacketmodem _q, - unsigned char * _syms, - unsigned char * _payload); - -// decode packet from demodulated frame bits (soft-decision decoding) -// _q : qpacketmodem object -// _bits : received soft-decision bits, [size: bps*frame_len x 1] -// _payload : recovered decoded payload bytes -int qpacketmodem_decode_bits(qpacketmodem _q, - unsigned char * _bits, - unsigned char * _payload); - -// encode and modulate packet into modulated frame samples -// _q : qpacketmodem object -// _payload : unencoded payload bytes -// _frame : encoded/modulated payload symbols -int qpacketmodem_encode(qpacketmodem _q, - const unsigned char * _payload, - liquid_float_complex * _frame); - -// decode packet from modulated frame samples, returning flag if CRC passed -// NOTE: hard-decision decoding -// _q : qpacketmodem object -// _frame : encoded/modulated payload symbols -// _payload : recovered decoded payload bytes -int qpacketmodem_decode(qpacketmodem _q, - liquid_float_complex * _frame, - unsigned char * _payload); - -// decode packet from modulated frame samples, returning flag if CRC passed -// NOTE: soft-decision decoding -// _q : qpacketmodem object -// _frame : encoded/modulated payload symbols -// _payload : recovered decoded payload bytes -int qpacketmodem_decode_soft(qpacketmodem _q, - liquid_float_complex * _frame, - unsigned char * _payload); - -int qpacketmodem_decode_soft_sym(qpacketmodem _q, - liquid_float_complex _symbol); - -int qpacketmodem_decode_soft_payload(qpacketmodem _q, - unsigned char * _payload); +// Macro: +// QPACKETMODEM : name-mangling macro +// T : data type +#define LIQUID_QPACKETMODEM_DEFINE_API(QPACKETMODEM,T) \ + \ +/* Packet encoder/decoder */ \ +typedef struct QPACKETMODEM(_s) * QPACKETMODEM(); \ + \ +/* Create packet encoder */ \ +QPACKETMODEM() QPACKETMODEM(_create)(); \ + \ +QPACKETMODEM() QPACKETMODEM(_copy) (QPACKETMODEM() _q); \ +int QPACKETMODEM(_destroy)(QPACKETMODEM() _q); \ +int QPACKETMODEM(_reset) (QPACKETMODEM() _q); \ +int QPACKETMODEM(_print) (QPACKETMODEM() _q); \ + \ +int QPACKETMODEM(_configure)(QPACKETMODEM() _q, \ + unsigned int _payload_len, \ + crc_scheme _check, \ + fec_scheme _fec0, \ + fec_scheme _fec1, \ + int _ms); \ + \ +/* get length of encoded frame in symbols */ \ +unsigned int QPACKETMODEM(_get_frame_len)(QPACKETMODEM() _q); \ + \ +/* get unencoded/decoded payload length (bytes) */ \ +unsigned int QPACKETMODEM(_get_payload_len)(QPACKETMODEM() _q); \ + \ +/* regular access methods */ \ +unsigned int QPACKETMODEM(_get_crc )(QPACKETMODEM() _q); \ +unsigned int QPACKETMODEM(_get_fec0 )(QPACKETMODEM() _q); \ +unsigned int QPACKETMODEM(_get_fec1 )(QPACKETMODEM() _q); \ +unsigned int QPACKETMODEM(_get_modscheme)(QPACKETMODEM() _q); \ + \ +float QPACKETMODEM(_get_demodulator_phase_error)(QPACKETMODEM() _q); \ +float QPACKETMODEM(_get_demodulator_evm)(QPACKETMODEM() _q); \ + \ +/* encode packet into un-modulated frame symbol indices */ \ +/* _q : qpacketmodem object */ \ +/* _payload : unencoded payload bytes */ \ +/* _syms : encoded but un-modulated payload symbol indices */ \ +int QPACKETMODEM(_encode_syms)(QPACKETMODEM() _q, \ + const unsigned char * _payload, \ + unsigned char * _syms); \ + \ +/* decode packet from demodulated frame symbol indices (hard-decision */ \ +/* decoding) */ \ +/* _q : qpacketmodem object */ \ +/* _syms : received hard-decision symbol indices, */ \ +/* [size: frame_len x 1] */ \ +/* _payload : recovered decoded payload bytes */ \ +int QPACKETMODEM(_decode_syms)(QPACKETMODEM() _q, \ + unsigned char * _syms, \ + unsigned char * _payload); \ + \ +/* decode packet from demodulated frame bits (soft-decision decoding) */ \ +/* _q : qpacketmodem object */ \ +/* _bits : received soft-decision bits, [size: bps*frame_len x 1] */ \ +/* _payload : recovered decoded payload bytes */ \ +int QPACKETMODEM(_decode_bits)(QPACKETMODEM() _q, \ + unsigned char * _bits, \ + unsigned char * _payload); \ + \ +/* encode and modulate packet into modulated frame samples */ \ +/* _q : qpacketmodem object */ \ +/* _payload : unencoded payload bytes */ \ +/* _frame : encoded/modulated payload symbols */ \ +int QPACKETMODEM(_encode)(QPACKETMODEM() _q, \ + const unsigned char * _payload, \ + liquid_float_complex * _frame); \ + \ +/* decode packet from modulated frame samples, returning flag if CRC */ \ +/* passed using hard-decision decoding */ \ +/* _q : qpacketmodem object */ \ +/* _frame : encoded/modulated payload symbols */ \ +/* _payload : recovered decoded payload bytes */ \ +int QPACKETMODEM(_decode)(QPACKETMODEM() _q, \ + liquid_float_complex * _frame, \ + unsigned char * _payload); \ + \ +/* decode packet from modulated frame samples, returning flag if CRC */ \ +/* passed using soft-decision decoding */ \ +/* _q : qpacketmodem object */ \ +/* _frame : encoded/modulated payload symbols */ \ +/* _payload : recovered decoded payload bytes */ \ +int QPACKETMODEM(_decode_soft)(QPACKETMODEM() _q, \ + liquid_float_complex * _frame, \ + unsigned char * _payload); \ + \ +int QPACKETMODEM(_decode_soft_sym)(QPACKETMODEM() _q, \ + liquid_float_complex _symbol); \ + \ +int QPACKETMODEM(_decode_soft_payload)(QPACKETMODEM() _q, \ + unsigned char * _payload); \ + +LIQUID_QPACKETMODEM_DEFINE_API(LIQUID_QPACKETMODEM_MANGLE_RRRF, float) // // pilot generator/synchronizer for packet burst recovery From ab70c185d8ae397751246ae007303190066eb179 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 11 Jun 2023 11:19:22 -0400 Subject: [PATCH 153/186] build/framing: consolidating prototype sources --- makefile.in | 41 +++++++------- src/framing/src/bpresync_cccf.c | 53 ------------------- src/framing/src/bsync.proto.c | 30 +++++------ src/framing/src/bsync_cccf.c | 47 ---------------- src/framing/src/bsync_rrrf.c | 47 ---------------- .../src/{symtrack_cccf.c => framing_cccf.c} | 33 ++++++++---- .../src/{bsync_crcf.c => framing_crcf.c} | 35 ++++++++---- .../src/{presync_cccf.c => framing_rrrf.c} | 44 ++++++++------- .../src/{symstreamrcf.c => framingcf.c} | 22 ++++++-- src/framing/src/msourcecf.c | 49 ----------------- .../{qpacketmodem.c => qpacketmodem.proto.c} | 0 src/framing/src/symstreamcf.c | 45 ---------------- 12 files changed, 124 insertions(+), 322 deletions(-) delete mode 100644 src/framing/src/bpresync_cccf.c delete mode 100644 src/framing/src/bsync_cccf.c delete mode 100644 src/framing/src/bsync_rrrf.c rename src/framing/src/{symtrack_cccf.c => framing_cccf.c} (75%) rename src/framing/src/{bsync_crcf.c => framing_crcf.c} (60%) rename src/framing/src/{presync_cccf.c => framing_rrrf.c} (62%) rename src/framing/src/{symstreamrcf.c => framingcf.c} (68%) delete mode 100644 src/framing/src/msourcecf.c rename src/framing/src/{qpacketmodem.c => qpacketmodem.proto.c} (100%) delete mode 100644 src/framing/src/symstreamcf.c diff --git a/makefile.in b/makefile.in index d92ca74c8..d8d0ba37b 100644 --- a/makefile.in +++ b/makefile.in @@ -630,10 +630,6 @@ filter_benchmarks := \ framing_objects := \ src/framing/src/bpacketgen.o \ src/framing/src/bpacketsync.o \ - src/framing/src/bpresync_cccf.o \ - src/framing/src/bsync_rrrf.o \ - src/framing/src/bsync_crcf.o \ - src/framing/src/bsync_cccf.o \ src/framing/src/detector_cccf.o \ src/framing/src/dsssframegen.o \ src/framing/src/dsssframesync.o \ @@ -641,34 +637,46 @@ framing_objects := \ src/framing/src/framesyncstats.o \ src/framing/src/framegen64.o \ src/framing/src/framesync64.o \ + src/framing/src/framingcf.o \ + src/framing/src/framing_rrrf.o \ + src/framing/src/framing_crcf.o \ + src/framing/src/framing_cccf.o \ src/framing/src/flexframegen.o \ src/framing/src/flexframesync.o \ src/framing/src/fskframegen.o \ src/framing/src/fskframesync.o \ src/framing/src/gmskframegen.o \ src/framing/src/gmskframesync.o \ - src/framing/src/msourcecf.o \ src/framing/src/ofdmflexframegen.o \ src/framing/src/ofdmflexframesync.o \ - src/framing/src/presync_cccf.o \ - src/framing/src/symstreamcf.o \ - src/framing/src/symstreamrcf.o \ - src/framing/src/symtrack_cccf.o \ src/framing/src/qdetector_cccf.o \ src/framing/src/qdsync_cccf.o \ - src/framing/src/qpacketmodem.o \ src/framing/src/qpilotgen.o \ src/framing/src/qpilotsync.o \ # list explicit targets and dependencies here +framing_prototypes_gen := \ + src/framing/src/msource.proto.c \ + src/framing/src/qpacketmodem.proto.c \ + src/framing/src/qsource.proto.c \ + src/framing/src/symstream.proto.c \ + src/framing/src/symstreamr.proto.c \ + +framing_prototypes_sync := \ + src/framing/src/bpresync.proto.c \ + src/framing/src/bsync.proto.c \ + src/framing/src/presync.proto.c \ + src/framing/src/symtrack.proto.c \ + +src/framing/src/framingcf.o : %.o : %.c $(include_headers) ${framing_prototypes_gen} +src/framing/src/framing_rrrf.o : %.o : %.c $(include_headers) ${framing_prototypes_sync} +src/framing/src/framing_crcf.o : %.o : %.c $(include_headers) ${framing_prototypes_sync} +src/framing/src/framing_cccf.o : %.o : %.c $(include_headers) ${framing_prototypes_sync} + src/framing/src/bpacketgen.o : %.o : %.c $(include_headers) src/framing/src/bpacketsync.o : %.o : %.c $(include_headers) -src/framing/src/bpresync_cccf.o : %.o : %.c $(include_headers) src/framing/src/bpresync.proto.c -src/framing/src/bsync_rrrf.o : %.o : %.c $(include_headers) src/framing/src/bsync.proto.c -src/framing/src/bsync_crcf.o : %.o : %.c $(include_headers) src/framing/src/bsync.proto.c -src/framing/src/bsync_cccf.o : %.o : %.c $(include_headers) src/framing/src/bsync.proto.c src/framing/src/detector_cccf.o : %.o : %.c $(include_headers) src/framing/src/dsssframegen.o : %.o : %.c $(include_headers) src/framing/src/dsssframesync.o : %.o : %.c $(include_headers) @@ -678,14 +686,9 @@ src/framing/src/framegen64.o : %.o : %.c $(include_headers) src/framing/src/framesync64.o : %.o : %.c $(include_headers) src/framing/src/flexframegen.o : %.o : %.c $(include_headers) src/framing/src/flexframesync.o : %.o : %.c $(include_headers) -src/framing/src/msourcecf.o : %.o : %.c $(include_headers) src/framing/src/msource.proto.c src/framing/src/qsource.proto.c src/framing/src/ofdmflexframegen.o : %.o : %.c $(include_headers) src/framing/src/ofdmflexframesync.o : %.o : %.c $(include_headers) -src/framing/src/presync_cccf.o : %.o : %.c $(include_headers) src/framing/src/presync.proto.c src/framing/src/qpacketmodem.o : %.o : %.c $(include_headers) -src/framing/src/symstreamcf.o : %.o : %.c $(include_headers) src/framing/src/symstream.proto.c -src/framing/src/symstreamrcf.o : %.o : %.c $(include_headers) src/framing/src/symstreamr.proto.c -src/framing/src/symtrack_cccf.o : %.o : %.c $(include_headers) src/framing/src/symtrack.proto.c framing_autotests := \ diff --git a/src/framing/src/bpresync_cccf.c b/src/framing/src/bpresync_cccf.c deleted file mode 100644 index e1b3b987a..000000000 --- a/src/framing/src/bpresync_cccf.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// Binary pre-demod synchronizer -// - -#include "liquid.internal.h" - -// -#define BPRESYNC(name) LIQUID_CONCAT(bpresync_cccf,name) - -// print and naming extensions -#define PRINTVAL(x) printf("%12.4e + j%12.4e", crealf(x), cimagf(x)) -#define EXTENSION_SHORT "f" -#define EXTENSION_FULL "cccf" - -#define TO float complex -#define TC float complex -#define TI float complex - -#define ABS(X) cabsf(X) -#define REAL(X) crealf(X) -#define IMAG(X) cimagf(X) - -#define BSYNC(name) LIQUID_CONCAT(bsync_cccf,name) - -#define TO_COMPLEX -#define TC_COMPLEX -#define TI_COMPLEX - -// prototypes -#include "bpresync.proto.c" - diff --git a/src/framing/src/bsync.proto.c b/src/framing/src/bsync.proto.c index 22cc681a3..2246df36a 100644 --- a/src/framing/src/bsync.proto.c +++ b/src/framing/src/bsync.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,11 +36,11 @@ struct BSYNC(_s) { bsequence sync_i; // synchronization pattern (in-phase) bsequence sym_i; // received symbols (in-phase) -//#if defined TC_COMPLEX +//#if TC_COMPLEX==1 bsequence sync_q; // synchronization pattern (quadrature) //#endif -//#if defined TI_COMPLEX +//#if TI_COMPLEX==1 bsequence sym_q; // received symbols (quadrature) //#endif TO rxy; // cross correlation @@ -52,19 +52,19 @@ BSYNC() BSYNC(_create)(unsigned int _n, TC * _v) fs->n = _n; fs->sync_i = bsequence_create(fs->n); -#ifdef TC_COMPLEX +#if TC_COMPLEX==1 fs->sync_q = bsequence_create(fs->n); #endif fs->sym_i = bsequence_create(fs->n); -#ifdef TI_COMPLEX +#if TI_COMPLEX==1 fs->sym_q = bsequence_create(fs->n); #endif unsigned int i; for (i=0; in; i++) { bsequence_push(fs->sync_i, crealf(_v[i])>0); -#ifdef TC_COMPLEX +#if TC_COMPLEX==1 bsequence_push(fs->sync_q, cimagf(_v[i])>0); #endif } @@ -89,12 +89,12 @@ BSYNC() BSYNC(_create_msequence)(unsigned int _g, unsigned int n = msequence_get_length(ms); fs->sync_i = bsequence_create(n * _k); -#ifdef TC_COMPLEX +#if TC_COMPLEX==1 fs->sync_q = bsequence_create(n * _k); #endif fs->sym_i = bsequence_create(n * _k); -#ifdef TI_COMPLEX +#if TI_COMPLEX==1 fs->sym_q = bsequence_create(n * _k); #endif @@ -102,7 +102,7 @@ BSYNC() BSYNC(_create_msequence)(unsigned int _g, #if 0 bsequence_init_msequence(fs->sync_i,ms); -#ifdef TC_COMPLEX +#if TC_COMPLEX==1 msequence_reset(ms); bsequence_init_msequence(fs->sync_q,ms); #endif @@ -114,7 +114,7 @@ BSYNC() BSYNC(_create_msequence)(unsigned int _g, for (j=0; j<_k; j++) { bsequence_push(fs->sync_i, bit); -#ifdef TC_COMPLEX +#if TC_COMPLEX==1 bsequence_push(fs->sync_q, bit); #endif } @@ -131,12 +131,12 @@ BSYNC() BSYNC(_create_msequence)(unsigned int _g, void BSYNC(_destroy)(BSYNC() _fs) { bsequence_destroy(_fs->sync_i); -#ifdef TC_COMPLEX +#if TC_COMPLEX==1 bsequence_destroy(_fs->sync_q); #endif bsequence_destroy(_fs->sym_i); -#ifdef TI_COMPLEX +#if TI_COMPLEX==1 bsequence_destroy(_fs->sym_q); #endif free(_fs); @@ -151,12 +151,12 @@ void BSYNC(_correlate)(BSYNC() _fs, TI _sym, TO *_y) { // push symbol into buffers bsequence_push(_fs->sym_i, crealf(_sym)>0.0 ? 1 : 0); -#ifdef TI_COMPLEX +#if TI_COMPLEX==1 bsequence_push(_fs->sym_q, cimagf(_sym)>0.0 ? 1 : 0); #endif // compute dotprod -#if defined TC_COMPLEX && defined TI_COMPLEX +#if TC_COMPLEX==1 && TI_COMPLEX==1 // cccx TO rxy_ii = 2.*bsequence_correlate(_fs->sync_i, _fs->sym_i) - (float)(_fs->n); TO rxy_qq = 2.*bsequence_correlate(_fs->sync_q, _fs->sym_q) - (float)(_fs->n); @@ -164,7 +164,7 @@ void BSYNC(_correlate)(BSYNC() _fs, TI _sym, TO *_y) TO rxy_qi = 2.*bsequence_correlate(_fs->sync_q, _fs->sym_i) - (float)(_fs->n); _fs->rxy = (rxy_ii - rxy_qq) + _Complex_I*(rxy_iq + rxy_qi); -#elif defined TI_COMPLEX +#elif TI_COMPLEX==1 // crcx float rxy_ii = 2.*bsequence_correlate(_fs->sync_i, _fs->sym_i) - (float)(_fs->n); float rxy_iq = 2.*bsequence_correlate(_fs->sync_i, _fs->sym_q) - (float)(_fs->n); diff --git a/src/framing/src/bsync_cccf.c b/src/framing/src/bsync_cccf.c deleted file mode 100644 index 4ac9fe834..000000000 --- a/src/framing/src/bsync_cccf.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// Framing API: floating-point -// - -#include "liquid.internal.h" - -// -#define BSYNC(name) LIQUID_CONCAT(bsync_cccf,name) - -#define PRINTVAL(x) printf("%12.4e + j%12.4e", crealf(x), cimagf(x)) - -#define TO float complex -#define TC float complex -#define TI float complex -#define ABS(X) cabsf(X) -#define WINDOW(name) LIQUID_CONCAT(windowcf,name) -#define DOTPROD(name) LIQUID_CONCAT(dotprod_cccf,name) - -#define TO_COMPLEX -#define TC_COMPLEX -#define TI_COMPLEX - -// prototypes -#include "bsync.proto.c" - diff --git a/src/framing/src/bsync_rrrf.c b/src/framing/src/bsync_rrrf.c deleted file mode 100644 index 39b9a82ce..000000000 --- a/src/framing/src/bsync_rrrf.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// Framing API: floating-point -// - -#include "liquid.internal.h" - -// -#define BSYNC(name) LIQUID_CONCAT(bsync_rrrf,name) - -#define PRINTVAL(x) printf("%12.4e", x) - -#define TO float -#define TC float -#define TI float -#define ABS(X) fabsf(X) -#define WINDOW(name) LIQUID_CONCAT(windowf,name) -#define DOTPROD(name) LIQUID_CONCAT(dotprod_rrrf,name) - -#undef TO_COMPLEX -#undef TC_COMPLEX -#undef TI_COMPLEX - -// prototypes -#include "bsync.proto.c" - diff --git a/src/framing/src/symtrack_cccf.c b/src/framing/src/framing_cccf.c similarity index 75% rename from src/framing/src/symtrack_cccf.c rename to src/framing/src/framing_cccf.c index 26f863fe1..599e2b23a 100644 --- a/src/framing/src/symtrack_cccf.c +++ b/src/framing/src/framing_cccf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,7 @@ * THE SOFTWARE. */ -// -// Framing API: floating-point -// +// complex floating-point synchronization and supporting objects #include "liquid.internal.h" @@ -37,19 +35,32 @@ #define TC float complex #define TI float complex #define ABS(X) cabsf(X) +#define REAL(X) crealf(X) +#define IMAG(X) cimagf(X) -// object references -#define SYMTRACK(name) LIQUID_CONCAT(symtrack_cccf,name) +#define TO_COMPLEX 1 +#define TC_COMPLEX 1 +#define TI_COMPLEX 1 + +// supporting references #define AGC(name) LIQUID_CONCAT(agc_crcf,name) -#define SYMSYNC(name) LIQUID_CONCAT(symsync_crcf,name) +#define BSYNC(name) LIQUID_CONCAT(bsync_cccf,name) +#define DOTPROD(name) LIQUID_CONCAT(dotprod_rrrf,name) #define EQLMS(name) LIQUID_CONCAT(eqlms_cccf,name) -#define NCO(name) LIQUID_CONCAT(nco_crcf,name) #define MODEM(name) LIQUID_CONCAT(modemcf,name) +#define NCO(name) LIQUID_CONCAT(nco_crcf,name) +#define SYMSYNC(name) LIQUID_CONCAT(symsync_crcf,name) +#define WINDOW(name) LIQUID_CONCAT(windowf,name) -#define TO_COMPLEX 1 -#define TC_COMPLEX 1 -#define TI_COMPLEX 1 +// object references +#define BPRESYNC(name) LIQUID_CONCAT(bpresync_cccf,name) +#define BSYNC(name) LIQUID_CONCAT(bsync_cccf,name) +#define PRESYNC(name) LIQUID_CONCAT(presync_cccf,name) +#define SYMTRACK(name) LIQUID_CONCAT(symtrack_cccf,name) // prototypes +#include "bpresync.proto.c" +#include "bsync.proto.c" +#include "presync.proto.c" #include "symtrack.proto.c" diff --git a/src/framing/src/bsync_crcf.c b/src/framing/src/framing_crcf.c similarity index 60% rename from src/framing/src/bsync_crcf.c rename to src/framing/src/framing_crcf.c index b6b470784..330f6c188 100644 --- a/src/framing/src/bsync_crcf.c +++ b/src/framing/src/framing_crcf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,27 +20,40 @@ * THE SOFTWARE. */ -// -// Framing API: floating-point -// +// complex floating-point synchronization and supporting objects #include "liquid.internal.h" -// -#define BSYNC(name) LIQUID_CONCAT(bsync_crcf,name) +// naming extensions (useful for print statements) +#define EXTENSION_SHORT "f" +#define EXTENSION_FULL "crcf" #define PRINTVAL(x) printf("%12.4e + j%12.4e", crealf(x), cimagf(x)) +#define T float #define TO float complex #define TC float #define TI float complex #define ABS(X) cabsf(X) -#define WINDOW(name) LIQUID_CONCAT(windowcf,name) -#define DOTPROD(name) LIQUID_CONCAT(dotprod_crcf,name) +#define REAL(X) crealf(X) +#define IMAG(X) cimagf(X) + +#define TO_COMPLEX 1 +#define TC_COMPLEX 0 +#define TI_COMPLEX 1 -#define TO_COMPLEX -#undef TC_COMPLEX -#define TI_COMPLEX +// supporting references +#define AGC(name) LIQUID_CONCAT(agc_crcf,name) +#define BSYNC(name) LIQUID_CONCAT(bsync_crcf,name) +#define DOTPROD(name) LIQUID_CONCAT(dotprod_rrrf,name) +#define EQLMS(name) LIQUID_CONCAT(eqlms_crcf,name) +#define MODEM(name) LIQUID_CONCAT(modemcf,name) +#define NCO(name) LIQUID_CONCAT(nco_crcf,name) +#define SYMSYNC(name) LIQUID_CONCAT(symsync_crcf,name) +#define WINDOW(name) LIQUID_CONCAT(windowf,name) + +// object references +#define BSYNC(name) LIQUID_CONCAT(bsync_crcf,name) // prototypes #include "bsync.proto.c" diff --git a/src/framing/src/presync_cccf.c b/src/framing/src/framing_rrrf.c similarity index 62% rename from src/framing/src/presync_cccf.c rename to src/framing/src/framing_rrrf.c index 12ad70a19..361f343a6 100644 --- a/src/framing/src/presync_cccf.c +++ b/src/framing/src/framing_rrrf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,37 +20,41 @@ * THE SOFTWARE. */ -// -// Binary pre-demod synchronizer -// +// real floating-point synchronization and supporting objects #include "liquid.internal.h" -// -#define PRESYNC(name) LIQUID_CONCAT(presync_cccf,name) - -// print and naming extensions -#define PRINTVAL(x) printf("%12.4e + j%12.4e", crealf(x), cimagf(x)) +// naming extensions (useful for print statements) #define EXTENSION_SHORT "f" -#define EXTENSION_FULL "cccf" +#define EXTENSION_FULL "rrrf" -#define T float // primitive type -#define TO float complex // output type -#define TC float complex // coefficient type -#define TI float complex // input type +#define PRINTVAL(x) printf("%12.4e + j%12.4e", crealf(x), cimagf(x)) +#define T float +#define TO float +#define TC float +#define TI float #define ABS(X) cabsf(X) #define REAL(X) crealf(X) #define IMAG(X) cimagf(X) -#define WINDOW(name) LIQUID_CONCAT(windowf,name) +#define TO_COMPLEX 0 +#define TC_COMPLEX 0 +#define TI_COMPLEX 0 + +// supporting references +#define AGC(name) LIQUID_CONCAT(agc_rrrf,name) +#define BSYNC(name) LIQUID_CONCAT(bsync_rrrf,name) #define DOTPROD(name) LIQUID_CONCAT(dotprod_rrrf,name) -#define BSYNC(name) LIQUID_CONCAT(bsync_cccf,name) +#define EQLMS(name) LIQUID_CONCAT(eqlms_rrrf,name) +#define MODEM(name) LIQUID_CONCAT(modemcf,name) +#define NCO(name) LIQUID_CONCAT(nco_rrrf,name) +#define SYMSYNC(name) LIQUID_CONCAT(symsync_rrrf,name) +#define WINDOW(name) LIQUID_CONCAT(windowf,name) -#define TO_COMPLEX -#define TC_COMPLEX -#define TI_COMPLEX +// object references +#define BSYNC(name) LIQUID_CONCAT(bsync_rrrf,name) // prototypes -#include "presync.proto.c" +#include "bsync.proto.c" diff --git a/src/framing/src/symstreamrcf.c b/src/framing/src/framingcf.c similarity index 68% rename from src/framing/src/symstreamrcf.c rename to src/framing/src/framingcf.c index b1d2b91e7..aeb19f8b5 100644 --- a/src/framing/src/symstreamrcf.c +++ b/src/framing/src/framingcf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,7 @@ * THE SOFTWARE. */ -// -// API: floating-point -// +// complex floating-point framing and signal generators #include "liquid.internal.h" @@ -35,11 +33,25 @@ #define TO_COMPLEX 1 #define T_COMPLEX 0 -// object references +// supporting references +#define FIRINTERP(name) LIQUID_CONCAT(firinterp_crcf,name) +#define IIRFILT(name) LIQUID_CONCAT(iirfilt_crcf, name) +#define MODEM(name) LIQUID_CONCAT(modemcf, name) #define MSRESAMP(name) LIQUID_CONCAT(msresamp_crcf,name) +#define NCO(name) LIQUID_CONCAT(nco_crcf, name) +#define SYMSTREAM(name) LIQUID_CONCAT(symstreamcf, name) + +// object references +#define MSOURCE(name) LIQUID_CONCAT(msourcecf, name) +#define QPACKETMODEM(name) LIQUID_CONCAT(qpacketmodem, name) +#define QSOURCE(name) LIQUID_CONCAT(qsourcecf, name) #define SYMSTREAM(name) LIQUID_CONCAT(symstreamcf, name) #define SYMSTREAMR(name) LIQUID_CONCAT(symstreamrcf, name) // prototypes +#include "msource.proto.c" +#include "qpacketmodem.proto.c" +#include "qsource.proto.c" +#include "symstream.proto.c" #include "symstreamr.proto.c" diff --git a/src/framing/src/msourcecf.c b/src/framing/src/msourcecf.c deleted file mode 100644 index 6013e826c..000000000 --- a/src/framing/src/msourcecf.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// API: floating-point -// - -#include "liquid.internal.h" - -// naming extensions (useful for print statements) -#define EXTENSION "cf" - -#define TO float complex // output type -#define T float // primitive type - -#define TO_COMPLEX 1 -#define T_COMPLEX 0 - -// object references -#define QSOURCE(name) LIQUID_CONCAT(qsourcecf,name) -#define MSOURCE(name) LIQUID_CONCAT(msourcecf,name) - -#define IIRFILT(name) LIQUID_CONCAT(iirfilt_crcf,name) -#define NCO(name) LIQUID_CONCAT(nco_crcf,name) -#define SYMSTREAM(name) LIQUID_CONCAT(symstreamcf,name) - -// prototypes -#include "msource.proto.c" -#include "qsource.proto.c" - diff --git a/src/framing/src/qpacketmodem.c b/src/framing/src/qpacketmodem.proto.c similarity index 100% rename from src/framing/src/qpacketmodem.c rename to src/framing/src/qpacketmodem.proto.c diff --git a/src/framing/src/symstreamcf.c b/src/framing/src/symstreamcf.c deleted file mode 100644 index 780157f7b..000000000 --- a/src/framing/src/symstreamcf.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2007 - 2022 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// API: floating-point -// - -#include "liquid.internal.h" - -// naming extensions (useful for print statements) -#define EXTENSION "cf" - -#define TO float complex // output type -#define T float // primitive type - -#define TO_COMPLEX 1 -#define T_COMPLEX 0 - -// object references -#define SYMSTREAM(name) LIQUID_CONCAT(symstreamcf,name) -#define MODEM(name) LIQUID_CONCAT(modemcf,name) -#define FIRINTERP(name) LIQUID_CONCAT(firinterp_crcf,name) - -// prototypes -#include "symstream.proto.c" - From 88edbda21c73b01aa7f05ea80c3724d750ebd1d0 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 11 Jun 2023 11:36:07 -0400 Subject: [PATCH 154/186] qpacketmodem: extending prototype into macro --- src/framing/src/qpacketmodem.proto.c | 88 ++++++++++++++-------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/framing/src/qpacketmodem.proto.c b/src/framing/src/qpacketmodem.proto.c index 6b5a3e433..96ee3c0a9 100644 --- a/src/framing/src/qpacketmodem.proto.c +++ b/src/framing/src/qpacketmodem.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,9 +31,9 @@ #include "liquid.internal.h" -struct qpacketmodem_s { +struct QPACKETMODEM(_s) { // properties - modemcf mod_payload; // payload modulator/demodulator + MODEM() mod_payload; // payload modulator/demodulator packetizer p; // packet encoder/decoder unsigned int bits_per_symbol; // modulator bits/symbol unsigned int payload_dec_len; // number of decoded payload bytes @@ -47,13 +47,13 @@ struct qpacketmodem_s { }; // create packet encoder -qpacketmodem qpacketmodem_create() +QPACKETMODEM() QPACKETMODEM(_create)() { // allocate memory for main object - qpacketmodem q = (qpacketmodem) malloc(sizeof(struct qpacketmodem_s)); + QPACKETMODEM() q = (QPACKETMODEM()) malloc(sizeof(struct QPACKETMODEM(_s))); // create payload modem (initially QPSK, overridden by properties) - q->mod_payload = modemcf_create(LIQUID_MODEM_QPSK); + q->mod_payload = MODEM(_create)(LIQUID_MODEM_QPSK); q->bits_per_symbol = 2; // initial memory allocation for payload @@ -87,22 +87,22 @@ qpacketmodem qpacketmodem_create() } // copy object -qpacketmodem qpacketmodem_copy(qpacketmodem q_orig) +QPACKETMODEM() QPACKETMODEM(_copy)(QPACKETMODEM() q_orig) { // validate input if (q_orig == NULL) return liquid_error_config("qpacketmodem_copy(), object cannot be NULL"); // create new object - qpacketmodem q_copy = qpacketmodem_create(); + QPACKETMODEM() q_copy = QPACKETMODEM(_create)(); // configure identically as original unsigned int payload_len = q_orig->payload_dec_len; crc_scheme check = packetizer_get_crc (q_orig->p); fec_scheme fec0 = packetizer_get_fec0(q_orig->p); fec_scheme fec1 = packetizer_get_fec1(q_orig->p); - int ms = modemcf_get_scheme (q_orig->mod_payload); - qpacketmodem_configure(q_copy, payload_len, check, fec0, fec1, ms); + int ms = MODEM(_get_scheme )(q_orig->mod_payload); + QPACKETMODEM(_configure)(q_copy, payload_len, check, fec0, fec1, ms); // return new object return q_copy; @@ -110,11 +110,11 @@ qpacketmodem qpacketmodem_copy(qpacketmodem q_orig) // destroy object, freeing all internal arrays -int qpacketmodem_destroy(qpacketmodem _q) +int QPACKETMODEM(_destroy)(QPACKETMODEM() _q) { // free objects packetizer_destroy(_q->p); - modemcf_destroy(_q->mod_payload); + MODEM(_destroy)(_q->mod_payload); // free arrays free(_q->payload_enc); @@ -125,19 +125,19 @@ int qpacketmodem_destroy(qpacketmodem _q) } // reset object -int qpacketmodem_reset(qpacketmodem _q) +int QPACKETMODEM(_reset)(QPACKETMODEM() _q) { - return modemcf_reset(_q->mod_payload); + return MODEM(_reset)(_q->mod_payload); } // print object internals -int qpacketmodem_print(qpacketmodem _q) +int QPACKETMODEM(_print)(QPACKETMODEM() _q) { printf("qpacketmodem:\n"); printf(" check : %s\n", crc_scheme_str[packetizer_get_crc(_q->p)][1]); printf(" fec (inner) : %s\n", fec_scheme_str[packetizer_get_fec0(_q->p)][1]); printf(" fec (outer) : %s\n", fec_scheme_str[packetizer_get_fec1(_q->p)][1]); - printf(" modulation scheme : %s\n", modulation_types[modemcf_get_scheme(_q->mod_payload)].name); + printf(" modulation scheme : %s\n", modulation_types[MODEM(_get_scheme)(_q->mod_payload)].name); printf(" payload dec len : %u\n", _q->payload_dec_len); printf(" payload enc len : %u\n", _q->payload_enc_len); printf(" payload bit len : %u\n", _q->payload_bit_len); @@ -146,7 +146,7 @@ int qpacketmodem_print(qpacketmodem _q) } // -int qpacketmodem_configure(qpacketmodem _q, +int QPACKETMODEM(_configure)(QPACKETMODEM() _q, unsigned int _payload_len, crc_scheme _check, fec_scheme _fec0, @@ -157,8 +157,8 @@ int qpacketmodem_configure(qpacketmodem _q, _q->payload_dec_len = _payload_len; // recreate modem object and get new bits per symbol - _q->mod_payload = modemcf_recreate(_q->mod_payload, _ms); - _q->bits_per_symbol = modemcf_get_bps(_q->mod_payload); + _q->mod_payload = MODEM(_recreate)(_q->mod_payload, _ms); + _q->bits_per_symbol = MODEM(_get_bps)(_q->mod_payload); // recreate packetizer object and compute new encoded payload length _q->p = packetizer_recreate(_q->p, _q->payload_dec_len, _check, _fec0, _fec1); @@ -185,44 +185,44 @@ int qpacketmodem_configure(qpacketmodem _q, } // get length of encoded frame in symbols -unsigned int qpacketmodem_get_frame_len(qpacketmodem _q) +unsigned int QPACKETMODEM(_get_frame_len)(QPACKETMODEM() _q) { return _q->payload_mod_len; } // get unencoded/decoded payload length (bytes) -unsigned int qpacketmodem_get_payload_len(qpacketmodem _q) +unsigned int QPACKETMODEM(_get_payload_len)(QPACKETMODEM() _q) { // number of decoded payload bytes return _q->payload_dec_len; } -unsigned int qpacketmodem_get_crc(qpacketmodem _q) +unsigned int QPACKETMODEM(_get_crc)(QPACKETMODEM() _q) { return packetizer_get_crc(_q->p); } -unsigned int qpacketmodem_get_fec0(qpacketmodem _q) +unsigned int QPACKETMODEM(_get_fec0)(QPACKETMODEM() _q) { return packetizer_get_fec0(_q->p); } -unsigned int qpacketmodem_get_fec1(qpacketmodem _q) +unsigned int QPACKETMODEM(_get_fec1)(QPACKETMODEM() _q) { return packetizer_get_fec1(_q->p); } -unsigned int qpacketmodem_get_modscheme(qpacketmodem _q) +unsigned int QPACKETMODEM(_get_modscheme)(QPACKETMODEM() _q) { - return modemcf_get_scheme(_q->mod_payload); + return MODEM(_get_scheme)(_q->mod_payload); } -float qpacketmodem_get_demodulator_phase_error(qpacketmodem _q) +float QPACKETMODEM(_get_demodulator_phase_error)(QPACKETMODEM() _q) { - return modemcf_get_demodulator_phase_error(_q->mod_payload); + return MODEM(_get_demodulator_phase_error)(_q->mod_payload); } -float qpacketmodem_get_demodulator_evm(qpacketmodem _q) +float QPACKETMODEM(_get_demodulator_evm)(QPACKETMODEM() _q) { return _q->evm; } @@ -231,7 +231,7 @@ float qpacketmodem_get_demodulator_evm(qpacketmodem _q) // _q : qpacketmodem object // _payload : unencoded payload bytes // _syms : encoded but un-modulated payload symbol indices -int qpacketmodem_encode_syms(qpacketmodem _q, +int QPACKETMODEM(_encode_syms)(qpacketmodem _q, const unsigned char * _payload, unsigned char * _syms) { @@ -256,7 +256,7 @@ int qpacketmodem_encode_syms(qpacketmodem _q, // _q : qpacketmodem object // _syms : received hard-decision symbol indices // _payload : recovered decoded payload bytes -int qpacketmodem_decode_syms(qpacketmodem _q, +int QPACKETMODEM(_decode_syms)(qpacketmodem _q, unsigned char * _syms, unsigned char * _payload) { @@ -276,7 +276,7 @@ int qpacketmodem_decode_syms(qpacketmodem _q, // _q : qpacketmodem object // _bits : received soft-decision bits // _payload : recovered decoded payload bytes -int qpacketmodem_decode_bits(qpacketmodem _q, +int QPACKETMODEM(_decode_bits)(qpacketmodem _q, unsigned char * _bits, unsigned char * _payload) { @@ -288,17 +288,17 @@ int qpacketmodem_decode_bits(qpacketmodem _q, // _q : qpacketmodem object // _payload : unencoded payload bytes // _frame : encoded/modulated payload symbols -int qpacketmodem_encode(qpacketmodem _q, +int QPACKETMODEM(_encode)(qpacketmodem _q, const unsigned char * _payload, float complex * _frame) { // encode payload symbols into internal buffer - qpacketmodem_encode_syms(_q, _payload, _q->payload_mod); + QPACKETMODEM(_encode_syms)(_q, _payload, _q->payload_mod); // modulate symbols unsigned int i; for (i=0; i<_q->payload_mod_len; i++) - modemcf_modulate(_q->mod_payload, _q->payload_mod[i], &_frame[i]); + MODEM(_modulate)(_q->mod_payload, _q->payload_mod[i], &_frame[i]); return LIQUID_OK; } @@ -306,7 +306,7 @@ int qpacketmodem_encode(qpacketmodem _q, // _q : qpacketmodem object // _frame : encoded/modulated payload symbols // _payload : recovered decoded payload bytes -int qpacketmodem_decode(qpacketmodem _q, +int QPACKETMODEM(_decode)(qpacketmodem _q, float complex * _frame, unsigned char * _payload) { @@ -318,10 +318,10 @@ int qpacketmodem_decode(qpacketmodem _q, _q->evm = 0.0f; for (i=0; i<_q->payload_mod_len; i++) { // demodulate symbol - modemcf_demodulate(_q->mod_payload, _frame[i], &sym); + MODEM(_demodulate)(_q->mod_payload, _frame[i], &sym); // accumulate error vector magnitude estimate - float e = modemcf_get_demodulator_evm(_q->mod_payload); + float e = MODEM(_get_demodulator_evm)(_q->mod_payload); _q->evm += e*e; // pack decoded symbol into array @@ -343,7 +343,7 @@ int qpacketmodem_decode(qpacketmodem _q, // _q : qpacketmodem object // _frame : encoded/modulated payload symbols // _payload : recovered decoded payload bytes -int qpacketmodem_decode_soft(qpacketmodem _q, +int QPACKETMODEM(_decode_soft)(qpacketmodem _q, float complex * _frame, unsigned char * _payload) { @@ -356,11 +356,11 @@ int qpacketmodem_decode_soft(qpacketmodem _q, _q->evm = 0.0f; for (i=0; i<_q->payload_mod_len; i++) { // demodulate symbol - modemcf_demodulate_soft(_q->mod_payload, _frame[i], &sym, _q->payload_enc+n); + MODEM(_demodulate_soft)(_q->mod_payload, _frame[i], &sym, _q->payload_enc+n); n += _q->bits_per_symbol; // accumulate error vector magnitude estimate - float e = modemcf_get_demodulator_evm(_q->mod_payload); + float e = MODEM(_get_demodulator_evm)(_q->mod_payload); _q->evm += e*e; } //printf("received %u bits (expected %u)\n", n, _q->payload_mod_len * _q->bits_per_symbol); @@ -376,16 +376,16 @@ int qpacketmodem_decode_soft(qpacketmodem _q, // decode symbol from modulated frame samples, returning flag if all symbols received // _q : qpacketmodem object // _frame : encoded/modulated symbol -int qpacketmodem_decode_soft_sym(qpacketmodem _q, +int QPACKETMODEM(_decode_soft_sym)(qpacketmodem _q, float complex _symbol) { unsigned int sym; - modemcf_demodulate_soft(_q->mod_payload, _symbol, &sym, _q->payload_enc + _q->n); + MODEM(_demodulate_soft)(_q->mod_payload, _symbol, &sym, _q->payload_enc + _q->n); _q->n += _q->bits_per_symbol; return _q->n == _q->payload_mod_len * _q->bits_per_symbol; } -int qpacketmodem_decode_soft_payload(qpacketmodem _q, +int QPACKETMODEM(_decode_soft_payload)(qpacketmodem _q, unsigned char * _payload) { assert( _q->n == _q->payload_mod_len * _q->bits_per_symbol); From fec8c7151705cfae32230595b76b7e05b16ec2d9 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 10 Jun 2023 09:41:53 -0400 Subject: [PATCH 155/186] cpfskdem: adding warning to demodulation with h > 2/3 --- src/modem/src/cpfskdem.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/modem/src/cpfskdem.c b/src/modem/src/cpfskdem.c index 4e7fd3321..425400620 100644 --- a/src/modem/src/cpfskdem.c +++ b/src/modem/src/cpfskdem.c @@ -164,10 +164,10 @@ cpfskdem cpfskdem_create(unsigned int _bps, // coherent or non-coherent? // TODO: allow user to specify if (q->h > 0.66667f) { - cpfskdem_init_noncoherent(q); - } else { - cpfskdem_init_coherent(q); + //cpfskdem_init_noncoherent(q); + fprintf(stderr,"warning: cpfskdem_create(), coherent demodulation with h > 2/3 not recommended\n"); } + cpfskdem_init_coherent(q); // reset modem object cpfskdem_reset(q); @@ -235,6 +235,7 @@ int cpfskdem_init_coherent(cpfskdem _q) // initialize non-coherent demodulator int cpfskdem_init_noncoherent(cpfskdem _q) { +#if 0 // specify non-coherent receiver _q->demod_type = CPFSKDEM_NONCOHERENT; @@ -250,6 +251,9 @@ int cpfskdem_init_noncoherent(cpfskdem _q) break; } return LIQUID_OK; +#else + return liquid_error(LIQUID_EUMODE,"cpfskdem_init_noncoherent(), unsupported mode"); +#endif } // destroy modem object From d0ea8d8c1ea3d783228083cd5bc4aeace404d72f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 18 Jun 2023 16:20:01 -0400 Subject: [PATCH 156/186] msequence: adding config autotest --- src/sequence/tests/msequence_autotest.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index 21b62c9a7..4503de323 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -92,3 +92,27 @@ void autotest_msequence_m10() { msequence_test_autocorrelation(10); } // n void autotest_msequence_m11() { msequence_test_autocorrelation(11); } // n = 2047 void autotest_msequence_m12() { msequence_test_autocorrelation(12); } // n = 4095 +void autotest_msequence_config() +{ +#if LIQUID_STRICT_EXIT + AUTOTEST_WARN("skipping firfilt config test with strict exit enabled\n"); + return; +#endif +#if !LIQUID_SUPPRESS_ERROR_OUTPUT + fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); +#endif + // check invalid configurations + CONTEND_ISNULL(msequence_create(100, 0, 0)) + CONTEND_ISNULL(msequence_create_genpoly(0)) + + // create proper object and test configurations + msequence q = msequence_create_genpoly(LIQUID_MSEQUENCE_GENPOLY_M11); + + CONTEND_EQUALITY(LIQUID_OK, msequence_print(q)) + CONTEND_EQUALITY(1<<10U, msequence_get_state(q)) + CONTEND_EQUALITY(LIQUID_OK, msequence_set_state(q, 0x8a)) + CONTEND_EQUALITY(0x8a, msequence_get_state(q)) + + msequence_destroy(q); +} + From 7cedc85ed7eb240d0ec5534ffdfa88fe6ae5876e Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sun, 18 Jun 2023 16:31:09 -0400 Subject: [PATCH 157/186] msequence: extending maximum sequence, divorcing from default validation --- src/sequence/src/msequence.c | 44 ++++++++++++------------- src/sequence/tests/msequence_autotest.c | 9 ++--- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index e0a98cbcf..56b380b82 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2020 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,7 @@ * THE SOFTWARE. */ -// -// m-sequence -// +// maximum-length sequence #include #include @@ -32,30 +30,30 @@ #include "liquid.internal.h" #define LIQUID_MIN_MSEQUENCE_M 2 -#define LIQUID_MAX_MSEQUENCE_M 15 +#define LIQUID_MAX_MSEQUENCE_M 31 // msequence structure // Note that 'g' is stored as the default polynomial shifted to the // right by one bit; this bit is implied and not actually used in // the shift register's feedback bit computation. struct msequence_s msequence_default[16] = { -// m, g, a, n, v, b - {0, 0, 1, 0, 1, 0}, // dummy placeholder - {0, 0, 1, 0, 1, 0}, // dummy placeholder - {2, 0x0003, 0x0002, 3, 0x0002, 0}, - {3, 0x0005, 0x0004, 7, 0x0004, 0}, - {4, 0x0009, 0x0008, 15, 0x0008, 0}, - {5, 0x0012, 0x0010, 31, 0x0010, 0}, - {6, 0x0021, 0x0020, 63, 0x0020, 0}, - {7, 0x0044, 0x0040, 127, 0x0040, 0}, - {8, 0x008E, 0x0080, 255, 0x0080, 0}, - {9, 0x0108, 0x0100, 511, 0x0100, 0}, - {10, 0x0204, 0x0200, 1023, 0x0200, 0}, - {11, 0x0402, 0x0400, 2047, 0x0400, 0}, - {12, 0x0829, 0x0800, 4095, 0x0800, 0}, - {13, 0x100d, 0x1000, 8191, 0x1000, 0}, - {14, 0x2015, 0x2000, 16383, 0x2000, 0}, - {15, 0x4001, 0x4000, 32767, 0x4000, 0} +// m, g, a, n, v, b + {0, 0, 1, 0, 1, 0}, // dummy placeholder + {0, 0, 1, 0, 1, 0}, // dummy placeholder + {2, 0x0003, 0x0002, (1<< 2U)-1, 0x0002, 0}, + {3, 0x0005, 0x0004, (1<< 3U)-1, 0x0004, 0}, + {4, 0x0009, 0x0008, (1<< 4U)-1, 0x0008, 0}, + {5, 0x0012, 0x0010, (1<< 5U)-1, 0x0010, 0}, + {6, 0x0021, 0x0020, (1<< 6U)-1, 0x0020, 0}, + {7, 0x0044, 0x0040, (1<< 7U)-1, 0x0040, 0}, + {8, 0x008E, 0x0080, (1<< 8U)-1, 0x0080, 0}, + {9, 0x0108, 0x0100, (1<< 9U)-1, 0x0100, 0}, + {10, 0x0204, 0x0200, (1<<10U)-1, 0x0200, 0}, + {11, 0x0402, 0x0400, (1<<11U)-1, 0x0400, 0}, + {12, 0x0829, 0x0800, (1<<12U)-1, 0x0800, 0}, + {13, 0x100d, 0x1000, (1<<13U)-1, 0x1000, 0}, + {14, 0x2015, 0x2000, (1<<14U)-1, 0x2000, 0}, + {15, 0x4001, 0x4000, (1<<15U)-1, 0x4000, 0} }; // create a maximal-length sequence (m-sequence) object with @@ -117,7 +115,7 @@ msequence msequence_create_genpoly(unsigned int _g) msequence msequence_create_default(unsigned int _m) { // validate input - if (_m > LIQUID_MAX_MSEQUENCE_M || _m < LIQUID_MIN_MSEQUENCE_M) + if (_m < LIQUID_MIN_MSEQUENCE_M || _m > 15) return liquid_error_config("msequence_create(), m not in range"); // allocate memory for msequence object diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index 4503de323..d379484df 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -102,16 +102,17 @@ void autotest_msequence_config() fprintf(stderr,"warning: ignore potential errors here; checking for invalid configurations\n"); #endif // check invalid configurations - CONTEND_ISNULL(msequence_create(100, 0, 0)) - CONTEND_ISNULL(msequence_create_genpoly(0)) + CONTEND_ISNULL(msequence_create (100, 0, 0)) + CONTEND_ISNULL(msequence_create_default( 16)) + CONTEND_ISNULL(msequence_create_genpoly( 0)) // create proper object and test configurations msequence q = msequence_create_genpoly(LIQUID_MSEQUENCE_GENPOLY_M11); CONTEND_EQUALITY(LIQUID_OK, msequence_print(q)) - CONTEND_EQUALITY(1<<10U, msequence_get_state(q)) + CONTEND_EQUALITY(1<<10U, msequence_get_state(q)) CONTEND_EQUALITY(LIQUID_OK, msequence_set_state(q, 0x8a)) - CONTEND_EQUALITY(0x8a, msequence_get_state(q)) + CONTEND_EQUALITY(0x8a, msequence_get_state(q)) msequence_destroy(q); } From eb85ff76d0590dda454521601f8a32e50ea0ce32 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 09:07:12 -0400 Subject: [PATCH 158/186] msequence/autotest: adding test to find period of sequence --- src/sequence/tests/msequence_autotest.c | 63 +++++++++++++++++++------ 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index d379484df..8fc69179c 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2015 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,7 @@ void autotest_bsequence_init_msequence() { // create and initialize m-sequence msequence ms = msequence_create_default(4); - + // create and initialize binary sequence on m-sequence bsequence bs; bs = bsequence_create( msequence_get_length(ms) ); @@ -48,7 +48,7 @@ void msequence_test_autocorrelation(unsigned int _m) // create and initialize m-sequence msequence ms = msequence_create_default(_m); unsigned int n = msequence_get_length(ms); - + // create and initialize first binary sequence on m-sequence bsequence bs1 = bsequence_create(n); bsequence_init_msequence(bs1, ms); @@ -80,17 +80,52 @@ void msequence_test_autocorrelation(unsigned int _m) msequence_destroy(ms); } -void autotest_msequence_m2() { msequence_test_autocorrelation(2); } // n = 3 -void autotest_msequence_m3() { msequence_test_autocorrelation(3); } // n = 7 -void autotest_msequence_m4() { msequence_test_autocorrelation(4); } // n = 15 -void autotest_msequence_m5() { msequence_test_autocorrelation(5); } // n = 31 -void autotest_msequence_m6() { msequence_test_autocorrelation(6); } // n = 63 -void autotest_msequence_m7() { msequence_test_autocorrelation(7); } // n = 127 -void autotest_msequence_m8() { msequence_test_autocorrelation(8); } // n = 255 -void autotest_msequence_m9() { msequence_test_autocorrelation(9); } // n = 511 -void autotest_msequence_m10() { msequence_test_autocorrelation(10); } // n = 1023 -void autotest_msequence_m11() { msequence_test_autocorrelation(11); } // n = 2047 -void autotest_msequence_m12() { msequence_test_autocorrelation(12); } // n = 4095 +void autotest_msequence_xcorr_m2() { msequence_test_autocorrelation(2); } // n = 3 +void autotest_msequence_xcorr_m3() { msequence_test_autocorrelation(3); } // n = 7 +void autotest_msequence_xcorr_m4() { msequence_test_autocorrelation(4); } // n = 15 +void autotest_msequence_xcorr_m5() { msequence_test_autocorrelation(5); } // n = 31 +void autotest_msequence_xcorr_m6() { msequence_test_autocorrelation(6); } // n = 63 +void autotest_msequence_xcorr_m7() { msequence_test_autocorrelation(7); } // n = 127 +void autotest_msequence_xcorr_m8() { msequence_test_autocorrelation(8); } // n = 255 +void autotest_msequence_xcorr_m9() { msequence_test_autocorrelation(9); } // n = 511 +void autotest_msequence_xcorr_m10() { msequence_test_autocorrelation(10); } // n = 1023 +void autotest_msequence_xcorr_m11() { msequence_test_autocorrelation(11); } // n = 2047 +void autotest_msequence_xcorr_m12() { msequence_test_autocorrelation(12); } // n = 4095 + +// helper function to test cyclic period of sequences +void msequence_test_period(unsigned int _m) +{ + // create and initialize m-sequence + msequence q = msequence_create_default(_m); + + unsigned int n = msequence_get_length(q); + unsigned int s = msequence_get_state(q); + + // cycle through sequence and look for initial state + unsigned int i; + unsigned int period = 0; + for (i=0; i Date: Mon, 19 Jun 2023 10:02:36 -0400 Subject: [PATCH 159/186] msequence: adding convenience methods for getting attributes --- include/liquid.h | 10 ++++++++-- src/sequence/src/msequence.c | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 08c168373..cf6096b4c 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -9561,7 +9561,7 @@ unsigned int msequence_advance(msequence _ms); // advancing _bps bits and returning compacted symbol // _ms : m-sequence object // _bps : bits per symbol of output -unsigned int msequence_generate_symbol(msequence _ms, +unsigned int msequence_generate_symbol(msequence _ms, unsigned int _bps); // reset msequence shift register to original state, typically '1' @@ -9573,9 +9573,15 @@ int msequence_reset(msequence _ms); int bsequence_init_msequence(bsequence _bs, msequence _ms); -// get the length of the sequence +// get the length of the generator polynomial, g (m) +unsigned int msequence_get_genpoly_length(msequence _ms); + +// get the length of the sequence (n=2^m-1) unsigned int msequence_get_length(msequence _ms); +// get the generator polynomial, g +unsigned int msequence_get_genpoly(msequence _ms); + // get the internal state of the sequence unsigned int msequence_get_state(msequence _ms); diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index 56b380b82..4736418c4 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -208,12 +208,24 @@ int bsequence_init_msequence(bsequence _bs, return LIQUID_OK; } +// get the length of the generator polynomial, g (m) +unsigned int msequence_get_genpoly_length(msequence _ms) +{ + return _ms->m; +} + // get the length of the sequence unsigned int msequence_get_length(msequence _ms) { return _ms->n; } +// get the generator polynomial, g +unsigned int msequence_get_genpoly(msequence _ms) +{ + return _ms->g; +} + // get the internal state of the sequence unsigned int msequence_get_state(msequence _ms) { From 9f936ed957d41f1c30e25333a83933fd0ffc10fd Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 10:04:28 -0400 Subject: [PATCH 160/186] msequence: moving structure definition to internal source file --- include/liquid.internal.h | 14 -------------- src/sequence/src/msequence.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/include/liquid.internal.h b/include/liquid.internal.h index d38648460..4e2f448e0 100644 --- a/include/liquid.internal.h +++ b/include/liquid.internal.h @@ -1645,20 +1645,6 @@ float randgammaf_delta(float _delta); // MODULE : sequence // -// maximal-length sequence -struct msequence_s { - unsigned int m; // length generator polynomial, shift register - unsigned int g; // generator polynomial - unsigned int a; // initial shift register state, default: 1 - - unsigned int n; // length of sequence, n = (2^m)-1 - unsigned int v; // shift register - unsigned int b; // return bit -}; - -// Default msequence generator objects -extern struct msequence_s msequence_default[16]; - // // MODULE : utility diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index 4736418c4..e1e41db1b 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -32,6 +32,17 @@ #define LIQUID_MIN_MSEQUENCE_M 2 #define LIQUID_MAX_MSEQUENCE_M 31 +// maximal-length sequence +struct msequence_s { + unsigned int m; // length generator polynomial, shift register + unsigned int g; // generator polynomial + unsigned int a; // initial shift register state, default: 1 + + unsigned int n; // length of sequence, n = (2^m)-1 + unsigned int v; // shift register + unsigned int b; // return bit +}; + // msequence structure // Note that 'g' is stored as the default polynomial shifted to the // right by one bit; this bit is implied and not actually used in From 5405b999d797af9b56d017d7c9abb3d233b0fbb3 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 10:10:49 -0400 Subject: [PATCH 161/186] msequence: using global default generator polynomials as defaults --- src/sequence/src/msequence.c | 54 +++++++++---------------- src/sequence/tests/msequence_autotest.c | 5 ++- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index e1e41db1b..8625b7c8d 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -43,30 +43,6 @@ struct msequence_s { unsigned int b; // return bit }; -// msequence structure -// Note that 'g' is stored as the default polynomial shifted to the -// right by one bit; this bit is implied and not actually used in -// the shift register's feedback bit computation. -struct msequence_s msequence_default[16] = { -// m, g, a, n, v, b - {0, 0, 1, 0, 1, 0}, // dummy placeholder - {0, 0, 1, 0, 1, 0}, // dummy placeholder - {2, 0x0003, 0x0002, (1<< 2U)-1, 0x0002, 0}, - {3, 0x0005, 0x0004, (1<< 3U)-1, 0x0004, 0}, - {4, 0x0009, 0x0008, (1<< 4U)-1, 0x0008, 0}, - {5, 0x0012, 0x0010, (1<< 5U)-1, 0x0010, 0}, - {6, 0x0021, 0x0020, (1<< 6U)-1, 0x0020, 0}, - {7, 0x0044, 0x0040, (1<< 7U)-1, 0x0040, 0}, - {8, 0x008E, 0x0080, (1<< 8U)-1, 0x0080, 0}, - {9, 0x0108, 0x0100, (1<< 9U)-1, 0x0100, 0}, - {10, 0x0204, 0x0200, (1<<10U)-1, 0x0200, 0}, - {11, 0x0402, 0x0400, (1<<11U)-1, 0x0400, 0}, - {12, 0x0829, 0x0800, (1<<12U)-1, 0x0800, 0}, - {13, 0x100d, 0x1000, (1<<13U)-1, 0x1000, 0}, - {14, 0x2015, 0x2000, (1<<14U)-1, 0x2000, 0}, - {15, 0x4001, 0x4000, (1<<15U)-1, 0x4000, 0} -}; - // create a maximal-length sequence (m-sequence) object with // an internal shift register length of _m bits. // _m : generator polynomial length, sequence length is (2^m)-1 @@ -125,18 +101,28 @@ msequence msequence_create_genpoly(unsigned int _g) // creates a default maximal-length sequence msequence msequence_create_default(unsigned int _m) { - // validate input - if (_m < LIQUID_MIN_MSEQUENCE_M || _m > 15) - return liquid_error_config("msequence_create(), m not in range"); - - // allocate memory for msequence object - msequence ms = (msequence) malloc(sizeof(struct msequence_s)); - - // copy default sequence - memmove(ms, &msequence_default[_m], sizeof(struct msequence_s)); + unsigned int g = 0; + switch (_m) { + case 2: g = LIQUID_MSEQUENCE_GENPOLY_M2; break; + case 3: g = LIQUID_MSEQUENCE_GENPOLY_M3; break; + case 4: g = LIQUID_MSEQUENCE_GENPOLY_M4; break; + case 5: g = LIQUID_MSEQUENCE_GENPOLY_M5; break; + case 6: g = LIQUID_MSEQUENCE_GENPOLY_M6; break; + case 7: g = LIQUID_MSEQUENCE_GENPOLY_M7; break; + case 8: g = LIQUID_MSEQUENCE_GENPOLY_M8; break; + case 9: g = LIQUID_MSEQUENCE_GENPOLY_M9; break; + case 10: g = LIQUID_MSEQUENCE_GENPOLY_M10; break; + case 11: g = LIQUID_MSEQUENCE_GENPOLY_M11; break; + case 12: g = LIQUID_MSEQUENCE_GENPOLY_M12; break; + case 13: g = LIQUID_MSEQUENCE_GENPOLY_M13; break; + case 14: g = LIQUID_MSEQUENCE_GENPOLY_M14; break; + case 15: g = LIQUID_MSEQUENCE_GENPOLY_M15; break; + default: + return liquid_error_config("msequence_create_default(), m (%u) not in range", _m); + } // return - return ms; + return msequence_create_genpoly(g); } // destroy an msequence object, freeing all internal memory diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index 8fc69179c..bdce9b22f 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -98,7 +98,7 @@ void msequence_test_period(unsigned int _m) // create and initialize m-sequence msequence q = msequence_create_default(_m); - unsigned int n = msequence_get_length(q); + unsigned int n = (1U << _m) - 1; unsigned int s = msequence_get_state(q); // cycle through sequence and look for initial state @@ -126,6 +126,9 @@ void autotest_msequence_period_m9() { msequence_test_period(9); } void autotest_msequence_period_m10() { msequence_test_period(10); } void autotest_msequence_period_m11() { msequence_test_period(11); } void autotest_msequence_period_m12() { msequence_test_period(12); } +void autotest_msequence_period_m13() { msequence_test_period(13); } +void autotest_msequence_period_m14() { msequence_test_period(14); } +void autotest_msequence_period_m15() { msequence_test_period(15); } void autotest_msequence_config() { From 68287b29988d006908e187864d5737fb5f21ab6c Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 10:21:06 -0400 Subject: [PATCH 162/186] msequence: adding methods to measure period of shift register --- include/liquid.h | 6 +++++ src/sequence/src/msequence.c | 33 +++++++++++++++++++++++++ src/sequence/tests/msequence_autotest.c | 15 +++-------- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index cf6096b4c..7cd461c91 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -9589,6 +9589,12 @@ unsigned int msequence_get_state(msequence _ms); int msequence_set_state(msequence _ms, unsigned int _a); +// measure the period the shift register (should be 2^m-1 with a proper generator polynomial) +unsigned int msequence_measure_period(msequence _ms); + +// measure the period of a generator polynomial +unsigned int msequence_genpoly_period(unsigned int _g); + // // MODULE : utility diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index 8625b7c8d..ea71ff625 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -240,3 +240,36 @@ int msequence_set_state(msequence _ms, return LIQUID_OK; } +// measure the period the shift register (should be 2^m-1 with a proper generator polynomial) +unsigned int msequence_measure_period(msequence _ms) +{ + // get current state + unsigned int s = msequence_get_state(_ms); + + // cycle through sequence and look for initial state + unsigned int i; + unsigned int period = 0; + for (i=0; i<_ms->n+1; i++) { + msequence_advance(_ms); + period++; + if (msequence_get_state(_ms)==s) + break; + } + + // assert that state has been returned + return period; +} + +// measure the period of a generator polynomial +unsigned int msequence_genpoly_period(unsigned int _g) +{ + msequence q = msequence_create_genpoly(_g); + if (q == NULL) { + liquid_error(LIQUID_EICONFIG,"msequence_genpoly_period(), invalid generator polynomial 0x%x\n", _g); + return 0; + } + unsigned int period = msequence_measure_period(q); + msequence_destroy(q); + return period; +} + diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index bdce9b22f..6c74711a4 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -98,19 +98,10 @@ void msequence_test_period(unsigned int _m) // create and initialize m-sequence msequence q = msequence_create_default(_m); + // measure period and compare to expected unsigned int n = (1U << _m) - 1; - unsigned int s = msequence_get_state(q); - - // cycle through sequence and look for initial state - unsigned int i; - unsigned int period = 0; - for (i=0; i Date: Mon, 19 Jun 2023 11:05:58 -0400 Subject: [PATCH 163/186] msequence: reversing order for generator polynomial in create methods * makes more consistent with literature * internal representation still reversed * simplifies instantiating new msequence objects with external polynomials --- examples/msequence_example.c | 1 - include/liquid.h | 30 ++++++++++++------------ src/sequence/src/msequence.c | 44 ++++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/examples/msequence_example.c b/examples/msequence_example.c index df35a08ab..aa34c6ec8 100644 --- a/examples/msequence_example.c +++ b/examples/msequence_example.c @@ -100,7 +100,6 @@ int main(int argc, char*argv[]) fclose(fid); printf("results written to %s.\n", OUTPUT_FILENAME); - return 0; } diff --git a/include/liquid.h b/include/liquid.h index 7cd461c91..6c394e077 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -9515,21 +9515,21 @@ int bsequence_create_ccodes(bsequence _a, bsequence _b); #define LIQUID_MAX_MSEQUENCE_LENGTH 32767 -// default m-sequence generators: g (hex) m n g (oct) g (binary) -#define LIQUID_MSEQUENCE_GENPOLY_M2 0x0007 // 2 3 7 111 -#define LIQUID_MSEQUENCE_GENPOLY_M3 0x000B // 3 7 13 1011 -#define LIQUID_MSEQUENCE_GENPOLY_M4 0x0013 // 4 15 23 10011 -#define LIQUID_MSEQUENCE_GENPOLY_M5 0x0025 // 5 31 45 100101 -#define LIQUID_MSEQUENCE_GENPOLY_M6 0x0043 // 6 63 103 1000011 -#define LIQUID_MSEQUENCE_GENPOLY_M7 0x0089 // 7 127 211 10001001 -#define LIQUID_MSEQUENCE_GENPOLY_M8 0x011D // 8 255 435 100101101 -#define LIQUID_MSEQUENCE_GENPOLY_M9 0x0211 // 9 511 1021 1000010001 -#define LIQUID_MSEQUENCE_GENPOLY_M10 0x0409 // 10 1023 2011 10000001001 -#define LIQUID_MSEQUENCE_GENPOLY_M11 0x0805 // 11 2047 4005 100000000101 -#define LIQUID_MSEQUENCE_GENPOLY_M12 0x1053 // 12 4095 10123 1000001010011 -#define LIQUID_MSEQUENCE_GENPOLY_M13 0x201b // 13 8191 20033 10000000011011 -#define LIQUID_MSEQUENCE_GENPOLY_M14 0x402b // 14 16383 40053 100000000101011 -#define LIQUID_MSEQUENCE_GENPOLY_M15 0x8003 // 15 32767 100003 1000000000000011 +// default m-sequence generators: g (hex) m n +#define LIQUID_MSEQUENCE_GENPOLY_M2 0x0003 // 2 3 +#define LIQUID_MSEQUENCE_GENPOLY_M3 0x0006 // 3 7 +#define LIQUID_MSEQUENCE_GENPOLY_M4 0x000c // 4 15 +#define LIQUID_MSEQUENCE_GENPOLY_M5 0x0014 // 5 31 +#define LIQUID_MSEQUENCE_GENPOLY_M6 0x0030 // 6 63 +#define LIQUID_MSEQUENCE_GENPOLY_M7 0x0060 // 7 127 +#define LIQUID_MSEQUENCE_GENPOLY_M8 0x00b8 // 8 255 +#define LIQUID_MSEQUENCE_GENPOLY_M9 0x0110 // 9 511 +#define LIQUID_MSEQUENCE_GENPOLY_M10 0x0240 // 10 1023 +#define LIQUID_MSEQUENCE_GENPOLY_M11 0x0500 // 11 2047 +#define LIQUID_MSEQUENCE_GENPOLY_M12 0x0e08 // 12 4095 +#define LIQUID_MSEQUENCE_GENPOLY_M13 0x1c80 // 13 8191 +#define LIQUID_MSEQUENCE_GENPOLY_M14 0x3802 // 14 16383 +#define LIQUID_MSEQUENCE_GENPOLY_M15 0x6000 // 15 32767 typedef struct msequence_s * msequence; diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index ea71ff625..6e8377a17 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -34,13 +34,15 @@ // maximal-length sequence struct msequence_s { - unsigned int m; // length generator polynomial, shift register - unsigned int g; // generator polynomial - unsigned int a; // initial shift register state, default: 1 - - unsigned int n; // length of sequence, n = (2^m)-1 - unsigned int v; // shift register - unsigned int b; // return bit + unsigned int m; // length generator polynomial, shift register + unsigned int g; // generator polynomial, form: { x^m + ... + 1 } + unsigned int a; // initial shift register state, default: 1 + + // derived values + unsigned int genpoly; // generator polynomial, bit-reversed from above + unsigned int n; // length of sequence, n = (2^m)-1 + unsigned int v; // shift register + unsigned int b; // return bit }; // create a maximal-length sequence (m-sequence) object with @@ -54,14 +56,17 @@ msequence msequence_create(unsigned int _m, { // validate input if (_m > LIQUID_MAX_MSEQUENCE_M || _m < LIQUID_MIN_MSEQUENCE_M) - return liquid_error_config("msequence_create(), m not in range"); + return liquid_error_config("msequence_create(), m (%u) not in range", _m); + //if (_a == 0) + // return liquid_error_config("msequence_create(), state 'a' cannot be 0"); // allocate memory for msequence object msequence ms = (msequence) malloc(sizeof(struct msequence_s)); // set internal values ms->m = _m; // generator polynomial length - ms->g = _g >> 1; // generator polynomial (clip off most significant bit) + ms->g = _g; // generator polynomial + //ms->g = _g >> 1; // generator polynomial (clip off most significant bit) // initialize state register, reversing order // 0001 -> 1000 @@ -73,6 +78,14 @@ msequence msequence_create(unsigned int _m, _a >>= 1; } + // initialize reverse-order generator polynomial, ignoring implied most-significant bit + ms->genpoly = 1; + for (i=0; im-1; i++) { + ms->genpoly <<= 1; + ms->genpoly |= (_g & 0x01); + _g >>= 1; + } + ms->n = (1<<_m)-1; // sequence length, (2^m)-1 ms->v = ms->a; // shift register ms->b = 0; // return bit @@ -85,14 +98,14 @@ msequence msequence_create(unsigned int _m, msequence msequence_create_genpoly(unsigned int _g) { unsigned int t = liquid_msb_index(_g); - + // validate input if (t < 2) return liquid_error_config("msequence_create_genpoly(), invalid generator polynomial: 0x%x", _g); // compute derived values - unsigned int m = t - 1; // m-sequence shift register length - unsigned int a = 1; // m-sequence initial state + unsigned int m = t; // m-sequence shift register length + unsigned int a = 1; // m-sequence initial state // generate object and return return msequence_create(m,_g,a); @@ -133,8 +146,10 @@ int msequence_destroy(msequence _ms) } // prints the sequence's internal state to the screen -int msequence_print(msequence _m) +int msequence_print(msequence _ms) { + printf("m, _ms->n, _ms->g, _ms->genpoly); +#if 0 unsigned int i; printf("msequence: m=%u (n=%u):\n", _m->m, _m->n); @@ -150,6 +165,7 @@ int msequence_print(msequence _m) for (i=0; i<_m->m; i++) printf("%c", ((_m->g) >> (_m->m-i-1)) & 0x01 ? '1' : '0'); printf("\n"); +#endif return LIQUID_OK; } @@ -158,7 +174,7 @@ unsigned int msequence_advance(msequence _ms) { // compute return bit as binary dot product between the // internal shift register and the generator polynomial - _ms->b = liquid_bdotprod( _ms->v, _ms->g ); + _ms->b = liquid_bdotprod( _ms->v, _ms->genpoly ); _ms->v <<= 1; // shift internal register _ms->v |= _ms->b; // push bit onto register From 9c5a16768ffdb706a01445f05d5f768630dc8636 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 11:27:51 -0400 Subject: [PATCH 164/186] msequence: extending default sequence lengths to m=31 --- include/liquid.h | 48 ++++++++++++++++--------- src/sequence/src/msequence.c | 16 +++++++++ src/sequence/tests/msequence_autotest.c | 18 +++++++++- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 6c394e077..5aed449dc 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -9513,23 +9513,37 @@ int bsequence_create_ccodes(bsequence _a, bsequence _b); // M-Sequence -#define LIQUID_MAX_MSEQUENCE_LENGTH 32767 - -// default m-sequence generators: g (hex) m n -#define LIQUID_MSEQUENCE_GENPOLY_M2 0x0003 // 2 3 -#define LIQUID_MSEQUENCE_GENPOLY_M3 0x0006 // 3 7 -#define LIQUID_MSEQUENCE_GENPOLY_M4 0x000c // 4 15 -#define LIQUID_MSEQUENCE_GENPOLY_M5 0x0014 // 5 31 -#define LIQUID_MSEQUENCE_GENPOLY_M6 0x0030 // 6 63 -#define LIQUID_MSEQUENCE_GENPOLY_M7 0x0060 // 7 127 -#define LIQUID_MSEQUENCE_GENPOLY_M8 0x00b8 // 8 255 -#define LIQUID_MSEQUENCE_GENPOLY_M9 0x0110 // 9 511 -#define LIQUID_MSEQUENCE_GENPOLY_M10 0x0240 // 10 1023 -#define LIQUID_MSEQUENCE_GENPOLY_M11 0x0500 // 11 2047 -#define LIQUID_MSEQUENCE_GENPOLY_M12 0x0e08 // 12 4095 -#define LIQUID_MSEQUENCE_GENPOLY_M13 0x1c80 // 13 8191 -#define LIQUID_MSEQUENCE_GENPOLY_M14 0x3802 // 14 16383 -#define LIQUID_MSEQUENCE_GENPOLY_M15 0x6000 // 15 32767 +// default m-sequence generators: g (hex) m n +#define LIQUID_MSEQUENCE_GENPOLY_M2 0x00000003 // 2 3 +#define LIQUID_MSEQUENCE_GENPOLY_M3 0x00000006 // 3 7 +#define LIQUID_MSEQUENCE_GENPOLY_M4 0x0000000c // 4 15 +#define LIQUID_MSEQUENCE_GENPOLY_M5 0x00000014 // 5 31 +#define LIQUID_MSEQUENCE_GENPOLY_M6 0x00000030 // 6 63 +#define LIQUID_MSEQUENCE_GENPOLY_M7 0x00000060 // 7 127 +#define LIQUID_MSEQUENCE_GENPOLY_M8 0x000000b8 // 8 255 +#define LIQUID_MSEQUENCE_GENPOLY_M9 0x00000110 // 9 511 +#define LIQUID_MSEQUENCE_GENPOLY_M10 0x00000240 // 10 1,023 +#define LIQUID_MSEQUENCE_GENPOLY_M11 0x00000500 // 11 2,047 +#define LIQUID_MSEQUENCE_GENPOLY_M12 0x00000e08 // 12 4,095 +#define LIQUID_MSEQUENCE_GENPOLY_M13 0x00001c80 // 13 8,191 +#define LIQUID_MSEQUENCE_GENPOLY_M14 0x00003802 // 14 16,383 +#define LIQUID_MSEQUENCE_GENPOLY_M15 0x00006000 // 15 32,767 +#define LIQUID_MSEQUENCE_GENPOLY_M16 0x0000d008 // 16 65,535 +#define LIQUID_MSEQUENCE_GENPOLY_M17 0x00012000 // 17 131,071 +#define LIQUID_MSEQUENCE_GENPOLY_M18 0x00020400 // 18 262,143 +#define LIQUID_MSEQUENCE_GENPOLY_M19 0x00072000 // 19 524,287 +#define LIQUID_MSEQUENCE_GENPOLY_M20 0x00090000 // 20 1,048,575 +#define LIQUID_MSEQUENCE_GENPOLY_M21 0x00140000 // 21 2,097,151 +#define LIQUID_MSEQUENCE_GENPOLY_M22 0x00300000 // 22 4,194,303 +#define LIQUID_MSEQUENCE_GENPOLY_M23 0x00420000 // 23 8,388,607 +#define LIQUID_MSEQUENCE_GENPOLY_M24 0x00e10000 // 24 16,777,215 +#define LIQUID_MSEQUENCE_GENPOLY_M25 0x01000004 // 25 33,554,431 +#define LIQUID_MSEQUENCE_GENPOLY_M26 0x02000023 // 26 67,108,863 +#define LIQUID_MSEQUENCE_GENPOLY_M27 0x04000013 // 27 134,217,727 +#define LIQUID_MSEQUENCE_GENPOLY_M28 0x08000004 // 28 268,435,455 +#define LIQUID_MSEQUENCE_GENPOLY_M29 0x10000002 // 29 536,870,911 +#define LIQUID_MSEQUENCE_GENPOLY_M30 0x20000029 // 30 1,073,741,823 +#define LIQUID_MSEQUENCE_GENPOLY_M31 0x40000004 // 31 2,147,483,647 typedef struct msequence_s * msequence; diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index 6e8377a17..b296d0608 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -130,6 +130,22 @@ msequence msequence_create_default(unsigned int _m) case 13: g = LIQUID_MSEQUENCE_GENPOLY_M13; break; case 14: g = LIQUID_MSEQUENCE_GENPOLY_M14; break; case 15: g = LIQUID_MSEQUENCE_GENPOLY_M15; break; + case 16: g = LIQUID_MSEQUENCE_GENPOLY_M16; break; + case 17: g = LIQUID_MSEQUENCE_GENPOLY_M17; break; + case 18: g = LIQUID_MSEQUENCE_GENPOLY_M18; break; + case 19: g = LIQUID_MSEQUENCE_GENPOLY_M19; break; + case 20: g = LIQUID_MSEQUENCE_GENPOLY_M20; break; + case 21: g = LIQUID_MSEQUENCE_GENPOLY_M21; break; + case 22: g = LIQUID_MSEQUENCE_GENPOLY_M22; break; + case 23: g = LIQUID_MSEQUENCE_GENPOLY_M23; break; + case 24: g = LIQUID_MSEQUENCE_GENPOLY_M24; break; + case 25: g = LIQUID_MSEQUENCE_GENPOLY_M25; break; + case 26: g = LIQUID_MSEQUENCE_GENPOLY_M26; break; + case 27: g = LIQUID_MSEQUENCE_GENPOLY_M27; break; + case 28: g = LIQUID_MSEQUENCE_GENPOLY_M28; break; + case 29: g = LIQUID_MSEQUENCE_GENPOLY_M29; break; + case 30: g = LIQUID_MSEQUENCE_GENPOLY_M30; break; + case 31: g = LIQUID_MSEQUENCE_GENPOLY_M31; break; default: return liquid_error_config("msequence_create_default(), m (%u) not in range", _m); } diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index 6c74711a4..dac971973 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -120,6 +120,22 @@ void autotest_msequence_period_m12() { msequence_test_period(12); } void autotest_msequence_period_m13() { msequence_test_period(13); } void autotest_msequence_period_m14() { msequence_test_period(14); } void autotest_msequence_period_m15() { msequence_test_period(15); } +void autotest_msequence_period_m16() { msequence_test_period(16); } +void autotest_msequence_period_m17() { msequence_test_period(17); } +void autotest_msequence_period_m18() { msequence_test_period(18); } +void autotest_msequence_period_m19() { msequence_test_period(19); } +void autotest_msequence_period_m20() { msequence_test_period(20); } +void autotest_msequence_period_m21() { msequence_test_period(21); } +void autotest_msequence_period_m22() { msequence_test_period(22); } +void autotest_msequence_period_m23() { msequence_test_period(23); } +void autotest_msequence_period_m24() { msequence_test_period(24); } +void autotest_msequence_period_m25() { msequence_test_period(25); } +void autotest_msequence_period_m26() { msequence_test_period(26); } +void autotest_msequence_period_m27() { msequence_test_period(27); } +void autotest_msequence_period_m28() { msequence_test_period(28); } +void autotest_msequence_period_m29() { msequence_test_period(29); } +void autotest_msequence_period_m30() { msequence_test_period(30); } +void autotest_msequence_period_m31() { msequence_test_period(31); } void autotest_msequence_config() { @@ -132,7 +148,7 @@ void autotest_msequence_config() #endif // check invalid configurations CONTEND_ISNULL(msequence_create (100, 0, 0)) - CONTEND_ISNULL(msequence_create_default( 16)) + CONTEND_ISNULL(msequence_create_default( 32)) // too long CONTEND_ISNULL(msequence_create_genpoly( 0)) // create proper object and test configurations From 55cd1f2c852b19d35fed240eddc1f6c8b94e9b5c Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 11:33:51 -0400 Subject: [PATCH 165/186] msequence: cleaning print() method --- src/sequence/src/msequence.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index b296d0608..d4d192edd 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -164,24 +164,8 @@ int msequence_destroy(msequence _ms) // prints the sequence's internal state to the screen int msequence_print(msequence _ms) { - printf("m, _ms->n, _ms->g, _ms->genpoly); -#if 0 - unsigned int i; - - printf("msequence: m=%u (n=%u):\n", _m->m, _m->n); - - // print shift register - printf(" shift register: "); - for (i=0; i<_m->m; i++) - printf("%c", ((_m->v) >> (_m->m-i-1)) & 0x01 ? '1' : '0'); - printf("\n"); - - // print generator polynomial - printf(" generator poly: "); - for (i=0; i<_m->m; i++) - printf("%c", ((_m->g) >> (_m->m-i-1)) & 0x01 ? '1' : '0'); - printf("\n"); -#endif + printf("\n", + _ms->m, _ms->n, _ms->g, _ms->v); return LIQUID_OK; } From 1bf047df31fbee7a3072e5bbc49c93a1a334df4d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 11:37:38 -0400 Subject: [PATCH 166/186] msequence: removing return bit from internal object --- src/sequence/src/msequence.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index d4d192edd..f1b943cc7 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -42,7 +42,6 @@ struct msequence_s { unsigned int genpoly; // generator polynomial, bit-reversed from above unsigned int n; // length of sequence, n = (2^m)-1 unsigned int v; // shift register - unsigned int b; // return bit }; // create a maximal-length sequence (m-sequence) object with @@ -88,8 +87,6 @@ msequence msequence_create(unsigned int _m, ms->n = (1<<_m)-1; // sequence length, (2^m)-1 ms->v = ms->a; // shift register - ms->b = 0; // return bit - return ms; } @@ -174,13 +171,12 @@ unsigned int msequence_advance(msequence _ms) { // compute return bit as binary dot product between the // internal shift register and the generator polynomial - _ms->b = liquid_bdotprod( _ms->v, _ms->genpoly ); + unsigned int b = liquid_bdotprod( _ms->v, _ms->genpoly ); _ms->v <<= 1; // shift internal register - _ms->v |= _ms->b; // push bit onto register + _ms->v |= b; // push bit onto register _ms->v &= _ms->n; // apply mask to register - - return _ms->b; // return result + return b; // return result } From 9d8788f94378c47ec349cbc31514cdcdcde981ba Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 11:38:53 -0400 Subject: [PATCH 167/186] msequence: changing variable name for clarity: v -> state --- src/sequence/src/msequence.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index f1b943cc7..7a7b57127 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -41,7 +41,7 @@ struct msequence_s { // derived values unsigned int genpoly; // generator polynomial, bit-reversed from above unsigned int n; // length of sequence, n = (2^m)-1 - unsigned int v; // shift register + unsigned int state; // shift register }; // create a maximal-length sequence (m-sequence) object with @@ -86,7 +86,7 @@ msequence msequence_create(unsigned int _m, } ms->n = (1<<_m)-1; // sequence length, (2^m)-1 - ms->v = ms->a; // shift register + ms->state = ms->a; // shift register state return ms; } @@ -162,7 +162,7 @@ int msequence_destroy(msequence _ms) int msequence_print(msequence _ms) { printf("\n", - _ms->m, _ms->n, _ms->g, _ms->v); + _ms->m, _ms->n, _ms->g, _ms->state); return LIQUID_OK; } @@ -171,12 +171,12 @@ unsigned int msequence_advance(msequence _ms) { // compute return bit as binary dot product between the // internal shift register and the generator polynomial - unsigned int b = liquid_bdotprod( _ms->v, _ms->genpoly ); + unsigned int b = liquid_bdotprod( _ms->state, _ms->genpoly ); - _ms->v <<= 1; // shift internal register - _ms->v |= b; // push bit onto register - _ms->v &= _ms->n; // apply mask to register - return b; // return result + _ms->state <<= 1; // shift internal register + _ms->state |= b; // push bit onto register + _ms->state &= _ms->n; // apply mask to register + return b; // return result } @@ -198,7 +198,7 @@ unsigned int msequence_generate_symbol(msequence _ms, // reset msequence shift register to original state, typically '1' int msequence_reset(msequence _ms) { - _ms->v = _ms->a; + _ms->state = _ms->a; return LIQUID_OK; } @@ -238,7 +238,7 @@ unsigned int msequence_get_genpoly(msequence _ms) // get the internal state of the sequence unsigned int msequence_get_state(msequence _ms) { - return _ms->v; + return _ms->state; } // set the internal state of the sequence @@ -248,7 +248,7 @@ int msequence_set_state(msequence _ms, // set internal state // NOTE: if state is set to zero, this will lock the sequence generator, // but let the user set this value if they wish - _ms->v = _a; + _ms->state = _a; return LIQUID_OK; } From 66350d2466eb5425005eb387ad451abea12fe4cd Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 14:10:05 -0400 Subject: [PATCH 168/186] msequence: moving example for auto-correlation --- .../{msequence_example.c => msequence_autocorr_example.c} | 7 ++----- makefile.in | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) rename examples/{msequence_example.c => msequence_autocorr_example.c} (97%) diff --git a/examples/msequence_example.c b/examples/msequence_autocorr_example.c similarity index 97% rename from examples/msequence_example.c rename to examples/msequence_autocorr_example.c index aa34c6ec8..0ea736dda 100644 --- a/examples/msequence_example.c +++ b/examples/msequence_autocorr_example.c @@ -1,15 +1,12 @@ -// -// msequence_example.c -// // This example demonstrates the auto-correlation properties of a // maximal-length sequence (m-sequence). An m-sequence of a // certain length is used to generate two binary sequences // (buffers) which are then cross-correlated. The resulting // correlation produces -1 for all values except at index zero, // where the sequences align. +// // SEE ALSO: bsequence_example.c // - #include #include #include @@ -17,7 +14,7 @@ #include "liquid.h" -#define OUTPUT_FILENAME "msequence_example.m" +#define OUTPUT_FILENAME "msequence_autocorr_example.m" int main(int argc, char*argv[]) { diff --git a/makefile.in b/makefile.in index d8d0ba37b..dca224e2c 100644 --- a/makefile.in +++ b/makefile.in @@ -1614,7 +1614,7 @@ example_programs := \ examples/modem_soft_example \ examples/modular_arithmetic_example \ examples/msequence_generator_example \ - examples/msequence_example \ + examples/msequence_autocorr_example \ examples/msourcecf_example \ examples/msresamp_crcf_example \ examples/msresamp_crcf_noise_example \ From 5ce0fcdeef00d3171b6e2a75fe4bd20c397e7768 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 14:15:49 -0400 Subject: [PATCH 169/186] msequence: adding example to print all states in cycle --- examples/msequence_example.c | 27 +++++++++++++++++++++++++++ makefile.in | 3 ++- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 examples/msequence_example.c diff --git a/examples/msequence_example.c b/examples/msequence_example.c new file mode 100644 index 000000000..a0b299c17 --- /dev/null +++ b/examples/msequence_example.c @@ -0,0 +1,27 @@ +// This example demonstrates the property of maximal-length sequence +// (m-sequence) linear feedback shift registers (LFSR) where the state +// cycles through all permutations of integers from 1 to 2^m-1. +#include +#include +#include +#include + +#include "liquid.h" + +int main(int argc, char*argv[]) +{ + // create and initialize m-sequence + msequence q = msequence_create_default(5); + msequence_print(q); + + // cycle through values and print state + unsigned int i; + for (i=0; i Date: Mon, 19 Jun 2023 14:25:17 -0400 Subject: [PATCH 170/186] msequence: reversing cycle for processing, removing old members --- examples/msequence_example.c | 8 ++++++ src/sequence/src/msequence.c | 33 ++++++------------------- src/sequence/tests/msequence_autotest.c | 2 +- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/examples/msequence_example.c b/examples/msequence_example.c index a0b299c17..2045d9eeb 100644 --- a/examples/msequence_example.c +++ b/examples/msequence_example.c @@ -17,10 +17,18 @@ int main(int argc, char*argv[]) // cycle through values and print state unsigned int i; for (i=0; i 0 && msequence_get_state(q)==1) { + printf("invalid state!\n"); + break; + } printf("%u\n",msequence_get_state(q)); msequence_advance(q); } + // ensure final state is 1 (circled all the way back around) + printf("final state (should be 1): %u\n", msequence_get_state(q)); + msequence_destroy(q); return 0; } diff --git a/src/sequence/src/msequence.c b/src/sequence/src/msequence.c index 7a7b57127..ecad555ff 100644 --- a/src/sequence/src/msequence.c +++ b/src/sequence/src/msequence.c @@ -34,14 +34,13 @@ // maximal-length sequence struct msequence_s { - unsigned int m; // length generator polynomial, shift register - unsigned int g; // generator polynomial, form: { x^m + ... + 1 } - unsigned int a; // initial shift register state, default: 1 + unsigned int m; // length generator polynomial, shift register + unsigned int g; // generator polynomial, form: { x^m + ... + 1 } + unsigned int a; // initial shift register state, default: 1 // derived values - unsigned int genpoly; // generator polynomial, bit-reversed from above - unsigned int n; // length of sequence, n = (2^m)-1 - unsigned int state; // shift register + unsigned int n; // length of sequence, n = (2^m)-1 + unsigned int state; // shift register }; // create a maximal-length sequence (m-sequence) object with @@ -65,25 +64,7 @@ msequence msequence_create(unsigned int _m, // set internal values ms->m = _m; // generator polynomial length ms->g = _g; // generator polynomial - //ms->g = _g >> 1; // generator polynomial (clip off most significant bit) - - // initialize state register, reversing order - // 0001 -> 1000 - unsigned int i; - ms->a = 0; - for (i=0; im; i++) { - ms->a <<= 1; - ms->a |= (_a & 0x01); - _a >>= 1; - } - - // initialize reverse-order generator polynomial, ignoring implied most-significant bit - ms->genpoly = 1; - for (i=0; im-1; i++) { - ms->genpoly <<= 1; - ms->genpoly |= (_g & 0x01); - _g >>= 1; - } + ms->a = _a; // generator polynomial ms->n = (1<<_m)-1; // sequence length, (2^m)-1 ms->state = ms->a; // shift register state @@ -171,7 +152,7 @@ unsigned int msequence_advance(msequence _ms) { // compute return bit as binary dot product between the // internal shift register and the generator polynomial - unsigned int b = liquid_bdotprod( _ms->state, _ms->genpoly ); + unsigned int b = liquid_bdotprod( _ms->state, _ms->g); _ms->state <<= 1; // shift internal register _ms->state |= b; // push bit onto register diff --git a/src/sequence/tests/msequence_autotest.c b/src/sequence/tests/msequence_autotest.c index dac971973..2bb3dd72a 100644 --- a/src/sequence/tests/msequence_autotest.c +++ b/src/sequence/tests/msequence_autotest.c @@ -155,7 +155,7 @@ void autotest_msequence_config() msequence q = msequence_create_genpoly(LIQUID_MSEQUENCE_GENPOLY_M11); CONTEND_EQUALITY(LIQUID_OK, msequence_print(q)) - CONTEND_EQUALITY(1<<10U, msequence_get_state(q)) + CONTEND_EQUALITY(1, msequence_get_state(q)) CONTEND_EQUALITY(LIQUID_OK, msequence_set_state(q, 0x8a)) CONTEND_EQUALITY(0x8a, msequence_get_state(q)) From 7871087084635385854aaee12c86f04ed9cb2d9f Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 14:50:19 -0400 Subject: [PATCH 171/186] build: removing tabs in global header --- include/liquid.h | 62 ++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 5aed449dc..35a74b062 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -9513,37 +9513,37 @@ int bsequence_create_ccodes(bsequence _a, bsequence _b); // M-Sequence -// default m-sequence generators: g (hex) m n -#define LIQUID_MSEQUENCE_GENPOLY_M2 0x00000003 // 2 3 -#define LIQUID_MSEQUENCE_GENPOLY_M3 0x00000006 // 3 7 -#define LIQUID_MSEQUENCE_GENPOLY_M4 0x0000000c // 4 15 -#define LIQUID_MSEQUENCE_GENPOLY_M5 0x00000014 // 5 31 -#define LIQUID_MSEQUENCE_GENPOLY_M6 0x00000030 // 6 63 -#define LIQUID_MSEQUENCE_GENPOLY_M7 0x00000060 // 7 127 -#define LIQUID_MSEQUENCE_GENPOLY_M8 0x000000b8 // 8 255 -#define LIQUID_MSEQUENCE_GENPOLY_M9 0x00000110 // 9 511 -#define LIQUID_MSEQUENCE_GENPOLY_M10 0x00000240 // 10 1,023 -#define LIQUID_MSEQUENCE_GENPOLY_M11 0x00000500 // 11 2,047 -#define LIQUID_MSEQUENCE_GENPOLY_M12 0x00000e08 // 12 4,095 -#define LIQUID_MSEQUENCE_GENPOLY_M13 0x00001c80 // 13 8,191 -#define LIQUID_MSEQUENCE_GENPOLY_M14 0x00003802 // 14 16,383 -#define LIQUID_MSEQUENCE_GENPOLY_M15 0x00006000 // 15 32,767 -#define LIQUID_MSEQUENCE_GENPOLY_M16 0x0000d008 // 16 65,535 -#define LIQUID_MSEQUENCE_GENPOLY_M17 0x00012000 // 17 131,071 -#define LIQUID_MSEQUENCE_GENPOLY_M18 0x00020400 // 18 262,143 -#define LIQUID_MSEQUENCE_GENPOLY_M19 0x00072000 // 19 524,287 -#define LIQUID_MSEQUENCE_GENPOLY_M20 0x00090000 // 20 1,048,575 -#define LIQUID_MSEQUENCE_GENPOLY_M21 0x00140000 // 21 2,097,151 -#define LIQUID_MSEQUENCE_GENPOLY_M22 0x00300000 // 22 4,194,303 -#define LIQUID_MSEQUENCE_GENPOLY_M23 0x00420000 // 23 8,388,607 -#define LIQUID_MSEQUENCE_GENPOLY_M24 0x00e10000 // 24 16,777,215 -#define LIQUID_MSEQUENCE_GENPOLY_M25 0x01000004 // 25 33,554,431 -#define LIQUID_MSEQUENCE_GENPOLY_M26 0x02000023 // 26 67,108,863 -#define LIQUID_MSEQUENCE_GENPOLY_M27 0x04000013 // 27 134,217,727 -#define LIQUID_MSEQUENCE_GENPOLY_M28 0x08000004 // 28 268,435,455 -#define LIQUID_MSEQUENCE_GENPOLY_M29 0x10000002 // 29 536,870,911 -#define LIQUID_MSEQUENCE_GENPOLY_M30 0x20000029 // 30 1,073,741,823 -#define LIQUID_MSEQUENCE_GENPOLY_M31 0x40000004 // 31 2,147,483,647 +// default m-sequence generators: g (hex) m n +#define LIQUID_MSEQUENCE_GENPOLY_M2 0x00000003 // 2 3 +#define LIQUID_MSEQUENCE_GENPOLY_M3 0x00000006 // 3 7 +#define LIQUID_MSEQUENCE_GENPOLY_M4 0x0000000c // 4 15 +#define LIQUID_MSEQUENCE_GENPOLY_M5 0x00000014 // 5 31 +#define LIQUID_MSEQUENCE_GENPOLY_M6 0x00000030 // 6 63 +#define LIQUID_MSEQUENCE_GENPOLY_M7 0x00000060 // 7 127 +#define LIQUID_MSEQUENCE_GENPOLY_M8 0x000000b8 // 8 255 +#define LIQUID_MSEQUENCE_GENPOLY_M9 0x00000110 // 9 511 +#define LIQUID_MSEQUENCE_GENPOLY_M10 0x00000240 // 10 1,023 +#define LIQUID_MSEQUENCE_GENPOLY_M11 0x00000500 // 11 2,047 +#define LIQUID_MSEQUENCE_GENPOLY_M12 0x00000e08 // 12 4,095 +#define LIQUID_MSEQUENCE_GENPOLY_M13 0x00001c80 // 13 8,191 +#define LIQUID_MSEQUENCE_GENPOLY_M14 0x00003802 // 14 16,383 +#define LIQUID_MSEQUENCE_GENPOLY_M15 0x00006000 // 15 32,767 +#define LIQUID_MSEQUENCE_GENPOLY_M16 0x0000d008 // 16 65,535 +#define LIQUID_MSEQUENCE_GENPOLY_M17 0x00012000 // 17 131,071 +#define LIQUID_MSEQUENCE_GENPOLY_M18 0x00020400 // 18 262,143 +#define LIQUID_MSEQUENCE_GENPOLY_M19 0x00072000 // 19 524,287 +#define LIQUID_MSEQUENCE_GENPOLY_M20 0x00090000 // 20 1,048,575 +#define LIQUID_MSEQUENCE_GENPOLY_M21 0x00140000 // 21 2,097,151 +#define LIQUID_MSEQUENCE_GENPOLY_M22 0x00300000 // 22 4,194,303 +#define LIQUID_MSEQUENCE_GENPOLY_M23 0x00420000 // 23 8,388,607 +#define LIQUID_MSEQUENCE_GENPOLY_M24 0x00e10000 // 24 16,777,215 +#define LIQUID_MSEQUENCE_GENPOLY_M25 0x01000004 // 25 33,554,431 +#define LIQUID_MSEQUENCE_GENPOLY_M26 0x02000023 // 26 67,108,863 +#define LIQUID_MSEQUENCE_GENPOLY_M27 0x04000013 // 27 134,217,727 +#define LIQUID_MSEQUENCE_GENPOLY_M28 0x08000004 // 28 268,435,455 +#define LIQUID_MSEQUENCE_GENPOLY_M29 0x10000002 // 29 536,870,911 +#define LIQUID_MSEQUENCE_GENPOLY_M30 0x20000029 // 30 1,073,741,823 +#define LIQUID_MSEQUENCE_GENPOLY_M31 0x40000004 // 31 2,147,483,647 typedef struct msequence_s * msequence; From 698b8f7b1164dcd960d570f08f588ea91e0dbcf2 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 16:09:55 -0400 Subject: [PATCH 172/186] build: updating readme to include notes on linking to external libs --- README.md | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7fa96418c..51c2b1123 100644 --- a/README.md +++ b/README.md @@ -233,11 +233,36 @@ lib_deps = https://github.com/jgaeddert/liquid-dsp.git ### License ### liquid projects are released under the X11/MIT license. +By default, this project will try to link to [FFTW](http://www.fftw.org) if it +is available on your build platform. +Because FFTW starting with version 1.3 is +[licensed](http://www.fftw.org/faq/section1.html) +under the [GNU General Public License v2](http://www.fftw.org/doc/License-and-Copyright.html) +this unfortunately means that (and I'm clearly not a lawyer, here) +you cannot distribute `liquid-dsp` without also distributing the source code +if you link to FFTW. +This is a similar situation with the classic +[libfec](https://github.com/quiet/libfec) +which uses the +[GNU Lesser GPL](https://www.gnu.org/licenses/licenses.html#LGPL). +Finally, `liquid-dsp` makes extensive use of GNU +[autoconf](https://www.gnu.org/software/autoconf/), +[automake](https://www.gnu.org/software/automake/), +and related tools. +These are fantastic libraires with amazing functionality and their authors +should be lauded for their efforts. +In a similar vain, much the software I write for a living I give away for +free; +however I believe in more permissive licenses to allow individuals the +flexibility to use software with more flexibility. +If these restrictions are not acceptible, `liquid-dsp` can be compiled and run +without use of these external libraries, albeit a bit slower and with limited +functionality. + Short version: this code is copyrighted to me (Joseph D. Gaeddert), I give you full permission to do whatever you want with it except remove my name from the credits. -Seriously, go nuts. -See the LICENSE file or -[https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT) -for specific terms. +Seriously, go nuts! but take caution when linking to other libraries with +different licenses. +See the [license](https://opensource.org/licenses/MIT) for specific terms. From 7ed1c3041cb54726150d6ef7ee887e206f388543 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 17:46:04 -0400 Subject: [PATCH 173/186] qdsync: cleaning duplicate interface --- include/liquid.h | 54 ----------------------------------- src/framing/src/qdsync_cccf.c | 43 ---------------------------- 2 files changed, 97 deletions(-) delete mode 100644 src/framing/src/qdsync_cccf.c diff --git a/include/liquid.h b/include/liquid.h index 92e34bdbd..c81b39389 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6284,60 +6284,6 @@ typedef int (*qdsync_callback)(liquid_float_complex * _buf, // - sample count since object was created // - sample count since beginning of frame -// create detector with generic sequence -// _s : sample sequence -// _s_len : length of sample sequence -qdsync_cccf qdsync_cccf_create_linear(liquid_float_complex * _s, - unsigned int _s_len, - int _ftype, - unsigned int _k, - unsigned int _m, - float _beta, - qdsync_callback _callback, - void * _context); - -// Copy object recursively including all internal objects and state -qdsync_cccf qdsync_cccf_copy(qdsync_cccf _q); - -int qdsync_cccf_destroy(qdsync_cccf _q); -int qdsync_cccf_reset (qdsync_cccf _q); -int qdsync_cccf_print (qdsync_cccf _q); - -// get detection threshold -float qdsync_cccf_get_threshold(qdsync_cccf _q); - -// set detection threshold -int qdsync_cccf_set_threshold(qdsync_cccf _q, float _threshold); - -// set carrier offset search range -int qdsync_cccf_set_range(qdsync_cccf _q, - float _dphi_max); - -// set callback method -int qdsync_cccf_set_callback(qdsync_cccf _q, qdsync_callback _callback); - -// set context value -int qdsync_cccf_set_context (qdsync_cccf _q, void * _context); - -// Set callback buffer size (the number of symbol provided to the callback -// whenever it is invoked). -int qdsync_cccf_set_buf_len (qdsync_cccf _q, unsigned int _buf_len); - -// execute block of samples -int qdsync_cccf_execute(qdsync_cccf _q, - liquid_float_complex * _buf, - unsigned int _buf_len); - -// is synchronizer actively running? -int qdsync_cccf_is_open(qdsync_cccf _q); - -// get detection metrics and offsets -float qdsync_cccf_get_rxy (qdsync_cccf _q); // correlator output -float qdsync_cccf_get_tau (qdsync_cccf _q); // fractional timing offset estimate -float qdsync_cccf_get_gamma(qdsync_cccf _q); // channel gain -float qdsync_cccf_get_dphi (qdsync_cccf _q); // carrier frequency offset estimate -float qdsync_cccf_get_phi (qdsync_cccf _q); // carrier phase offset estimate - // // qdsync // diff --git a/src/framing/src/qdsync_cccf.c b/src/framing/src/qdsync_cccf.c deleted file mode 100644 index 28104f7a9..000000000 --- a/src/framing/src/qdsync_cccf.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2007 - 2023 Joseph Gaeddert - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// -// API: floating-point -// - -#include "liquid.internal.h" - -// naming extensions (useful for print statements) -#define EXTENSION "cccf" - -#define TO float complex // output type -#define TC float complex // coefficients type -#define TI float complex // input type -#define T float // primitive type - -// object references -#define QDSYNC(name) LIQUID_CONCAT(qdsync_cccf,name) -#define QDETECTOR(name) LIQUID_CONCAT(qdetector_cccf,name) - -// prototypes -#include "qdsync.proto.c" - From b6707a8dadb0491111ec1665711b3c97aa2ec197 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 17:50:58 -0400 Subject: [PATCH 174/186] qdsync: cleaning up method descriptions --- include/liquid.h | 28 +++++++++++++++++++++------- src/framing/src/qdsync.proto.c | 13 +++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index c81b39389..b1ab242ae 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6323,14 +6323,19 @@ QDSYNC() QDSYNC(_create_linear)(TI * _s, \ /* Copy object recursively including all internal objects and state */ \ QDSYNC() QDSYNC(_copy)(QDSYNC() _q); \ \ +/* Destroy synchronizer object and free all internal memory */ \ int QDSYNC(_destroy)(QDSYNC() _q); \ -int QDSYNC(_reset) (QDSYNC() _q); \ -int QDSYNC(_print) (QDSYNC() _q); \ \ -/* get detection threshold */ \ +/* Reset synchronizer object's internal buffer */ \ +int QDSYNC(_reset)(QDSYNC() _q); \ + \ +/* Print synchronizer object information to stdout */ \ +int QDSYNC(_print)(QDSYNC() _q); \ + \ +/* Get detection threshold */ \ float QDSYNC(_get_threshold)(QDSYNC() _q); \ \ -/* set detection threshold */ \ +/* Set detection threshold */ \ int QDSYNC(_set_threshold)(QDSYNC() _q, \ float _threshold); \ \ @@ -6338,16 +6343,16 @@ int QDSYNC(_set_threshold)(QDSYNC() _q, \ int QDSYNC(_set_range)(QDSYNC() _q, \ float _dphi_max); \ \ -/* set callback method */ \ +/* Set callback method */ \ int QDSYNC(_set_callback)(QDSYNC() _q, \ QDSYNC(_callback) _callback); \ \ -/* set context value */ \ +/* Set context value */ \ int QDSYNC(_set_context)(QDSYNC() _q, void * _context); \ \ /* Set callback buffer size (the number of symbol provided to the */ \ /* callback whenever it is invoked). */ \ -int QDSYNC(_set_buf_len )(QDSYNC() _q, unsigned int _buf_len); \ +int QDSYNC(_set_buf_len)(QDSYNC() _q, unsigned int _buf_len); \ \ /* execute block of samples */ \ int QDSYNC(_execute)(QDSYNC() _q, \ @@ -6357,10 +6362,19 @@ int QDSYNC(_execute)(QDSYNC() _q, \ /* Return flag indicating if synchronizer actively running. */ \ int QDSYNC(_is_open)(QDSYNC() _q); \ \ +/* Get synchronizer correlator output after frame was detected */ \ float QDSYNC(_get_rxy) (QDSYNC() _q); \ + \ +/* Get synchronizer fractional timing offset after frame was detected */ \ float QDSYNC(_get_tau) (QDSYNC() _q); \ + \ +/* Get synchronizer channel gain after frame was detected */ \ float QDSYNC(_get_gamma)(QDSYNC() _q); \ + \ +/* Get synchronizer frequency offset estimate after frame was detected */ \ float QDSYNC(_get_dphi) (QDSYNC() _q); \ + \ +/* Get synchronizer phase offset estimate after frame was detected */ \ float QDSYNC(_get_phi) (QDSYNC() _q); \ LIQUID_QDSYNC_DEFINE_API(LIQUID_QDSYNC_MANGLE_CCCF, diff --git a/src/framing/src/qdsync.proto.c b/src/framing/src/qdsync.proto.c index 12bbee5c0..06f8fc0a0 100644 --- a/src/framing/src/qdsync.proto.c +++ b/src/framing/src/qdsync.proto.c @@ -164,12 +164,6 @@ int QDSYNC(_destroy)(QDSYNC() _q) return LIQUID_OK; } -int QDSYNC(_print)(QDSYNC() _q) -{ - printf("\n"); - return LIQUID_OK; -} - int QDSYNC(_reset)(QDSYNC() _q) { QDETECTOR(_reset)(_q->detector); @@ -180,6 +174,12 @@ int QDSYNC(_reset)(QDSYNC() _q) return LIQUID_OK; } +int QDSYNC(_print)(QDSYNC() _q) +{ + printf("\n"); + return LIQUID_OK; +} + // get detection threshold float QDSYNC(_get_threshold)(QDSYNC() _q) { @@ -254,6 +254,7 @@ int QDSYNC(_set_buf_len)(QDSYNC() _q, unsigned int _buf_len) return LIQUID_OK; } +// execute synchronizer on a block of samples int QDSYNC(_execute)(QDSYNC() _q, TI * _buf, unsigned int _buf_len) From 7da042924f93e11a08253075f9b83522cda42f9b Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 15:42:38 -0400 Subject: [PATCH 175/186] build: adding release notes to HISTORY file --- HISTORY | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/HISTORY b/HISTORY index d1d7b6993..ded8d297a 100644 --- a/HISTORY +++ b/HISTORY @@ -1,8 +1,49 @@ ## Latest improvements ## +## Improvements for v1.6.0 ## + +Version 1.6.0 includes a new qdsync object to greatly simplify the frame +synchronization process in liquid, allowing for both detection and channel +impairment correction with a simplified interface. Additionally, code +coverage has been increased to 85% across the entire project, with numerous +bug fixes, stability improvements, and massive testing enhancements. From +an "architectural" standpoint, objects have been migrated to use standard +methods for consistency. + + * build + - increased code coverage to 85% globally across entire project. This + is the single largest effort included in this version and touches + most modules in some way, most particularly the framing objects + - cleaning build to remove compiler warnings (e.g. unused variables) + - stripped version number off archive + * dotprod + - added support for AVX512-F (thanks, @vankxr!) + * framing + - added numerous tests to increase coverage to 84% + - framesync64: using new qdsync object for simplified operation + - qdsync: new frame detector and synchronizer to much more easily + support frame processing. The object not only detects the frame, but + also provides an initial carrier frequency, phase, and timign offset, + and also corrects for these impairments, passing the results to the + user in a clean callback function. + * modem + - cpfskmod: increasing phase stability for long runs + * multichannel + - added numerous tests to increase coverage to 88% + * optim + - added numerous tests to increase coverage to 92% + * sequence + - msequence: extended support for state variables up to m=31, reversed + order for generator polynomial and internal state definition to be + more consistent with literature and readily-available genpolys + ## Improvements for v1.5.0 ## +This release includes substantially improved testing coverage, deep copy() +methods for nearly all objects, improved speed, and resolves a number of +issues and pull requests. + * build - added support for PlatformIO (https://platformio.org) for embeedded development (thanks, @jcw!) From b11b40c2653386894b5cf77d230f7916fbeea77d Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 15:42:51 -0400 Subject: [PATCH 176/186] build: bumping version to 1.6.0 --- configure.ac | 4 ++-- include/liquid.h | 4 ++-- library.json | 2 +- scripts/config.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 34ab3dfc3..72518d1ce 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# Copyright (c) 2007 - 2022 Joseph Gaeddert +# Copyright (c) 2007 - 2023 Joseph Gaeddert # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ # Process with autoconf to generate configure script # -AC_INIT([liquid-dsp],[1.5],[joseph@liquidsdr.org]) +AC_INIT([liquid-dsp],[1.6],[joseph@liquidsdr.org]) AC_CONFIG_SRCDIR([src/libliquid.c]) AC_CONFIG_MACRO_DIR([scripts]) diff --git a/include/liquid.h b/include/liquid.h index b1ab242ae..f6758a9e8 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -51,8 +51,8 @@ extern "C" { // LIQUID_VERSION = "X.Y.Z" // LIQUID_VERSION_NUMBER = (X*1000000 + Y*1000 + Z) // -#define LIQUID_VERSION "1.5.0" -#define LIQUID_VERSION_NUMBER 1005000 +#define LIQUID_VERSION "1.6.0" +#define LIQUID_VERSION_NUMBER 1006000 // // Run-time library version numbers diff --git a/library.json b/library.json index 5a05e3128..d96b4a3f5 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "liquid-dsp", - "version": "1.5.0", + "version": "1.6.0", "description": "Software-defined radio digital signal processing library", "homepage": "https://liquidsdr.org", "keywords": diff --git a/scripts/config.h b/scripts/config.h index 3b81cd5e4..c7914ac07 100644 --- a/scripts/config.h +++ b/scripts/config.h @@ -169,7 +169,7 @@ #define PACKAGE_NAME "liquid-dsp" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "liquid-dsp 1.5.0" +#define PACKAGE_STRING "liquid-dsp 1.6.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "liquid-dsp" @@ -178,7 +178,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.5.0" +#define PACKAGE_VERSION "1.6.0" /* The size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 From 6e29a7cc6e503320bc3a3066eba96d169cb36eb3 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 17:57:10 -0400 Subject: [PATCH 177/186] build: bumping config.sub, config.guess --- scripts/config.guess | 6 +++--- scripts/config.sub | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/config.guess b/scripts/config.guess index 980b02083..69188da73 100644 --- a/scripts/config.guess +++ b/scripts/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2022 Free Software Foundation, Inc. +# Copyright 1992-2023 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2022-09-17' +timestamp='2023-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -60,7 +60,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2022 Free Software Foundation, Inc. +Copyright 1992-2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." diff --git a/scripts/config.sub b/scripts/config.sub index baf1512b3..de4259e40 100644 --- a/scripts/config.sub +++ b/scripts/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2022 Free Software Foundation, Inc. +# Copyright 1992-2023 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2022-09-17' +timestamp='2023-01-21' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -76,7 +76,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2022 Free Software Foundation, Inc. +Copyright 1992-2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -1075,7 +1075,7 @@ case $cpu-$vendor in pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) cpu=i586 ;; - pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*) + pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*) cpu=i686 ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) From 8bf87b6fe325d98c250d6911fa50518d14175d86 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Mon, 19 Jun 2023 18:04:12 -0400 Subject: [PATCH 178/186] fec/autotest: adding 'default' switch case to clear gcc warnings --- src/fec/tests/fec_autotest.c | 1 + src/fec/tests/fec_copy_autotest.c | 1 + src/fec/tests/fec_soft_autotest.c | 1 + 3 files changed, 3 insertions(+) diff --git a/src/fec/tests/fec_autotest.c b/src/fec/tests/fec_autotest.c index d03712ce1..e65fa110a 100644 --- a/src/fec/tests/fec_autotest.c +++ b/src/fec/tests/fec_autotest.c @@ -49,6 +49,7 @@ void fec_test_codec(fec_scheme _fs, unsigned int _n, void * _opts) case LIQUID_FEC_RS_M8: AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; + default:; } #endif diff --git a/src/fec/tests/fec_copy_autotest.c b/src/fec/tests/fec_copy_autotest.c index bcb2d1ca3..a47addb78 100644 --- a/src/fec/tests/fec_copy_autotest.c +++ b/src/fec/tests/fec_copy_autotest.c @@ -49,6 +49,7 @@ void fec_test_copy(fec_scheme _fs) case LIQUID_FEC_RS_M8: AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; + default:; } #endif unsigned int n_dec = 64; diff --git a/src/fec/tests/fec_soft_autotest.c b/src/fec/tests/fec_soft_autotest.c index 0ef350493..c0d2ef247 100644 --- a/src/fec/tests/fec_soft_autotest.c +++ b/src/fec/tests/fec_soft_autotest.c @@ -52,6 +52,7 @@ void fec_test_soft_codec(fec_scheme _fs, case LIQUID_FEC_RS_M8: AUTOTEST_WARN("convolutional, Reed-Solomon codes unavailable (install libfec)"); return; + default:; } #endif From 89bffef859eec2d833eaa8052dbdbdd2c1cb4368 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Tue, 20 Jun 2023 18:10:34 -0400 Subject: [PATCH 179/186] qpacketmodem: adding more description to method definitions --- include/liquid.h | 92 ++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index f6758a9e8..58c9bf4f4 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -5218,11 +5218,11 @@ typedef int (*framesync_callback)(unsigned char * _header, typedef void (*framesync_csma_callback)(void * _userdata); -#define LIQUID_QPACKETMODEM_MANGLE_RRRF(name) LIQUID_CONCAT(qpacketmodem,name) +#define LIQUID_QPACKETMODEM_MANGLE_FLOAT(name) LIQUID_CONCAT(qpacketmodem,name) // Macro: // QPACKETMODEM : name-mangling macro -// T : data type +// T : primitive data type #define LIQUID_QPACKETMODEM_DEFINE_API(QPACKETMODEM,T) \ \ /* Packet encoder/decoder */ \ @@ -5231,38 +5231,62 @@ typedef struct QPACKETMODEM(_s) * QPACKETMODEM(); \ /* Create packet encoder */ \ QPACKETMODEM() QPACKETMODEM(_create)(); \ \ +/* Copy object including all internal objects and state */ \ QPACKETMODEM() QPACKETMODEM(_copy) (QPACKETMODEM() _q); \ -int QPACKETMODEM(_destroy)(QPACKETMODEM() _q); \ -int QPACKETMODEM(_reset) (QPACKETMODEM() _q); \ -int QPACKETMODEM(_print) (QPACKETMODEM() _q); \ \ +/* Destroy object, freeing all allocated memory */ \ +int QPACKETMODEM(_destroy)(QPACKETMODEM() _q); \ + \ +/* Print modem status to stdout */ \ +int QPACKETMODEM(_print)(QPACKETMODEM() _q); \ + \ +/* Reset internal state of modem object */ \ +int QPACKETMODEM(_reset)(QPACKETMODEM() _q); \ + \ +/* Configure object with particular parameters */ \ +/* _q : qpacketmodem object */ \ +/* _payload_len : length of payload message [bytes] */ \ +/* _check : data integrity check, e.g LIQUID_CRC_32 */ \ +/* _fec0 : forward error-correction scheme (inner), e.g. */ \ +/* LIQUID_FEC_GOLAY2412 */ \ +/* _fec1 : forward error-correction scheme (outer) */ \ +/* _ms : modulation scheme, e.g. LIQUID_MODEM_QPSK */ \ int QPACKETMODEM(_configure)(QPACKETMODEM() _q, \ - unsigned int _payload_len, \ - crc_scheme _check, \ - fec_scheme _fec0, \ - fec_scheme _fec1, \ - int _ms); \ + unsigned int _payload_len, \ + crc_scheme _check, \ + fec_scheme _fec0, \ + fec_scheme _fec1, \ + int _ms); \ \ -/* get length of encoded frame in symbols */ \ +/* Get length of encoded frame in symbols */ \ unsigned int QPACKETMODEM(_get_frame_len)(QPACKETMODEM() _q); \ \ -/* get unencoded/decoded payload length (bytes) */ \ +/* Get unencoded/decoded payload length (bytes) */ \ unsigned int QPACKETMODEM(_get_payload_len)(QPACKETMODEM() _q); \ \ -/* regular access methods */ \ -unsigned int QPACKETMODEM(_get_crc )(QPACKETMODEM() _q); \ -unsigned int QPACKETMODEM(_get_fec0 )(QPACKETMODEM() _q); \ -unsigned int QPACKETMODEM(_get_fec1 )(QPACKETMODEM() _q); \ +/* Get data integrity check, e.g. LIQUID_CRC_32 */ \ +unsigned int QPACKETMODEM(_get_crc)(QPACKETMODEM() _q); \ + \ +/* Get inner forward error-correction scheme, e.g. LIQUID_GOLAY_2412 */ \ +unsigned int QPACKETMODEM(_get_fec0)(QPACKETMODEM() _q); \ + \ +/* Get outer forward error-correction scheme, e.g. LIQUID_GOLAY_2412 */ \ +unsigned int QPACKETMODEM(_get_fec1)(QPACKETMODEM() _q); \ + \ +/* Get modulation scheme, e.g. LIQUID_MODEM_QPSK */ \ unsigned int QPACKETMODEM(_get_modscheme)(QPACKETMODEM() _q); \ \ +/* Get demodulator phase error (instantaneous) [radians] */ \ float QPACKETMODEM(_get_demodulator_phase_error)(QPACKETMODEM() _q); \ + \ +/* Get demodulator error-vector magnitude after frame was received */ \ float QPACKETMODEM(_get_demodulator_evm)(QPACKETMODEM() _q); \ \ /* encode packet into un-modulated frame symbol indices */ \ /* _q : qpacketmodem object */ \ /* _payload : unencoded payload bytes */ \ /* _syms : encoded but un-modulated payload symbol indices */ \ -int QPACKETMODEM(_encode_syms)(QPACKETMODEM() _q, \ +int QPACKETMODEM(_encode_syms)(QPACKETMODEM() _q, \ const unsigned char * _payload, \ unsigned char * _syms); \ \ @@ -5280,43 +5304,43 @@ int QPACKETMODEM(_decode_syms)(QPACKETMODEM() _q, \ /* _q : qpacketmodem object */ \ /* _bits : received soft-decision bits, [size: bps*frame_len x 1] */ \ /* _payload : recovered decoded payload bytes */ \ -int QPACKETMODEM(_decode_bits)(QPACKETMODEM() _q, \ - unsigned char * _bits, \ - unsigned char * _payload); \ +int QPACKETMODEM(_decode_bits)(QPACKETMODEM() _q, \ + unsigned char * _bits, \ + unsigned char * _payload); \ \ /* encode and modulate packet into modulated frame samples */ \ /* _q : qpacketmodem object */ \ /* _payload : unencoded payload bytes */ \ /* _frame : encoded/modulated payload symbols */ \ -int QPACKETMODEM(_encode)(QPACKETMODEM() _q, \ - const unsigned char * _payload, \ - liquid_float_complex * _frame); \ +int QPACKETMODEM(_encode)(QPACKETMODEM() _q, \ + const unsigned char * _payload, \ + liquid_float_complex * _frame); \ \ /* decode packet from modulated frame samples, returning flag if CRC */ \ /* passed using hard-decision decoding */ \ /* _q : qpacketmodem object */ \ /* _frame : encoded/modulated payload symbols */ \ /* _payload : recovered decoded payload bytes */ \ -int QPACKETMODEM(_decode)(QPACKETMODEM() _q, \ - liquid_float_complex * _frame, \ - unsigned char * _payload); \ +int QPACKETMODEM(_decode)(QPACKETMODEM() _q, \ + liquid_float_complex * _frame, \ + unsigned char * _payload); \ \ /* decode packet from modulated frame samples, returning flag if CRC */ \ /* passed using soft-decision decoding */ \ /* _q : qpacketmodem object */ \ /* _frame : encoded/modulated payload symbols */ \ /* _payload : recovered decoded payload bytes */ \ -int QPACKETMODEM(_decode_soft)(QPACKETMODEM() _q, \ - liquid_float_complex * _frame, \ - unsigned char * _payload); \ +int QPACKETMODEM(_decode_soft)(QPACKETMODEM() _q, \ + liquid_float_complex * _frame, \ + unsigned char * _payload); \ \ -int QPACKETMODEM(_decode_soft_sym)(QPACKETMODEM() _q, \ - liquid_float_complex _symbol); \ +int QPACKETMODEM(_decode_soft_sym)(QPACKETMODEM() _q, \ + liquid_float_complex _symbol); \ \ -int QPACKETMODEM(_decode_soft_payload)(QPACKETMODEM() _q, \ - unsigned char * _payload); \ +int QPACKETMODEM(_decode_soft_payload)(QPACKETMODEM() _q, \ + unsigned char * _payload); \ -LIQUID_QPACKETMODEM_DEFINE_API(LIQUID_QPACKETMODEM_MANGLE_RRRF, float) +LIQUID_QPACKETMODEM_DEFINE_API(LIQUID_QPACKETMODEM_MANGLE_FLOAT, float) // // pilot generator/synchronizer for packet burst recovery From 29dc259fc0a10e68ae32d05f4d3d7fe2e3647b70 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Tue, 20 Jun 2023 18:20:58 -0400 Subject: [PATCH 180/186] qpacketmodem: defining output type in macro, source --- include/liquid.h | 26 +++++++++++++------------- src/framing/src/qpacketmodem.proto.c | 17 +++++++++-------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 58c9bf4f4..15b136667 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -5223,7 +5223,7 @@ typedef void (*framesync_csma_callback)(void * _userdata); // Macro: // QPACKETMODEM : name-mangling macro // T : primitive data type -#define LIQUID_QPACKETMODEM_DEFINE_API(QPACKETMODEM,T) \ +#define LIQUID_QPACKETMODEM_DEFINE_API(QPACKETMODEM,T,TC) \ \ /* Packet encoder/decoder */ \ typedef struct QPACKETMODEM(_s) * QPACKETMODEM(); \ @@ -5312,35 +5312,35 @@ int QPACKETMODEM(_decode_bits)(QPACKETMODEM() _q, \ /* _q : qpacketmodem object */ \ /* _payload : unencoded payload bytes */ \ /* _frame : encoded/modulated payload symbols */ \ -int QPACKETMODEM(_encode)(QPACKETMODEM() _q, \ - const unsigned char * _payload, \ - liquid_float_complex * _frame); \ +int QPACKETMODEM(_encode)(QPACKETMODEM() _q, \ + const unsigned char * _payload, \ + TC * _frame); \ \ /* decode packet from modulated frame samples, returning flag if CRC */ \ /* passed using hard-decision decoding */ \ /* _q : qpacketmodem object */ \ /* _frame : encoded/modulated payload symbols */ \ /* _payload : recovered decoded payload bytes */ \ -int QPACKETMODEM(_decode)(QPACKETMODEM() _q, \ - liquid_float_complex * _frame, \ - unsigned char * _payload); \ +int QPACKETMODEM(_decode)(QPACKETMODEM() _q, \ + TC * _frame, \ + unsigned char * _payload); \ \ /* decode packet from modulated frame samples, returning flag if CRC */ \ /* passed using soft-decision decoding */ \ /* _q : qpacketmodem object */ \ /* _frame : encoded/modulated payload symbols */ \ /* _payload : recovered decoded payload bytes */ \ -int QPACKETMODEM(_decode_soft)(QPACKETMODEM() _q, \ - liquid_float_complex * _frame, \ - unsigned char * _payload); \ +int QPACKETMODEM(_decode_soft)(QPACKETMODEM() _q, \ + TC * _frame, \ + unsigned char * _payload); \ \ -int QPACKETMODEM(_decode_soft_sym)(QPACKETMODEM() _q, \ - liquid_float_complex _symbol); \ +int QPACKETMODEM(_decode_soft_sym)(QPACKETMODEM() _q, \ + TC _symbol); \ \ int QPACKETMODEM(_decode_soft_payload)(QPACKETMODEM() _q, \ unsigned char * _payload); \ -LIQUID_QPACKETMODEM_DEFINE_API(LIQUID_QPACKETMODEM_MANGLE_FLOAT, float) +LIQUID_QPACKETMODEM_DEFINE_API(LIQUID_QPACKETMODEM_MANGLE_FLOAT,float,liquid_float_complex) // // pilot generator/synchronizer for packet burst recovery diff --git a/src/framing/src/qpacketmodem.proto.c b/src/framing/src/qpacketmodem.proto.c index 96ee3c0a9..d883e8023 100644 --- a/src/framing/src/qpacketmodem.proto.c +++ b/src/framing/src/qpacketmodem.proto.c @@ -288,9 +288,9 @@ int QPACKETMODEM(_decode_bits)(qpacketmodem _q, // _q : qpacketmodem object // _payload : unencoded payload bytes // _frame : encoded/modulated payload symbols -int QPACKETMODEM(_encode)(qpacketmodem _q, +int QPACKETMODEM(_encode)(qpacketmodem _q, const unsigned char * _payload, - float complex * _frame) + TO * _frame) { // encode payload symbols into internal buffer QPACKETMODEM(_encode_syms)(_q, _payload, _q->payload_mod); @@ -307,8 +307,8 @@ int QPACKETMODEM(_encode)(qpacketmodem _q, // _frame : encoded/modulated payload symbols // _payload : recovered decoded payload bytes int QPACKETMODEM(_decode)(qpacketmodem _q, - float complex * _frame, - unsigned char * _payload) + TO * _frame, + unsigned char * _payload) { unsigned int i; @@ -344,8 +344,8 @@ int QPACKETMODEM(_decode)(qpacketmodem _q, // _frame : encoded/modulated payload symbols // _payload : recovered decoded payload bytes int QPACKETMODEM(_decode_soft)(qpacketmodem _q, - float complex * _frame, - unsigned char * _payload) + TO * _frame, + unsigned char * _payload) { unsigned int i; @@ -376,8 +376,8 @@ int QPACKETMODEM(_decode_soft)(qpacketmodem _q, // decode symbol from modulated frame samples, returning flag if all symbols received // _q : qpacketmodem object // _frame : encoded/modulated symbol -int QPACKETMODEM(_decode_soft_sym)(qpacketmodem _q, - float complex _symbol) +int QPACKETMODEM(_decode_soft_sym)(qpacketmodem _q, + TO _symbol) { unsigned int sym; MODEM(_demodulate_soft)(_q->mod_payload, _symbol, &sym, _q->payload_enc + _q->n); @@ -392,3 +392,4 @@ int QPACKETMODEM(_decode_soft_payload)(qpacketmodem _q, _q->n = 0; return packetizer_decode_soft(_q->p, _q->payload_enc, _payload); } + From 5cfff748678e3c9bf4cf5151e918318479345504 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Wed, 21 Jun 2023 19:16:41 -0400 Subject: [PATCH 181/186] qpacketmodem: adding even more description to method definitions --- README.md | 2 +- include/liquid.h | 26 +++++++++++++++++++------- src/framing/src/qpacketmodem.proto.c | 9 +++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 51c2b1123..947501531 100644 --- a/README.md +++ b/README.md @@ -254,7 +254,7 @@ should be lauded for their efforts. In a similar vain, much the software I write for a living I give away for free; however I believe in more permissive licenses to allow individuals the -flexibility to use software with more flexibility. +flexibility to use software with fewer limitations. If these restrictions are not acceptible, `liquid-dsp` can be compiled and run without use of these external libraries, albeit a bit slower and with limited functionality. diff --git a/include/liquid.h b/include/liquid.h index 15b136667..15db1a97a 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -5296,7 +5296,8 @@ int QPACKETMODEM(_encode_syms)(QPACKETMODEM() _q, \ /* _syms : received hard-decision symbol indices, */ \ /* [size: frame_len x 1] */ \ /* _payload : recovered decoded payload bytes */ \ -int QPACKETMODEM(_decode_syms)(QPACKETMODEM() _q, \ +/* _return : flag indicating if data integrity check passed */ \ +int QPACKETMODEM(_decode_syms)(QPACKETMODEM() _q, \ unsigned char * _syms, \ unsigned char * _payload); \ \ @@ -5318,25 +5319,36 @@ int QPACKETMODEM(_encode)(QPACKETMODEM() _q, \ \ /* decode packet from modulated frame samples, returning flag if CRC */ \ /* passed using hard-decision decoding */ \ -/* _q : qpacketmodem object */ \ -/* _frame : encoded/modulated payload symbols */ \ -/* _payload : recovered decoded payload bytes */ \ +/* _q : qpacketmodem object */ \ +/* _frame : encoded/modulated payload symbols */ \ +/* _payload : recovered decoded payload bytes */ \ +/* _return : flag indicating if data integrity check passed */ \ int QPACKETMODEM(_decode)(QPACKETMODEM() _q, \ TC * _frame, \ unsigned char * _payload); \ \ /* decode packet from modulated frame samples, returning flag if CRC */ \ /* passed using soft-decision decoding */ \ -/* _q : qpacketmodem object */ \ -/* _frame : encoded/modulated payload symbols */ \ -/* _payload : recovered decoded payload bytes */ \ +/* _q : qpacketmodem object */ \ +/* _frame : encoded/modulated payload symbols */ \ +/* _payload : recovered decoded payload bytes */ \ +/* _return : flag indicating if data integrity check passed */ \ int QPACKETMODEM(_decode_soft)(QPACKETMODEM() _q, \ TC * _frame, \ unsigned char * _payload); \ \ +/* decode symbol from modulated frame samples, returning flag if all */ \ +/* symbols received */ \ +/* _q : qpacketmodem object */ \ +/* _symbol : input received symbol before demodulation */ \ +/* _return : flag indicating if all symbols were received */ \ int QPACKETMODEM(_decode_soft_sym)(QPACKETMODEM() _q, \ TC _symbol); \ \ +/* Decode entire packet, assuming that entire frame has been received. */ \ +/* _q : qpacketmodem object */ \ +/* _payload : output payload [bytes] */ \ +/* _return : flag indicating if data integrity check passed */ \ int QPACKETMODEM(_decode_soft_payload)(QPACKETMODEM() _q, \ unsigned char * _payload); \ diff --git a/src/framing/src/qpacketmodem.proto.c b/src/framing/src/qpacketmodem.proto.c index d883e8023..6f4a1ef9e 100644 --- a/src/framing/src/qpacketmodem.proto.c +++ b/src/framing/src/qpacketmodem.proto.c @@ -374,8 +374,6 @@ int QPACKETMODEM(_decode_soft)(qpacketmodem _q, } // decode symbol from modulated frame samples, returning flag if all symbols received -// _q : qpacketmodem object -// _frame : encoded/modulated symbol int QPACKETMODEM(_decode_soft_sym)(qpacketmodem _q, TO _symbol) { @@ -386,9 +384,12 @@ int QPACKETMODEM(_decode_soft_sym)(qpacketmodem _q, } int QPACKETMODEM(_decode_soft_payload)(qpacketmodem _q, - unsigned char * _payload) + unsigned char * _payload) { - assert( _q->n == _q->payload_mod_len * _q->bits_per_symbol); + if ( _q->n != _q->payload_mod_len * _q->bits_per_symbol) { + liquid_error(LIQUID_ENOINIT,"qpacketmodem_decode_soft_payload(), insufficient number of symbols received"); + return 0; + } _q->n = 0; return packetizer_decode_soft(_q->p, _q->payload_enc, _payload); } From 77c255c75e4474ef1ba0fa0a619bf879e585f702 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 22 Jun 2023 07:54:50 -0400 Subject: [PATCH 182/186] qdsync: removing duplicate definitions in header --- include/liquid.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 15db1a97a..1069b9e0b 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6306,16 +6306,7 @@ float qdetector_cccf_get_gamma (qdetector_cccf _q); // channel gain float qdetector_cccf_get_dphi (qdetector_cccf _q); // carrier frequency offset estimate float qdetector_cccf_get_phi (qdetector_cccf _q); // carrier phase offset estimate -// Frame detector and synchronizer; uses a novel correlation method to -// detect a synchronization pattern, estimate carrier frequency and -// phase offsets as well as timing phase, then correct for these -// impairments in a simple interface suitable for custom frame recovery. -typedef struct qdsync_cccf_s * qdsync_cccf; - -// synchronization callback, return 0:continue, 1:reset -typedef int (*qdsync_callback)(liquid_float_complex * _buf, - unsigned int _buf_len, - void * _context); + // metadata struct: // - sample count since object was created // - sample count since beginning of frame From c20d578ba848b8a4446488b09091f8df2216b220 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 22 Jun 2023 18:02:04 -0400 Subject: [PATCH 183/186] qdetector: making function declarations part of macro for consistency --- include/liquid.h | 200 ++++++++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 82 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 1069b9e0b..66cb54924 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6222,90 +6222,126 @@ LIQUID_PRESYNC_DEFINE_API(LIQUID_BPRESYNC_MANGLE_CCCF, // Frame detector // -typedef struct qdetector_cccf_s * qdetector_cccf; - -// create detector with generic sequence -// _s : sample sequence -// _s_len : length of sample sequence -qdetector_cccf qdetector_cccf_create(liquid_float_complex * _s, - unsigned int _s_len); - -// create detector from sequence of symbols using internal linear interpolator -// _sequence : symbol sequence -// _sequence_len : length of symbol sequence -// _ftype : filter prototype (e.g. LIQUID_FIRFILT_RRC) -// _k : samples/symbol -// _m : filter delay -// _beta : excess bandwidth factor -qdetector_cccf qdetector_cccf_create_linear(liquid_float_complex * _sequence, - unsigned int _sequence_len, - int _ftype, - unsigned int _k, - unsigned int _m, - float _beta); - -// create detector from sequence of GMSK symbols -// _sequence : bit sequence -// _sequence_len : length of bit sequence -// _k : samples/symbol -// _m : filter delay -// _beta : excess bandwidth factor -qdetector_cccf qdetector_cccf_create_gmsk(unsigned char * _sequence, - unsigned int _sequence_len, - unsigned int _k, - unsigned int _m, - float _beta); - -// create detector from sequence of CP-FSK symbols (assuming one bit/symbol) -// _sequence : bit sequence -// _sequence_len : length of bit sequence -// _bps : bits per symbol, 0 < _bps <= 8 -// _h : modulation index, _h > 0 -// _k : samples/symbol -// _m : filter delay -// _beta : filter bandwidth parameter, _beta > 0 -// _type : filter type (e.g. LIQUID_CPFSK_SQUARE) -qdetector_cccf qdetector_cccf_create_cpfsk(unsigned char * _sequence, - unsigned int _sequence_len, - unsigned int _bps, - float _h, - unsigned int _k, - unsigned int _m, - float _beta, - int _type); +#define LIQUID_QDETECTOR_MANGLE_CCCF(name) LIQUID_CONCAT(qdetector_cccf,name) -// Copy object including all internal objects and state -qdetector_cccf qdetector_cccf_copy(qdetector_cccf _q); - -int qdetector_cccf_destroy(qdetector_cccf _q); -int qdetector_cccf_print (qdetector_cccf _q); -int qdetector_cccf_reset (qdetector_cccf _q); - -// run detector, looking for sequence; return pointer to aligned, buffered samples -void * qdetector_cccf_execute(qdetector_cccf _q, - liquid_float_complex _x); - -// get detection threshold -float qdetector_cccf_get_threshold(qdetector_cccf _q); - -// set detection threshold (should be between 0 and 1, good starting point is 0.5) -int qdetector_cccf_set_threshold(qdetector_cccf _q, - float _threshold); - -// set carrier offset search range -int qdetector_cccf_set_range(qdetector_cccf _q, - float _dphi_max); - -// access methods -unsigned int qdetector_cccf_get_seq_len (qdetector_cccf _q); // sequence length -const void * qdetector_cccf_get_sequence(qdetector_cccf _q); // pointer to sequence -unsigned int qdetector_cccf_get_buf_len (qdetector_cccf _q); // buffer length -float qdetector_cccf_get_rxy (qdetector_cccf _q); // correlator output -float qdetector_cccf_get_tau (qdetector_cccf _q); // fractional timing offset estimate -float qdetector_cccf_get_gamma (qdetector_cccf _q); // channel gain -float qdetector_cccf_get_dphi (qdetector_cccf _q); // carrier frequency offset estimate -float qdetector_cccf_get_phi (qdetector_cccf _q); // carrier phase offset estimate +#define LIQUID_QDETECTOR_DEFINE_API(QDETECTOR,TO,TC,TI) \ + \ +/* Frame detector and synchronizer; uses a novel correlation method to */ \ +/* detect a synchronization pattern, estimate carrier frequency and */ \ +/* phase offsets as well as timing phase, then correct for these */ \ +/* impairments in a simple interface suitable for custom frame recovery.*/ \ +typedef struct QDETECTOR(_s) * QDETECTOR(); \ + \ +typedef struct qdetector_cccf_s * qdetector_cccf; \ + \ +/* Create detector with generic sequence */ \ +/* _s : sample sequence */ \ +/* _s_len : length of sample sequence */ \ +QDETECTOR() QDETECTOR(_create)(TI * _s, \ + unsigned int _s_len); \ + \ +/* Create detector from sequence of symbols using internal linear */ \ +/* interpolator */ \ +/* _sequence : symbol sequence */ \ +/* _sequence_len : length of symbol sequence */ \ +/* _ftype : filter prototype (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _k : samples/symbol */ \ +/* _m : filter delay */ \ +/* _beta : excess bandwidth factor */ \ +QDETECTOR() QDETECTOR(_create_linear)(TI * _sequence, \ + unsigned int _sequence_len, \ + int _ftype, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta); \ + \ +/* create detector from sequence of GMSK symbols */ \ +/* _sequence : bit sequence */ \ +/* _sequence_len : length of bit sequence */ \ +/* _k : samples/symbol */ \ +/* _m : filter delay */ \ +/* _beta : excess bandwidth factor */ \ +QDETECTOR() QDETECTOR(_create_gmsk)(unsigned char * _sequence, \ + unsigned int _sequence_len, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta); \ + \ +/* create detector from sequence of CP-FSK symbols (assuming one */ \ +/* bit/symbol) */ \ +/* _sequence : bit sequence */ \ +/* _sequence_len : length of bit sequence */ \ +/* _bps : bits per symbol, 0 < _bps <= 8 */ \ +/* _h : modulation index, _h > 0 */ \ +/* _k : samples/symbol */ \ +/* _m : filter delay */ \ +/* _beta : filter bandwidth parameter, _beta > 0 */ \ +/* _type : filter type (e.g. LIQUID_CPFSK_SQUARE) */ \ +QDETECTOR() QDETECTOR(_create_cpfsk)(unsigned char * _sequence, \ + unsigned int _sequence_len, \ + unsigned int _bps, \ + float _h, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + int _type); \ + \ +/* Copy object including all internal objects and state */ \ +QDETECTOR() QDETECTOR(_copy)(QDETECTOR() _q); \ + \ +/* Destroy synchronizer object and free all internal memory */ \ +int QDETECTOR(_destroy)(QDETECTOR() _q); \ + \ +/* Reset synchronizer object's internal buffer */ \ +int QDETECTOR(_reset)(QDETECTOR() _q); \ + \ +/* Print synchronizer object information to stdout */ \ +int QDETECTOR(_print)(QDETECTOR() _q); \ + \ +/* run detector, looking for sequence; return pointer to aligned, */ \ +/* buffered samples */ \ +void * QDETECTOR(_execute)(QDETECTOR() _q, TI _x); \ + \ +/* get detection threshold */ \ +float QDETECTOR(_get_threshold)(QDETECTOR() _q); \ + \ +/* set detection threshold (should be between 0 and 1, good starting */ \ +/* point is 0.5) */ \ +int QDETECTOR(_set_threshold)(QDETECTOR() _q, \ + float _threshold); \ + \ +/* Set carrier offset search range */ \ +int QDETECTOR(_set_range)(QDETECTOR() _q, \ + float _dphi_max); \ + \ +/* Get sequence length */ \ +unsigned int QDETECTOR(_get_seq_len)(QDETECTOR() _q); \ + \ +/* Get pointer to sequence of detected frame */ \ +const void * QDETECTOR(_get_sequence)(QDETECTOR() _q); \ + \ +/* Get buffer length */ \ +unsigned int QDETECTOR(_get_buf_len)(QDETECTOR() _q); \ + \ +/* Get correlator output of detected frame */ \ +float QDETECTOR(_get_rxy)(QDETECTOR() _q); \ + \ +/* Get fractional timing offset estimate of detected frame */ \ +float QDETECTOR(_get_tau)(QDETECTOR() _q); \ + \ +/* Get channel gain of detected frame */ \ +float QDETECTOR(_get_gamma)(QDETECTOR() _q); \ + \ +/* Get carrier frequency offset estimateof detected frame */ \ +float QDETECTOR(_get_dphi)(QDETECTOR() _q); \ + \ +/* Get carrier phase offset estimate of detected frame */ \ +float QDETECTOR(_get_phi)(QDETECTOR() _q); \ +LIQUID_QDETECTOR_DEFINE_API(LIQUID_QDETECTOR_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) // metadata struct: // - sample count since object was created From 60769e36a9e52a1818111cd003923857735eb735 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 22 Jun 2023 18:08:38 -0400 Subject: [PATCH 184/186] qdetector: including as prototype in top-level framing source --- makefile.in | 2 +- src/framing/src/framing_cccf.c | 2 ++ src/framing/src/{qdetector_cccf.c => qdetector.proto.c} | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename src/framing/src/{qdetector_cccf.c => qdetector.proto.c} (100%) diff --git a/makefile.in b/makefile.in index ca8d92205..ddac1ece7 100644 --- a/makefile.in +++ b/makefile.in @@ -649,7 +649,6 @@ framing_objects := \ src/framing/src/gmskframesync.o \ src/framing/src/ofdmflexframegen.o \ src/framing/src/ofdmflexframesync.o \ - src/framing/src/qdetector_cccf.o \ src/framing/src/qpilotgen.o \ src/framing/src/qpilotsync.o \ @@ -667,6 +666,7 @@ framing_prototypes_sync := \ src/framing/src/bpresync.proto.c \ src/framing/src/bsync.proto.c \ src/framing/src/presync.proto.c \ + src/framing/src/qdetector.proto.c \ src/framing/src/qdsync.proto.c \ src/framing/src/symtrack.proto.c \ diff --git a/src/framing/src/framing_cccf.c b/src/framing/src/framing_cccf.c index 6dc777ffc..8cdd68b92 100644 --- a/src/framing/src/framing_cccf.c +++ b/src/framing/src/framing_cccf.c @@ -57,6 +57,7 @@ #define BPRESYNC(name) LIQUID_CONCAT(bpresync_cccf,name) #define BSYNC(name) LIQUID_CONCAT(bsync_cccf,name) #define PRESYNC(name) LIQUID_CONCAT(presync_cccf,name) +#define QDETECTOR(name) LIQUID_CONCAT(qdetector_cccf,name) #define QDSYNC(name) LIQUID_CONCAT(qdsync_cccf,name) #define SYMTRACK(name) LIQUID_CONCAT(symtrack_cccf,name) @@ -64,6 +65,7 @@ #include "bpresync.proto.c" #include "bsync.proto.c" #include "presync.proto.c" +#include "qdetector.proto.c" #include "qdsync.proto.c" #include "symtrack.proto.c" diff --git a/src/framing/src/qdetector_cccf.c b/src/framing/src/qdetector.proto.c similarity index 100% rename from src/framing/src/qdetector_cccf.c rename to src/framing/src/qdetector.proto.c From 26494d9e67eb6286dd4c368d2342e3636485fbf2 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Thu, 22 Jun 2023 18:15:13 -0400 Subject: [PATCH 185/186] qdetector: completing conversion of qdetector to macro --- src/framing/src/qdetector.proto.c | 197 +++++++++++++++--------------- 1 file changed, 97 insertions(+), 100 deletions(-) diff --git a/src/framing/src/qdetector.proto.c b/src/framing/src/qdetector.proto.c index f520b4a28..5c13f45eb 100644 --- a/src/framing/src/qdetector.proto.c +++ b/src/framing/src/qdetector.proto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 - 2022 Joseph Gaeddert + * Copyright (c) 2007 - 2023 Joseph Gaeddert * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,22 +35,22 @@ #define DEBUG_QDETECTOR_FILENAME "qdetector_cccf_debug.m" // seek signal (initial detection) -int qdetector_cccf_execute_seek(qdetector_cccf _q, float complex _x); +int QDETECTOR(_execute_seek)(QDETECTOR() _q, TI _x); // align signal in time, compute offset estimates -int qdetector_cccf_execute_align(qdetector_cccf _q, float complex _x); +int QDETECTOR(_execute_align)(QDETECTOR() _q, TI _x); // main object definition -struct qdetector_cccf_s { +struct QDETECTOR(_s) { unsigned int s_len; // template (time) length: k * (sequence_len + 2*m) - float complex * s; // template (time), [size: s_len x 1] - float complex * S; // template (freq), [size: nfft x 1] + TI * s; // template (time), [size: s_len x 1] + TI * S; // template (freq), [size: nfft x 1] float s2_sum; // sum{ s^2 } - float complex * buf_time_0; // time-domain buffer (FFT) - float complex * buf_freq_0; // frequence-domain buffer (FFT) - float complex * buf_freq_1; // frequence-domain buffer (IFFT) - float complex * buf_time_1; // time-domain buffer (IFFT) + TI * buf_time_0; // time-domain buffer (FFT) + TI * buf_freq_0; // frequence-domain buffer (FFT) + TI * buf_freq_1; // frequence-domain buffer (IFFT) + TI * buf_time_1; // time-domain buffer (IFFT) unsigned int nfft; // fft size FFT_PLAN fft; // FFT object: buf_time_0 > buf_freq_0 FFT_PLAN ifft; // IFFT object: buf_freq_1 > buf_freq_1 @@ -80,38 +80,38 @@ struct qdetector_cccf_s { // create detector with generic sequence // _s : sample sequence // _s_len : length of sample sequence -qdetector_cccf qdetector_cccf_create(float complex * _s, - unsigned int _s_len) +QDETECTOR() QDETECTOR(_create)(TI * _s, + unsigned int _s_len) { // validate input if (_s_len == 0) - return liquid_error_config("qdetector_cccf_create(), sequence length cannot be zero"); + return liquid_error_config("QDETECTOR(_create)(), sequence length cannot be zero"); // allocate memory for main object and set internal properties - qdetector_cccf q = (qdetector_cccf) malloc(sizeof(struct qdetector_cccf_s)); + QDETECTOR() q = (QDETECTOR()) malloc(sizeof(struct QDETECTOR(_s))); q->s_len = _s_len; // allocate memory and copy sequence - q->s = (float complex*) malloc(q->s_len * sizeof(float complex)); - memmove(q->s, _s, q->s_len*sizeof(float complex)); + q->s = (TI*) malloc(q->s_len * sizeof(TI)); + memmove(q->s, _s, q->s_len*sizeof(TI)); q->s2_sum = liquid_sumsqcf(q->s, q->s_len); // compute sum{ s^2 } // prepare transforms q->nfft = 1 << liquid_nextpow2( (unsigned int)( 2 * q->s_len ) ); // NOTE: must be even - q->buf_time_0 = (float complex*) FFT_MALLOC(q->nfft * sizeof(float complex)); - q->buf_freq_0 = (float complex*) FFT_MALLOC(q->nfft * sizeof(float complex)); - q->buf_freq_1 = (float complex*) FFT_MALLOC(q->nfft * sizeof(float complex)); - q->buf_time_1 = (float complex*) FFT_MALLOC(q->nfft * sizeof(float complex)); + q->buf_time_0 = (TI*) FFT_MALLOC(q->nfft * sizeof(TI)); + q->buf_freq_0 = (TI*) FFT_MALLOC(q->nfft * sizeof(TI)); + q->buf_freq_1 = (TI*) FFT_MALLOC(q->nfft * sizeof(TI)); + q->buf_time_1 = (TI*) FFT_MALLOC(q->nfft * sizeof(TI)); q->fft = FFT_CREATE_PLAN(q->nfft, q->buf_time_0, q->buf_freq_0, FFT_DIR_FORWARD, 0); q->ifft = FFT_CREATE_PLAN(q->nfft, q->buf_freq_1, q->buf_time_1, FFT_DIR_BACKWARD, 0); // create frequency-domain template by taking nfft-point transform on 's', storing in 'S' - q->S = (float complex*) malloc(q->nfft * sizeof(float complex)); - memset(q->buf_time_0, 0x00, q->nfft*sizeof(float complex)); - memmove(q->buf_time_0, q->s, q->s_len*sizeof(float complex)); + q->S = (TI*) malloc(q->nfft * sizeof(TI)); + memset(q->buf_time_0, 0x00, q->nfft*sizeof(TI)); + memmove(q->buf_time_0, q->s, q->s_len*sizeof(TI)); FFT_EXECUTE(q->fft); - memmove(q->S, q->buf_freq_0, q->nfft*sizeof(float complex)); + memmove(q->S, q->buf_freq_0, q->nfft*sizeof(TI)); // reset state variables q->counter = q->nfft/2; @@ -120,7 +120,7 @@ qdetector_cccf qdetector_cccf_create(float complex * _s, q->x2_sum_1 = 0.0f; q->state = QDETECTOR_STATE_SEEK; q->frame_detected = 0; - memset(q->buf_time_0, 0x00, q->nfft*sizeof(float complex)); + memset(q->buf_time_0, 0x00, q->nfft*sizeof(TI)); // reset estimates q->rxy = 0.0f; @@ -129,8 +129,8 @@ qdetector_cccf qdetector_cccf_create(float complex * _s, q->dphi_hat = 0.0f; q->phi_hat = 0.0f; - qdetector_cccf_set_threshold(q,0.5f); - qdetector_cccf_set_range (q,0.3f); // set initial range for higher detection + QDETECTOR(_set_threshold)(q,0.5f); + QDETECTOR(_set_range )(q,0.3f); // set initial range for higher detection // return object return q; @@ -144,26 +144,26 @@ qdetector_cccf qdetector_cccf_create(float complex * _s, // _k : samples/symbol // _m : filter delay // _beta : excess bandwidth factor -qdetector_cccf qdetector_cccf_create_linear(float complex * _sequence, - unsigned int _sequence_len, - int _ftype, - unsigned int _k, - unsigned int _m, - float _beta) +QDETECTOR() QDETECTOR(_create_linear)(TI * _sequence, + unsigned int _sequence_len, + int _ftype, + unsigned int _k, + unsigned int _m, + float _beta) { // validate input if (_sequence_len == 0) - return liquid_error_config("qdetector_cccf_create_linear(), sequence length cannot be zero"); + return liquid_error_config("QDETECTOR(_create_linear)(), sequence length cannot be zero"); if (_k < 2 || _k > 80) - return liquid_error_config("qdetector_cccf_create_linear(), samples per symbol must be in [2,80]"); + return liquid_error_config("QDETECTOR(_create_linear)(), samples per symbol must be in [2,80]"); if (_m < 1 || _m > 100) - return liquid_error_config("qdetector_cccf_create_linear(), filter delay must be in [1,100]"); + return liquid_error_config("QDETECTOR(_create_linear)(), filter delay must be in [1,100]"); if (_beta < 0.0f || _beta > 1.0f) - return liquid_error_config("qdetector_cccf_create_linear(), excess bandwidth factor must be in [0,1]"); + return liquid_error_config("QDETECTOR(_create_linear)(), excess bandwidth factor must be in [0,1]"); // create time-domain template unsigned int s_len = _k * (_sequence_len + 2*_m); - float complex * s = (float complex*) malloc(s_len * sizeof(float complex)); + TI * s = (TI*) malloc(s_len * sizeof(TI)); firinterp_crcf interp = firinterp_crcf_create_prototype(_ftype, _k, _m, _beta, 0); unsigned int i; for (i=0; i<_sequence_len + 2*_m; i++) @@ -171,7 +171,7 @@ qdetector_cccf qdetector_cccf_create_linear(float complex * _sequence, firinterp_crcf_destroy(interp); // create main object - qdetector_cccf q = qdetector_cccf_create(s, s_len); + QDETECTOR() q = QDETECTOR(_create)(s, s_len); // free allocated temporary array free(s); @@ -186,25 +186,25 @@ qdetector_cccf qdetector_cccf_create_linear(float complex * _sequence, // _k : samples/symbol // _m : filter delay // _beta : excess bandwidth factor -qdetector_cccf qdetector_cccf_create_gmsk(unsigned char * _sequence, - unsigned int _sequence_len, - unsigned int _k, - unsigned int _m, - float _beta) +QDETECTOR() QDETECTOR(_create_gmsk)(unsigned char * _sequence, + unsigned int _sequence_len, + unsigned int _k, + unsigned int _m, + float _beta) { // validate input if (_sequence_len == 0) - return liquid_error_config("qdetector_cccf_create_gmsk(), sequence length cannot be zero"); + return liquid_error_config("QDETECTOR(_create_gmsk)(), sequence length cannot be zero"); if (_k < 2 || _k > 80) - return liquid_error_config("qdetector_cccf_create_gmsk(), samples per symbol must be in [2,80]"); + return liquid_error_config("QDETECTOR(_create_gmsk)(), samples per symbol must be in [2,80]"); if (_m < 1 || _m > 100) - return liquid_error_config("qdetector_cccf_create_gmsk(), filter delay must be in [1,100]"); + return liquid_error_config("QDETECTOR(_create_gmsk)(), filter delay must be in [1,100]"); if (_beta < 0.0f || _beta > 1.0f) - return liquid_error_config("qdetector_cccf_create_gmsk(), excess bandwidth factor must be in [0,1]"); + return liquid_error_config("QDETECTOR(_create_gmsk)(), excess bandwidth factor must be in [0,1]"); // create time-domain template using GMSK modem unsigned int s_len = _k * (_sequence_len + 2*_m); - float complex * s = (float complex*) malloc(s_len * sizeof(float complex)); + TI * s = (TI*) malloc(s_len * sizeof(TI)); gmskmod mod = gmskmod_create(_k, _m, _beta); unsigned int i; for (i=0; i<_sequence_len + 2*_m; i++) @@ -212,7 +212,7 @@ qdetector_cccf qdetector_cccf_create_gmsk(unsigned char * _sequence, gmskmod_destroy(mod); // create main object - qdetector_cccf q = qdetector_cccf_create(s, s_len); + QDETECTOR() q = QDETECTOR(_create)(s, s_len); // free allocated temporary array free(s); @@ -230,28 +230,28 @@ qdetector_cccf qdetector_cccf_create_gmsk(unsigned char * _sequence, // _m : filter delay // _beta : filter bandwidth parameter, _beta > 0 // _type : filter type (e.g. LIQUID_CPFSK_SQUARE) -qdetector_cccf qdetector_cccf_create_cpfsk(unsigned char * _sequence, - unsigned int _sequence_len, - unsigned int _bps, - float _h, - unsigned int _k, - unsigned int _m, - float _beta, - int _type) +QDETECTOR() QDETECTOR(_create_cpfsk)(unsigned char * _sequence, + unsigned int _sequence_len, + unsigned int _bps, + float _h, + unsigned int _k, + unsigned int _m, + float _beta, + int _type) { // validate input if (_sequence_len == 0) - return liquid_error_config("qdetector_cccf_create_cpfsk(), sequence length cannot be zero"); + return liquid_error_config("QDETECTOR(_create_cpfsk)(), sequence length cannot be zero"); if (_k < 2 || _k > 80) - return liquid_error_config("qdetector_cccf_create_cpfsk(), samples per symbol must be in [2,80]"); + return liquid_error_config("QDETECTOR(_create_cpfsk)(), samples per symbol must be in [2,80]"); if (_m < 1 || _m > 100) - return liquid_error_config("qdetector_cccf_create_cpfsk(), filter delay must be in [1,100]"); + return liquid_error_config("QDETECTOR(_create_cpfsk)(), filter delay must be in [1,100]"); if (_beta < 0.0f || _beta > 1.0f) - return liquid_error_config("qdetector_cccf_create_cpfsk(), excess bandwidth factor must be in [0,1]"); + return liquid_error_config("QDETECTOR(_create_cpfsk)(), excess bandwidth factor must be in [0,1]"); // create time-domain template using GMSK modem - unsigned int s_len = _k * (_sequence_len + 2*_m); - float complex * s = (float complex*) malloc(s_len * sizeof(float complex)); + unsigned int s_len = _k * (_sequence_len + 2*_m); + TI * s = (TI*) malloc(s_len * sizeof(TI)); cpfskmod mod = cpfskmod_create(_bps, _h, _k, _m, _beta, _type); unsigned int i; for (i=0; i<_sequence_len + 2*_m; i++) @@ -259,7 +259,7 @@ qdetector_cccf qdetector_cccf_create_cpfsk(unsigned char * _sequence, cpfskmod_destroy(mod); // create main object - qdetector_cccf q = qdetector_cccf_create(s, s_len); + QDETECTOR() q = QDETECTOR(_create)(s, s_len); // free allocated temporary array free(s); @@ -269,20 +269,20 @@ qdetector_cccf qdetector_cccf_create_cpfsk(unsigned char * _sequence, } // copy object -qdetector_cccf qdetector_cccf_copy(qdetector_cccf q_orig) +QDETECTOR() QDETECTOR(_copy)(QDETECTOR() q_orig) { // validate input if (q_orig == NULL) - return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", "cccf"); + return liquid_error_config("qdetector_%s_copy(), object cannot be NULL", EXTENSION_FULL); // create new object from internal sequence - qdetector_cccf q_copy = qdetector_cccf_create(q_orig->s, q_orig->s_len); + QDETECTOR() q_copy = QDETECTOR(_create)(q_orig->s, q_orig->s_len); // copy buffer contents - memmove(q_copy->buf_time_0, q_orig->buf_time_0, q_orig->nfft*sizeof(float complex)); - memmove(q_copy->buf_freq_0, q_orig->buf_freq_0, q_orig->nfft*sizeof(float complex)); - memmove(q_copy->buf_time_1, q_orig->buf_time_1, q_orig->nfft*sizeof(float complex)); - memmove(q_copy->buf_freq_1, q_orig->buf_freq_1, q_orig->nfft*sizeof(float complex)); + memmove(q_copy->buf_time_0, q_orig->buf_time_0, q_orig->nfft*sizeof(TI)); + memmove(q_copy->buf_freq_0, q_orig->buf_freq_0, q_orig->nfft*sizeof(TI)); + memmove(q_copy->buf_time_1, q_orig->buf_time_1, q_orig->nfft*sizeof(TI)); + memmove(q_copy->buf_freq_1, q_orig->buf_freq_1, q_orig->nfft*sizeof(TI)); // copy internal state q_copy->counter = q_orig->counter; @@ -300,7 +300,7 @@ qdetector_cccf qdetector_cccf_copy(qdetector_cccf q_orig) return q_copy; } -int qdetector_cccf_destroy(qdetector_cccf _q) +int QDETECTOR(_destroy)(QDETECTOR() _q) { // free allocated arrays free(_q->s); @@ -319,9 +319,9 @@ int qdetector_cccf_destroy(qdetector_cccf _q) return LIQUID_OK; } -int qdetector_cccf_print(qdetector_cccf _q) +int QDETECTOR(_print)(QDETECTOR() _q) { - printf("qdetector_cccf:\n"); + printf("qdetector_%s:\n", EXTENSION_FULL); printf(" template length (time): %-u\n", _q->s_len); printf(" FFT size : %-u\n", _q->nfft); printf(" search range (bins) : %-d\n", _q->range); @@ -330,23 +330,22 @@ int qdetector_cccf_print(qdetector_cccf _q) return LIQUID_OK; } -int qdetector_cccf_reset(qdetector_cccf _q) +int QDETECTOR(_reset)(QDETECTOR() _q) { return LIQUID_OK; } -void * qdetector_cccf_execute(qdetector_cccf _q, - float complex _x) +void * QDETECTOR(_execute)(QDETECTOR() _q, TI _x) { switch (_q->state) { case QDETECTOR_STATE_SEEK: // seek signal - qdetector_cccf_execute_seek(_q, _x); + QDETECTOR(_execute_seek)(_q, _x); break; case QDETECTOR_STATE_ALIGN: // align signal - qdetector_cccf_execute_align(_q, _x); + QDETECTOR(_execute_align)(_q, _x); break; } @@ -364,13 +363,13 @@ void * qdetector_cccf_execute(qdetector_cccf _q, } // get detection threshold -float qdetector_cccf_get_threshold(qdetector_cccf _q) +float QDETECTOR(_get_threshold)(QDETECTOR() _q) { return _q->threshold; } // set detection threshold (should be between 0 and 1, good starting point is 0.5) -int qdetector_cccf_set_threshold(qdetector_cccf _q, +int QDETECTOR(_set_threshold)(QDETECTOR() _q, float _threshold) { if (_threshold <= 0.0f || _threshold > 2.0f) @@ -382,7 +381,7 @@ int qdetector_cccf_set_threshold(qdetector_cccf _q, } // set carrier offset search range -int qdetector_cccf_set_range(qdetector_cccf _q, +int QDETECTOR(_set_range)(QDETECTOR() _q, float _dphi_max) { if (_dphi_max < 0.0f || _dphi_max > 0.5f) @@ -396,49 +395,49 @@ int qdetector_cccf_set_range(qdetector_cccf _q, } // get sequence length -unsigned int qdetector_cccf_get_seq_len(qdetector_cccf _q) +unsigned int QDETECTOR(_get_seq_len)(QDETECTOR() _q) { return _q->s_len; } // pointer to sequence -const void * qdetector_cccf_get_sequence(qdetector_cccf _q) +const void * QDETECTOR(_get_sequence)(QDETECTOR() _q) { return (const void*) _q->s; } // buffer length -unsigned int qdetector_cccf_get_buf_len(qdetector_cccf _q) +unsigned int QDETECTOR(_get_buf_len)(QDETECTOR() _q) { return _q->nfft; } // correlator output -float qdetector_cccf_get_rxy(qdetector_cccf _q) +float QDETECTOR(_get_rxy)(QDETECTOR() _q) { return _q->rxy; } // fractional timing offset estimate -float qdetector_cccf_get_tau(qdetector_cccf _q) +float QDETECTOR(_get_tau)(QDETECTOR() _q) { return _q->tau_hat; } // channel gain -float qdetector_cccf_get_gamma(qdetector_cccf _q) +float QDETECTOR(_get_gamma)(QDETECTOR() _q) { return _q->gamma_hat; } // carrier frequency offset estimate -float qdetector_cccf_get_dphi(qdetector_cccf _q) +float QDETECTOR(_get_dphi)(QDETECTOR() _q) { return _q->dphi_hat; } // carrier phase offset estimate -float qdetector_cccf_get_phi(qdetector_cccf _q) +float QDETECTOR(_get_phi)(QDETECTOR() _q) { return _q->phi_hat; } @@ -449,8 +448,7 @@ float qdetector_cccf_get_phi(qdetector_cccf _q) // // seek signal (initial detection) -int qdetector_cccf_execute_seek(qdetector_cccf _q, - float complex _x) +int QDETECTOR(_execute_seek)(QDETECTOR() _q, TI _x) { // write sample to buffer and increment counter _q->buf_time_0[_q->counter++] = _x; @@ -554,7 +552,7 @@ int qdetector_cccf_execute_seek(qdetector_cccf _q, // TODO: check for edge case where rxy_index is zero (signal already aligned) // copy last part of fft input buffer to front - memmove(_q->buf_time_0, _q->buf_time_0 + rxy_index, (_q->nfft - rxy_index)*sizeof(float complex)); + memmove(_q->buf_time_0, _q->buf_time_0 + rxy_index, (_q->nfft - rxy_index)*sizeof(TI)); _q->counter = _q->nfft - rxy_index; return LIQUID_OK; @@ -564,7 +562,7 @@ int qdetector_cccf_execute_seek(qdetector_cccf _q, #endif // copy last half of fft input buffer to front - memmove(_q->buf_time_0, _q->buf_time_0 + _q->nfft/2, (_q->nfft/2)*sizeof(float complex)); + memmove(_q->buf_time_0, _q->buf_time_0 + _q->nfft/2, (_q->nfft/2)*sizeof(TI)); // swap accumulated signal levels _q->x2_sum_0 = _q->x2_sum_1; @@ -573,8 +571,7 @@ int qdetector_cccf_execute_seek(qdetector_cccf _q, } // align signal in time, compute offset estimates -int qdetector_cccf_execute_align(qdetector_cccf _q, - float complex _x) +int QDETECTOR(_execute_align)(QDETECTOR() _q, TI _x) { // write sample to buffer and increment counter _q->buf_time_0[_q->counter++] = _x; @@ -611,7 +608,7 @@ int qdetector_cccf_execute_align(qdetector_cccf _q, // TODO: revise estimate of rxy here // copy buffer to preserve data integrity - memmove(_q->buf_time_1, _q->buf_time_0, _q->nfft*sizeof(float complex)); + memmove(_q->buf_time_1, _q->buf_time_0, _q->nfft*sizeof(TI)); // estimate carrier frequency offset for (i=0; i<_q->nfft; i++) @@ -670,7 +667,7 @@ int qdetector_cccf_execute_align(qdetector_cccf _q, // METHOD 2: compute metric by de-rotating signal and measuring resulting phase // NOTE: this is possibly more accurate than the above method but might also // be more computationally complex - float complex metric = 0; + TI metric = 0; for (i=0; i<_q->s_len; i++) metric += _q->buf_time_0[i] * cexpf(-_Complex_I*_q->dphi_hat*i); //printf("metric : %12.8f <%12.8f>\n", cabsf(metric), cargf(metric)); @@ -696,7 +693,7 @@ int qdetector_cccf_execute_align(qdetector_cccf _q, // reset state // copy saved buffer state (last half of buf_time_1 to front half of buf_time_0) - memmove(_q->buf_time_0, _q->buf_time_1 + _q->nfft/2, (_q->nfft/2)*sizeof(float complex)); + memmove(_q->buf_time_0, _q->buf_time_1 + _q->nfft/2, (_q->nfft/2)*sizeof(TI)); _q->state = QDETECTOR_STATE_SEEK; _q->x2_sum_0 = liquid_sumsqcf(_q->buf_time_0, _q->nfft/2); _q->x2_sum_1 = 0; From 1e4a9f94666332894ca1a4cb07b96ec5d2631e18 Mon Sep 17 00:00:00 2001 From: "Joseph D. Gaeddert" Date: Sat, 24 Jun 2023 11:03:38 -0400 Subject: [PATCH 186/186] build: removing duplicate struct pointer definition for qdetector_cccf --- include/liquid.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/liquid.h b/include/liquid.h index 66cb54924..d5b55a8d2 100644 --- a/include/liquid.h +++ b/include/liquid.h @@ -6232,8 +6232,6 @@ LIQUID_PRESYNC_DEFINE_API(LIQUID_BPRESYNC_MANGLE_CCCF, /* impairments in a simple interface suitable for custom frame recovery.*/ \ typedef struct QDETECTOR(_s) * QDETECTOR(); \ \ -typedef struct qdetector_cccf_s * qdetector_cccf; \ - \ /* Create detector with generic sequence */ \ /* _s : sample sequence */ \ /* _s_len : length of sample sequence */ \