qdsync (sequence detection and synchronization)
API Keywords: qdsync detector carrier recovery timing recovery
The qdsync family of objects implements a sequence detector, carrier and timing offset estimation, and correction. The object wraps the lower-level qdetector object in a higher-level structure to abstract pointers, buffering, resampling, and carrier recovery by invoking a user-defined callback function when the frame is detected. This greatly simplifies the interface for end-to-end operation of defining a preamble and receiving the recovered symbols when the frame is recovered.
Internal Use∞
To simplify processing and reduce code duplication, liquid makes extensive use of the qdsync family of objects for many of the framing objects.
Example∞
An example of the qdsync object can be found in the listing, below.
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <complex.h>
#include <liquid/liquid.h>
void print_cfloat(float complex * _buf, unsigned int _buf_len)
{
unsigned int i;
for (i=0; i<_buf_len; i++)
printf(" %12.8f %12.8f\n", crealf(_buf[i]), cimagf(_buf[i]));
}
// synchronization callback
int callback(float complex * _buf, unsigned int _buf_len, void * _context)
{
print_cfloat(_buf, _buf_len);
return 0;
}
int main(int argc, char*argv[])
{
// options
unsigned int preamble_len = 64; // number of sync symbols
unsigned int payload_len = 1024; // 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;
// derived values
unsigned int num_symbols = preamble_len + payload_len + 2*m;
unsigned int num_samples = num_symbols * k;
// buffers
float complex buf_symbols[num_symbols];
float complex buf_samples[num_samples];
// generate frame symbols (QPSK for preamble, BPSK for payload)
unsigned int i;
for (i=0; i<num_symbols; i++) {
if (i < preamble_len) {
buf_symbols[i] = (rand() % 2 ? 1.0f : -1.0f) * M_SQRT1_2 +
(rand() % 2 ? 1.0f : -1.0f) * M_SQRT1_2 * _Complex_I;
} else {
buf_symbols[i] = rand() % 2 ? 1.0f : -1.0f;
}
}
// create interpolator and generate full sequence
firinterp_crcf interp = firinterp_crcf_create_prototype(ftype,k,m,beta,0);
firinterp_crcf_execute_block(interp, buf_symbols, num_symbols, buf_samples);
// flush interpolator with zeros
for (i=0; i<2*m; i++)
firinterp_crcf_execute(interp, 0, buf_samples + i*k);
firinterp_crcf_destroy(interp);
printf("# frame samples\n");
print_cfloat(buf_samples, num_samples);
// create synchronization object just on "preamble" portion of symbols
qdsync_cccf q = qdsync_cccf_create_linear(
buf_symbols,preamble_len,ftype,k,m,beta,callback,NULL);
// run entire signal through sync object
printf("# recovered symbols\n");
qdsync_cccf_execute(q, buf_samples, num_samples);
qdsync_cccf_destroy(q);
return 0;
}