Initial implementation of packet modulation

Not really tested yet…
This commit is contained in:
Thomas Kolb 2021-10-17 19:26:38 +02:00
parent 0b237a47cb
commit 9ba470adeb
9 changed files with 582 additions and 0 deletions

16
impl/src/config.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <liquid/liquid.h>
#define CHANNEL_CODE LIQUID_FEC_CONV_V27P34
#define MODULATION LIQUID_MODEM_QAM16
#define HEADER_CHANNEL_CODE LIQUID_FEC_NONE
#define HEADER_MODULATION LIQUID_MODEM_QPSK
#define PREAMBLE_MSEQ_M 6
#define PREAMBLE_MSEQ_POLY LIQUID_MSEQUENCE_GENPOLY_M6
#define PREAMBLE_MSEQ_INIT 0x00000001
#endif // CONFIG_H

97
impl/src/main.c Normal file
View file

@ -0,0 +1,97 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <liquid/liquid.h>
#include "utils.h"
#include "packet_mod.h"
#include "config.h"
#include "preamble.h"
int main(void)
{
uint8_t msg_org[] = "Hello Liquid! This is the message to transmit. Hopefully it can be decoded correctly...";
fec q = fec_create(CHANNEL_CODE, NULL);
modem demod = modem_create(MODULATION);
channel_cccf channel = channel_cccf_create();
float snr = 20.0f;
channel_cccf_add_awgn(channel, -snr, snr);
unsigned int k = fec_get_enc_msg_length(CHANNEL_CODE, sizeof(msg_org));
packet_mod_ctx_t pmod;
packet_mod_init(&pmod);
packet_mod_set_data(&pmod, msg_org, sizeof(msg_org));
packet_mod_encode(&pmod);
packet_mod_modulate(&pmod);
packet_mod_add_header(&pmod);
packet_mod_add_preamble(&pmod);
size_t nsyms;
packet_mod_get_result_cf(&pmod, NULL, &nsyms); // determine number of symbols for allocation
float complex msg_mod[nsyms];
packet_mod_get_result_cf(&pmod, msg_mod, &nsyms); // get the data
// channel
float complex msg_received[nsyms];
nsyms = nsyms - 63 - 8;
//memcpy(msg_received, msg_mod, sizeof(msg_received)); // no noise in channel
dump_array_cf(msg_mod, nsyms, 1.0f, "/tmp/tx.cpx");
channel_cccf_execute_block(channel, msg_mod + 63 + 8, nsyms, msg_received);
dump_array_cf(msg_received, nsyms, 1.0f, "/tmp/rx.cpx");
// demodulate
unsigned int bps = modem_get_bps(demod);
unsigned char msg_demod_syms[nsyms];
unsigned char msg_demod[k+1];
for(size_t i = 0; i < nsyms; i++) {
unsigned int symbol;
modem_demodulate(demod, msg_received[i], &symbol);
msg_demod_syms[i] = symbol;
}
unsigned int received_bytes;
liquid_repack_bytes(msg_demod_syms, bps, nsyms, msg_demod, 8, k+1, &received_bytes);
//assert(received_bytes == k);
// decode
uint8_t msg_dec[sizeof(msg_org)];
//memcpy(msg_dec, msg_enc, sizeof(msg_dec));
fec_decode(q, sizeof(msg_dec), msg_demod, msg_dec);
// compare original to decoded message
for(size_t i = 0; i < sizeof(msg_org); i++)
{
printf("%02x => %02x", msg_org[i], msg_dec[i]);
if(msg_org[i] != msg_dec[i]) {
printf(" <<< !!!\n");
} else {
printf("\n");
}
}
unsigned int bit_errors = count_bit_errors_array(msg_org, msg_dec, sizeof(msg_org));
printf("%u bit errors detected.\n", bit_errors);
fec_destroy(q);
modem_destroy(demod);
channel_cccf_destroy(channel);
printf("Done.\n");
}

251
impl/src/packet_mod.c Normal file
View file

@ -0,0 +1,251 @@
#include <string.h>
#include "preamble.h"
#include "config.h"
#include "packet_mod.h"
result_t packet_mod_init(packet_mod_ctx_t *ctx)
{
ctx->pkt_bytes = NULL;
ctx->pkt_symbols = NULL;
ctx->length = 0;
ctx->fec = fec_create(CHANNEL_CODE, NULL);
ctx->modem = modem_create(MODULATION);
ctx->state = NOT_STARTED;
return OK;
}
result_t packet_mod_free(packet_mod_ctx_t *ctx)
{
switch(ctx->state) {
case DATA_RAW:
case DATA_CODED:
free(ctx->pkt_bytes);
ctx->pkt_bytes = NULL;
break;
case DATA_MODULATED:
case HEADER_ADDED:
case PREAMBLE_ADDED:
free(ctx->pkt_symbols);
ctx->pkt_symbols = NULL;
break;
case NOT_STARTED:
// nothing to do
break;
}
ctx->state = NOT_STARTED;
ctx->length = 0;
fec_destroy(ctx->fec);
modem_destroy(ctx->modem);
return OK;
}
result_t packet_mod_set_data(packet_mod_ctx_t *ctx, const unsigned char *data, size_t length)
{
if(ctx->state != NOT_STARTED) {
return ERR_INVALID_STATE;
}
ctx->pkt_bytes = malloc(length);
if(!ctx->pkt_bytes) {
return ERR_NO_MEM;
}
memcpy(ctx->pkt_bytes, data, length);
ctx->length = length;
ctx->state = DATA_RAW;
return OK;
}
result_t packet_mod_encode(packet_mod_ctx_t *ctx)
{
if(ctx->state != DATA_RAW) {
return ERR_INVALID_STATE;
}
unsigned int enc_length = fec_get_enc_msg_length(CHANNEL_CODE, ctx->length);
unsigned char *enc_msg = malloc(enc_length);
if(!enc_msg) {
return ERR_NO_MEM;
}
ERR_CHECK_LIQUID(fec_encode(ctx->fec, ctx->length, ctx->pkt_bytes, enc_msg));
free(ctx->pkt_bytes);
ctx->pkt_bytes = enc_msg;
ctx->length = enc_length;
ctx->state = DATA_CODED;
return OK;
}
result_t packet_mod_modulate(packet_mod_ctx_t *ctx)
{
if(!((ctx->state == DATA_RAW) || (ctx->state == DATA_CODED))) {
return ERR_INVALID_STATE;
}
unsigned int bps = modem_get_bps(ctx->modem);
unsigned int nsyms = (ctx->length * 8 + bps/2) / bps;
unsigned char msg_sym_idcs[nsyms];
float complex *msg_mod = malloc(sizeof(float complex) * nsyms);
if(!msg_mod) {
return ERR_NO_MEM;
}
ERR_CHECK_LIQUID(liquid_repack_bytes(ctx->pkt_bytes, 8, ctx->length, msg_sym_idcs, bps, nsyms, &nsyms));
for(size_t i = 0; i < nsyms; i++) {
ERR_CHECK_LIQUID(modem_modulate(ctx->modem, msg_sym_idcs[i], &msg_mod[i]));
}
free(ctx->pkt_bytes);
ctx->pkt_bytes = NULL;
ctx->pkt_symbols = msg_mod;
ctx->length = nsyms;
ctx->state = DATA_MODULATED;
return OK;
}
result_t packet_mod_add_header(packet_mod_ctx_t *ctx)
{
if(ctx->state != DATA_MODULATED) {
return ERR_INVALID_STATE;
}
if(ctx->length >= (1 << 16)) {
// packet length cannot be stored in the uint16_t in the header.
return ERR_SIZE;
}
// build the header. All field are transferred in network byte order (big endian).
uint8_t header[4];
// set length
header[0] = (ctx->length >> 8) & 0xFF;
header[1] = (ctx->length >> 0) & 0xFF;
// set raw data CRC
header[2] = (ctx->raw_data_crc >> 8) & 0xFF;
header[3] = (ctx->raw_data_crc >> 0) & 0xFF;
// the header is coded and modulated differently than the data.
fec hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
modem hdr_modem = modem_create(HEADER_MODULATION);
// encode the header
unsigned int hdr_enc_length = fec_get_enc_msg_length(HEADER_CHANNEL_CODE, ctx->length);
unsigned char header_encoded[hdr_enc_length];
ERR_CHECK_LIQUID(fec_encode(hdr_fec, sizeof(header), header, header_encoded));
// modulate the header
unsigned int bps = modem_get_bps(hdr_modem);
unsigned int nsyms = (hdr_enc_length * 8 + bps/2) / bps;
unsigned char header_sym_idcs[nsyms];
float complex header_mod[nsyms];
ERR_CHECK_LIQUID(liquid_repack_bytes(header_encoded, 8, hdr_enc_length, header_sym_idcs, bps, nsyms, &nsyms));
for(size_t i = 0; i < nsyms; i++) {
ERR_CHECK_LIQUID(modem_modulate(hdr_modem, header_sym_idcs[i], &header_mod[i]));
}
// concatenate the modulated header and message
size_t packet_length_with_header = ctx->length + nsyms;
float complex *packet_with_header = malloc(sizeof(float complex) * packet_length_with_header);
if(!packet_with_header) {
return ERR_NO_MEM;
}
memcpy(packet_with_header, header_mod, sizeof(float complex) * nsyms);
memcpy(packet_with_header+nsyms, ctx->pkt_symbols, sizeof(float complex) * ctx->length);
// replace the data in the context struct
free(ctx->pkt_symbols);
ctx->pkt_symbols = packet_with_header;
ctx->length = packet_length_with_header;
ctx->state = HEADER_ADDED;
return OK;
}
result_t packet_mod_add_preamble(packet_mod_ctx_t *ctx)
{
if(ctx->state != HEADER_ADDED) {
return ERR_INVALID_STATE;
}
// concatenate the preamble with the message
size_t pre_length = preamble_get_symbol_count();
size_t packet_length_with_preamble = ctx->length + pre_length;
float complex *packet_with_preamble = malloc(sizeof(float complex) * packet_length_with_preamble);
if(!packet_with_preamble) {
return ERR_NO_MEM;
}
memcpy(packet_with_preamble, preamble_get_symbols(), sizeof(float complex) * pre_length);
memcpy(packet_with_preamble+pre_length, ctx->pkt_symbols, sizeof(float complex) * ctx->length);
// replace the data in the context struct
free(ctx->pkt_symbols);
ctx->pkt_symbols = packet_with_preamble;
ctx->length = packet_length_with_preamble;
ctx->state = PREAMBLE_ADDED;
return OK;
}
result_t packet_mod_get_result_cf(packet_mod_ctx_t *ctx, float complex *data, size_t *length)
{
if(!((ctx->state == DATA_MODULATED) || (ctx->state == HEADER_ADDED) || (ctx->state == PREAMBLE_ADDED))) {
return ERR_INVALID_STATE;
}
if(!data) {
// data is NULL, so we only return the required length
*length = ctx->length;
return OK;
}
if(*length < ctx->length) {
return ERR_NO_MEM;
}
*length = ctx->length;
memcpy(data, ctx->pkt_symbols, *length * sizeof(float complex));
return OK;
}

51
impl/src/packet_mod.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef PACKET_MOD
#define PACKET_MOD
#include <stdlib.h>
#include <complex.h>
#include <liquid/liquid.h>
#include "results.h"
typedef enum
{
NOT_STARTED,
DATA_RAW,
DATA_CODED,
DATA_MODULATED,
HEADER_ADDED,
PREAMBLE_ADDED,
} packet_mod_state_t;
typedef struct
{
unsigned char *pkt_bytes;
float complex *pkt_symbols;
packet_mod_state_t state;
modem modem;
fec fec;
uint16_t raw_data_crc;
size_t length;
} packet_mod_ctx_t;
result_t packet_mod_init(packet_mod_ctx_t *ctx);
result_t packet_mod_free(packet_mod_ctx_t *ctx);
result_t packet_mod_set_data(packet_mod_ctx_t *ctx, const unsigned char *data, size_t length);
result_t packet_mod_encode(packet_mod_ctx_t *ctx);
result_t packet_mod_modulate(packet_mod_ctx_t *ctx);
result_t packet_mod_add_header(packet_mod_ctx_t *ctx);
result_t packet_mod_add_preamble(packet_mod_ctx_t *ctx);
result_t packet_mod_dump(packet_mod_ctx_t *ctx, const char *filename);
result_t packet_mod_get_result_b(packet_mod_ctx_t *ctx, unsigned char *data, size_t *length);
result_t packet_mod_get_result_cf(packet_mod_ctx_t *ctx, float complex *data, size_t *length);
#endif // PACKET_MOD

37
impl/src/preamble.c Normal file
View file

@ -0,0 +1,37 @@
#include <stdbool.h>
#include "config.h"
#include "preamble.h"
#define PREAMBLE_LEN ((1 << PREAMBLE_MSEQ_M) - 1)
static float complex symbol_cache[PREAMBLE_LEN];
static bool symbol_cache_initialized = false;
const float complex* preamble_get_symbols(void)
{
if(!symbol_cache_initialized) {
msequence mseq = msequence_create(
PREAMBLE_MSEQ_M,
PREAMBLE_MSEQ_POLY,
PREAMBLE_MSEQ_INIT);
for(unsigned i = 0; i < PREAMBLE_LEN; i++) {
unsigned int bit = msequence_advance(mseq);
symbol_cache[i] = bit ? 1.0f : -1.0f;
}
msequence_destroy(mseq);
symbol_cache_initialized = true;
}
return symbol_cache;
}
size_t preamble_get_symbol_count(void)
{
return PREAMBLE_LEN;
}

10
impl/src/preamble.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef PREAMBLE_H
#define PREAMBLE_H
#include <stdlib.h>
#include <complex.h>
const float complex* preamble_get_symbols(void);
size_t preamble_get_symbol_count(void);
#endif // PREAMBLE_H

27
impl/src/results.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef RESULTS_H
#define RESULTS_H
typedef enum {
OK,
ERR_INVALID_STATE,
ERR_NO_MEM,
ERR_SIZE,
ERR_LIQUID,
} result_t;
#ifdef DEBUG_LIQUID
#include <stdio.h>
# define ERR_CHECK_LIQUID(call) \
do { \
liquid_error_code result; \
if((result = (call)) != LIQUID_OK) { \
fprintf(stderr, "Liquid call failed in %s:%d: %s\n", __FILE__, __LINE__, liquid_error_info(result)); \
return ERR_LIQUID; \
} \
} while(0);
#else
# define ERR_CHECK_LIQUID(call) if((call) != LIQUID_OK) { return ERR_LIQUID; }
#endif
#endif // RESULTS_H

66
impl/src/utils.c Normal file
View file

@ -0,0 +1,66 @@
#include <stdio.h>
#include "utils.h"
bool dump_array_cf(const float complex *data, size_t n, float T, const char *filename)
{
FILE *f = fopen(filename, "w");
if(!f) {
return false;
}
size_t written = fwrite("CPX_", 1, 4, f);
if(written != 4) {
goto err_close;
}
written = fwrite(&T, sizeof(T), 1, f);
if(written != 1) {
goto err_close;
}
written = fwrite(data, sizeof(data[0]), n, f);
if(written != n) {
goto err_close;
}
fclose(f);
return true;
err_close:
fclose(f);
return false;
}
bool dump_array_f(const float *data, size_t n, float T, const char *filename)
{
FILE *f = fopen(filename, "w");
if(!f) {
return false;
}
size_t written = fwrite("FLT_", 1, 4, f);
if(written != 4) {
goto err_close;
}
written = fwrite(&T, sizeof(T), 1, f);
if(written != 1) {
goto err_close;
}
written = fwrite(data, sizeof(data[0]), n, f);
if(written != n) {
goto err_close;
}
fclose(f);
return true;
err_close:
fclose(f);
return false;
}

27
impl/src/utils.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef UTILS_H
#define UTILS_H
#include <stdlib.h>
#include <stdbool.h>
#include <liquid/liquid.h>
/*! Dump a array of complex numbers.
*
* \param data Pointer to the data to dump.
* \param n Number of data points to dump.
* \param T The sampling interval of the data (set to 1.0 if not relevant).
* \param filename The file to write to.
*/
bool dump_array_cf(const float complex *data, size_t n, float T, const char *filename);
/*! Dump a array of real numbers.
*
* \param data Pointer to the data to dump.
* \param n Number of data points to dump.
* \param T The sampling interval of the data (set to 1.0 if not relevant).
* \param filename The file to write to.
*/
bool dump_array_f(const float *data, size_t n, float T, const char *filename);
#endif // UTILS_H