Libm17

From M17 Foundation Wiki
Jump to navigation Jump to search

libm17 is a reference C library written by Wojciech Kaczmarski SP5WWP et al. It contains the whole M17 protocol's RF stack. It is released under GPL-2.0 license: GitHub repository.

Examples

Generating 100 stream frames

The example below uses libm17 to generate a stream of 100 frames with zero-valued payload.

Let's start with the basics - we need a few standard libraries to be able to run our code. We will also need the m17.h header file to access the libm17 functions:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "m17.h"

Next, we will need variables to hold the data used in the Link Setup Frame, an array of floats to hold the symbol stream, a symbol counter, and another array for the payload. How do we deal with the LSF? Luckily, libm17 offers a convenient structure for storing LSF contents: lsf_t. Stream array should be able to hold 100 frames worth of M17 - we have to keep in mind that the preamble, Link Setup Frame and the End of Transmission (EoT) marker also take some space (192 symbols each). Symbol counter is required to navigate through the stream variable - the code needs to know where to append next symbols. The payload part is simple - it always occupies 16 bytes (128 bits) per frame.

lsf_t lsf; 
float stream[SYM_PER_FRA*(2+100+1)];
uint32_t sym_cnt;
uint8_t payload[128/8];

All variables above are global, therefore we don't need to zero them out explicitly. This makes LSF contents meaningless, let's fill the structure with actual data! We will set the destination to @ALL, making the transmission targeted at anyone. The source can be any callsign, using N0CALL should be fine for now. We will also use a few handy hash-defines provided by the library, i.e. setting the Channel Access Number to 0 and setting the transmission's parameters: stream type, data in the payload (but not voice), META field holding extended callsign data, unsigned stream. The last parameter, NULL', tells the function to zero out the META field.

set_LSF(&lsf, "N0CALL", "@ALL", M17_TYPE_STREAM | M17_TYPE_DATA | M17_TYPE_CAN(0) | M17_TYPE_META_EXT_CALL | M17_TYPE_UNSIGNED, NULL);

Now that we have the LSF ready, let's start generating some symbols. We will start with the preamble - a 40ms warm-up symbol sequence.

gen_preamble(stream, &sym_cnt, PREAM_LSF);

Now it's time for the LSF:

gen_frame(&stream[sym_cnt], NULL, FRAME_LSF, &lsf, 0, 0);
sym_cnt+=SYM_PER_FRA;

Remember to increment the symbol counter, otherwise your previous symbols will get overwritten. When that's done, we can finally generate out 100 frames with zero-valued payload:

for(uint8_t i=0; i<99; i++)
{
   gen_frame(&stream[sym_cnt], payload, FRAME_STR, &lsf, i%6, i);
   sym_cnt+=SYM_PER_FRA;
}

gen_frame(&stream[sym_cnt], payload, FRAME_STR, &lsf, 99%6, 0x8000|99);
sym_cnt+=SYM_PER_FRA;

gen_frame() function expects the caller to pass two counters: the LICH Counter and the Frame Counter. Frame counter starts at 0 and increments per frame. The last frame's Counter value should have its most significant bit set to 1. Usually, the LICH counter is just Frame Counter modulo 6. The transmission ends with the End of Transmission marker:

gen_eot(stream, &sym_cnt);

Finally, we are ready to save our freshly generated M17 stream to a file and terminate the program:

FILE *fp = fopen("dump.raw", "wb");
fwrite(stream, sizeof(float), sym_cnt, fp);
fclose(fp);

return 0;

Let's combine all the code snippets together:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "m17.h"

//structure for the Link Setup Frame data
lsf_t lsf; 

//variable for holding the resulting symbol stream
float stream[SYM_PER_FRA*(2+100+1)];

//counter for symbols written to the 'stream' variable
uint32_t sym_cnt;

//128-bit payload. since it's a global variable - it gets initialized with zeros.
uint8_t payload[128/8];

int main(void)
{
   //set the LSF data
   set_LSF(&lsf, "N0CALL", "@ALL", M17_TYPE_STREAM | M17_TYPE_DATA | M17_TYPE_CAN(0) | M17_TYPE_META_EXT_CALL | M17_TYPE_UNSIGNED, NULL);

   //generate preamble symbols - 40ms - save symbols to 'stream'
   gen_preamble(stream, &sym_cnt, PREAM_LSF);

   //generate LSF frame - save symbols to 'stream'
   gen_frame(&stream[sym_cnt], NULL, FRAME_LSF, &lsf, 0, 0);
   sym_cnt+=SYM_PER_FRA;

   //generate stream frames from 0 to 98
   for(uint8_t i=0; i<99; i++)
   {
      gen_frame(&stream[sym_cnt], payload, FRAME_STR, &lsf, i%6, i);
      sym_cnt+=SYM_PER_FRA;
   }

   //generate symbols for the last frame
   //0x8000 marks the last frame
   gen_frame(&stream[sym_cnt], payload, FRAME_STR, &lsf, 99%6, 0x8000|99);
   sym_cnt+=SYM_PER_FRA;

   //generate the End of Transmission 40ms long marker - save symbols in the 'stream' variable
   gen_eot(stream, &sym_cnt);

   //dump all the generated symbols to a 'dump.raw' file
   FILE *fp = fopen("dump.raw", "wb");
   fwrite(stream, sizeof(float), sym_cnt, fp);
   fclose(fp);

   return 0;
}

The code above can be compiled with gcc:

gcc example.c -o example -lm17