Binary Packet Generator/Synchronizer (bpacket)

The bpacketgen and bpacketsync objects realize a pair of binary packet generator and synchronizer objects useful for streaming data applications. The bpacketgen object generates packets by encapsulating data using a packetizer object but adds a special bit sequence and header to the beginning of the packet. The bit sequence at the beginning of the packet allows the synchronizer to find it using a binary cross-correlator; the header includes information about how the packet is encoded, including the two levels of forward error-correction coding used, the validity check (e.g. cyclic redundancy check), and the length of the payload. The full packet is assembled according to [fig-framing-bpacket_structure] .

bpacket_structure.png

Figure [fig-framing-bpacket_structure]. Structure used for the bpacketgen and bpacketsync objects.

At the receiver the bpacketsync object correlates against the bit sequence looking for the beginning of the packet. It is important to realize that the receiver does not need to be byte-aligned as the packet synchronizer takes care of this internally. Once a packet has been found the packet synchronizer decodes the header to determine how the payload is to be decoded. The payload is decoded and the resulting data is passed to a callback function. The synchronizer compensates for the situation where all the bits are flipped (e.g. coherent BPSK with a phase offset of \(\pi\) radians). Because the packet's header includes information about how to decode the payload the synchronizer automatically reconfigures itself to the packet parameters without any additional specification by the user. This allows great flexibility adapting encoding parameters to dynamic channel environments.

bpacketgen interface

The functionality of the bpacket structure is split into two objects: the bpacketgen object generates the packets and runs on the transmit side of the link while the bpacketsync object synchronizes and decodes the packets and runs on the receive side of the link. Listed below is the full interface to the bpacketgen object.

  • bpacketgen_create(m,n,crc,fec0,fec1) creates and returns a bpacketgen object which accepts \(n\) uncoded input bytes and uses the specified CRC and bi-level FEC schemes. The first parameter ( \(m\) ) is reserved for future development and is currently ignored.
  • bpacketgen_recreate(q,m,n,crc,fec0,fec1) re-creates an existing bpacketgen object with new parameters.
  • bpacketgen_destroy(q) destroys an bpacketgen object, freeing all internally-allocated memory.
  • bpacketgen_print(q) prints the internal state of the bpacketgen object to the standard output.
  • bpacketgen_get_packet_len(q) returns the length in bytes of the fully-encoded packet.
  • bpacketgen_encode(q,*msg,*pkt) encodes the \(n\) -byte input message msg , storing the result in the encoded output packet pkt .

bpacketsync interface

As stated before, the bpacketsync runs on the receiver to synchronize to and decode the incoming packets. Listed below is the full interface to the bpacketsync object.

  • bpacketsync_create(m,callback,*userdata) creates and returns a bpacketsync object which invokes a user-defined callback function, passing to it a user-defined object pointer. The first parameter ( \(m\) ) is reserved for future development and is currently ignored.
  • bpacketsync_destroy(q) destroys an bpacketsync object, freeing all internally-allocated memory.
  • bpacketsync_print(q) prints the internal state of the bpacketsync object to the standard output.
  • bpacketsync_reset(q) resets the internal state of the object.
  • bpacketsync_execute(q,*bytes,n) runs the synchronizer on \(n\) bytes of received data.
  • bpacketsync_execute_byte(q,byte) runs the synchronizer on a single byte of received data.
  • bpacketsync_execute_sym(q,sym,bps) runs the synchronizer on a symbol with bps bits of information.
  • bpacketsync_execute_bit(q,bit) runs the synchronizer on a single bit.

The bpacketsync object has a callback function which has four arguments and looks like this:


int bpacketsync_callback(unsigned char *  _payload,
                         int              _payload_valid,
                         unsigned int     _payload_len,
                         void *           _userdata);

The callback is typically defined to be static and is passed to the instance of bpacketsync object when it is created.

  • _payload is a pointer to the decoded bytes of payload data. This pointer is not static and cannot be used after returning from the callback function. This means that it needs to be copied locally for you to retain the data.
  • _payload_valid is simply a flag to indicate if the payload passed its cyclic redundancy check (" 0 " means invalid, " 1 " means valid). If this flag is zero then the payload most likely has errors in it. Some applications are error tolerant and so it is possible that the payload data are still useful. Typically, though, the payload should be discarded and a re-transmission request should be issued.
  • _payload_len indicates the number of bytes in the _payload argument.
  • _userdata is a pointer that given to the bpacketsync object when it was created. This pointer is passed to the callback and can represent just about anything. Typically it points to another structure and is the method by which the decoded header and payload data are returned to the program outside of the callback.

Code example

Listed below is a basic example of of the interface to the bpacketgen and bpacketsync objects. For a detailed example program, see examples/bpacketsync_example.c under the main liquid project directory.


#include <liquid/liquid.h>

int callback(unsigned char *  _payload,
             int              _payload_valid,
             unsigned int     _payload_len,
             framesyncstats_s _stats,
             void *           _userdata)
{
    printf("callback invoked\n");
    return 0;
}

int main() {
    // options
    unsigned int n=64;                       // original data message length
    crc_scheme check = LIQUID_CRC_32;        // data integrity check
    fec_scheme fec0 = LIQUID_FEC_HAMMING128; // inner code
    fec_scheme fec1 = LIQUID_FEC_NONE;       // outer code

    // create packet generator and compute packet length
    bpacketgen pg = bpacketgen_create(0, n, check, fec0, fec1);
    unsigned int k = bpacketgen_get_packet_len(pg);

    // initialize arrays
    unsigned char msg_org[n];   // original message
    unsigned char msg_enc[k];   // encoded message
    unsigned char msg_dec[n];   // decoded message

    // create packet synchronizer
    bpacketsync ps = bpacketsync_create(0, callback, NULL);

    // initialize original data message
    unsigned int i;
    for (i=0; i<n; i++) msg_org[i] = rand() % 256;

    // encode packet
    bpacketgen_encode(pg, msg_org, msg_enc);

    // ... channel ...

    // push packet through synchronizer
    bpacketsync_execute(ps, msg_enc, k);

    // clean up allocated objects
    bpacketgen_destroy(pg);
    bpacketsync_destroy(ps);
}