liquid-dsp release 1.3.0

By gaeddert Joseph D. Gaeddert on December 30, 2016

Version 1.3.0 is finally tagged in the Git repository . You could download a compressed archive from that link, but I would recommend cloning the repository and checking out the tag directly from there:


$ git clone https://github.com/jgaeddert/liquid-dsp.git
$ cd liquid-dsp
$ git checkout v1.3.0

Now configure and build:


$ ./bootstrap.sh
$ ./configure
$ make -j8
$ sudo make install

More than three years and 4,600 commits have passed since version 1.2.0 which was released on April 26, 2012. Keeping up with releases has clearly been a challenge, and while this tag has been a long time coming, version 1.3.0 offers more stability and a better interface for many of the tools liquid-dsp has to offer. Here is a condensed changelog of updates since version 1.2.0:

  • New MIT/X11 license
  • Consistent interface across objects
  • execute_block() methods for objects to operate on buffers rather than just single samples at a time
  • agc : major code scrub, improved reliability, simplified interface, block-level operation
  • buffer : new cbuffer objects for dynamic-sized circular buffering
  • channel : new module to emulate channel effects such as carrier frequency/phase offsets, (time-varying) multi-path, shadowing, and additive noise
  • dotprod : adding method to compute \(x^T x\) of a vector (sum of squares)
  • equalization : improved interface and simplified methods to support blind operation
  • fec : interleaver and packetizer moved from the framing to the fec module, packetizer now includes data whitening
  • fft : general speed improvements, completely reimplemented spgram and asgram objects
  • filter : additional prototype create methods, block execution, added new fftfilt family of objects to realize linear filter with fast Fourier transforms, interp family renamed to firinterp , new iirinterp family, decim family renamed to firdecim , new iirdecim family, add linear interpolation for resamp output new multi-stage half-band resampler family msresamp2 , symsync : improved stability, added rate adjustment to help pull in sample rate offsets
  • framing added framedatastats object for counting statistics across different framing objects (e.g. number of total bytes received), adding generic callback function definition for all framing structures, qpacketmodem : new object to easily combine modulating and encoding; buffer of data in, modulated and encoded samples out qpilotgen/qpilotsync : new objects to add and synchronize pilot symbols to modulated symbols to recover carrier frequency/phase, and gain framing objects: frame64 , flexframe now use qpacketmodem, qpilotgen, and qpilotsync objects for unified interface and vastly improved performance flexframe : vastly simplified interface qdetector : new family for pre-demodulator synchronizion and detection, moved interleaver and packetizer objects to fec module, symstream : new family for generating random stream of modulated samples msource : new family for generating multiple signals for a single source including tones, noise, modulated symbols symtrack : new family for tracking a stream of symbols and recovering signal level, timing, carrier frequency/phase without pilots
  • math new windowing methods (e.g. 7-term Blackman-harris window)
  • matrix adding smatrix family of objects (sparse matrices) improving linear solver methods (roughly doubled speed)
  • modem re-organizing internal linear modem code (no interface change) freqmod/freqdem : new interface, block-level execution for analog FM cpfskmod/cpfskdem : new family for generic non-linear continuous-phase frequency-shift modulation (e.g. minimum-shift keying) fskmod/fskdem : new family for non-coherent frequency-shift keying modulation, often with many samples per symbol (e.g. 256-FSK)
  • multicarrier adding OFDM framing option fo [window tapering] (/doc/ofdmflexframe/#tapering], simplfying OFDM framing for generating preamble symbols (all generated OFDM symbols are the same length), adding run-time option for debugging ofdmframesync , adding method for initializing subcarriers with frequency range
  • nco (numerically-controlled oscillator) general performance improvements, adding block-level execution
  • optim gradsearch (gradient search) uses internal linesearch for significant speed increase and better reliability, gradsearch interface greatly simplified
  • utility build no longer auto-generates tables at compile time (helps with cross compilation)
  • vector new module to simplify basic vector operations
  • miscellany documentation moved from core repository to website global header file ( liquid.h ) include more structured source consistent naming of reset() methods for most objects

The documentation on this site is also being updated to reflect version 1.3.0, but rather than expect you to dig through pages of documents to see what this new version has to offer I thought an example might be more prudent.

Example Package

To showcase what's available in this release, I have packaged a data file liquid-dsp-1.3.0-example.tar.gz containing samples with various signals present along with a program for processing it. The included image waterfall.png shows a waterfall plot of the included data file:

waterfall.png

Waterfall plot of included data file showing its spectral content over time.

Embedded in the samples are five packets 120 bytes of data each. The included source file signal_analyzer.c extracts five packets of data located at f/Fs=0.237 and over-sampled by 7.2280. The signal analyzer program uses the nco object to mix the signal to baseband, the msresamp object to efficiently resample the signal to 2 samples per symbol, and the flexframesync to detect, demodulate, and decode packets:


// signal_analyzer.c : process example file and extract packets
// http://liquidsdr.org/blog/liquid-dsp-1.3.0/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <liquid/liquid.h>

// user-defined callback function
static int callback(unsigned char *  _header,
                    int              _header_valid,
                    unsigned char *  _payload,
                    unsigned int     _payload_len,
                    int              _payload_valid,
                    framesyncstats_s _stats,
                    void *           _userdata)
{
    printf("*** callback, header %s, payload %s (%u bytes) ***\n",
        _header_valid  ? "pass" : "FAIL",
        _payload_valid ? "pass" : "FAIL",
        _payload_len);
    framesyncstats_print(&_stats);

    // update counters and return
    unsigned int * counters = (unsigned int*)_userdata;
    counters[0] ++;                                   // number of frames detected
    counters[1] += _payload_valid ?            1 : 0; // number of frames valid
    counters[2] += _payload_valid ? _payload_len : 0; // number of bytes valid
    return 0;
}

int main(int argc, char *argv[])
{
    // options
    float fc            = 0.237f;       // center frequency
    float rate          = 7.2280;       // over-sampling rate
    char  filename[256] = "signal.dat"; // input filename
    if (argc > 1)
        strncpy(filename,argv[1],256);

    // create flexframesync object
    unsigned int counters[3] = {0,0,0}; // detects, frames, bytes
    flexframesync fs = flexframesync_create(callback,counters);

    // create oscillator for mixing to baseband
    nco_crcf nco = nco_crcf_create(LIQUID_VCO);
    nco_crcf_set_frequency(nco,2*3.14159f*fc);

    // create efficient resampler object
    msresamp_crcf resamp = msresamp_crcf_create(1.0f/rate, 80.0f);

    // receive frame in blocks
    unsigned int  buf_len = 256;
    float complex buf_file  [buf_len];  // buffer: samples from file
    float complex buf_nco   [buf_len];  // buffer: mixer output
    float complex buf_resamp[buf_len];  // buffer: resampler output

    // try to load input file
    FILE * fid_dat = fopen(filename,"r");
    if (fid_dat == NULL) {
        fprintf(stderr,"error: could not open '%s' for reading\n", filename);
        exit(1);
    }

    // process entire file
    while (!feof(fid_dat)) {
        // read up to buf_len samples from buffer
        int rc = fread(buf_file, sizeof(float complex), buf_len, fid_dat);

        // mix down to baseband
        nco_crcf_mix_block_down(nco, buf_file, buf_nco, rc);

        // resample
        unsigned int num_resamp = 0;
        msresamp_crcf_execute(resamp, buf_nco, rc, buf_resamp, &num_resamp);

        // push through frame synchronizer
        flexframesync_execute(fs, buf_resamp, num_resamp);
    }
    fclose(fid_dat);

    // destroy allocated objects
    flexframesync_destroy(fs);
    nco_crcf_destroy     (nco);
    msresamp_crcf_destroy(resamp);

    // print results
    printf("detected %u frames (%u valid), %u total valid bytes\n",
            counters[0], counters[1], counters[2]);
    return 0;
}

You may compile and run the program from the command line simply by typing make test . This should compile and run the test program and print information about the packets to the screen like this:


*** callback, header pass, payload pass (120 bytes) ***
    EVM                 :   -28.42033768 dB
    rssi                :   -40.02705383 dB
    carrier offset      :    -0.00075654 Fs
    num symbols         :   1496
    mod scheme          :   qpsk (2 bits/symbol)
    validity check      :   crc32
    fec (inner)         :   g2412
    fec (outer)         :   h128
*** callback, header pass, payload pass (120 bytes) ***
    EVM                 :   -28.57869339 dB
    rssi                :   -40.00438690 dB

...

detected 5 frames (5 valid), 600 total valid bytes

What's next?

I have over 40 active development branches in liquid-dsp containing new signal processing algorithms, interface clean-ups, and various improvements. My hope is to provide releases on a more regular schedule, as well as respond to the many pull requests, tickets, and comments against liquid. Furthermore, I have almost a dozen blog posts in the works on various DSP algorithms as well that I'm hoping to publish in the coming months.