Initial implementation of packet modulation
Not really tested yet…
This commit is contained in:
parent
0b237a47cb
commit
9ba470adeb
16
impl/src/config.h
Normal file
16
impl/src/config.h
Normal 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
97
impl/src/main.c
Normal 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
251
impl/src/packet_mod.c
Normal 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
51
impl/src/packet_mod.h
Normal 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
37
impl/src/preamble.c
Normal 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
10
impl/src/preamble.h
Normal 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
27
impl/src/results.h
Normal 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
66
impl/src/utils.c
Normal 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
27
impl/src/utils.h
Normal 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
|
Loading…
Reference in a new issue