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;
}