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.


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);
    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(

    // run entire signal through sync object
    printf("# recovered symbols\n");
    qdsync_cccf_execute(q, buf_samples, num_samples);

    return 0;