layer1: implement per-packet modulation/coding

This allows a higher layer to adjust data rate based on the current
channel conditions. For example, the SNR could be measured and if it
becomes too low for the current modulation/code, a lower-order
modulation or stronger code could be used.
This commit is contained in:
Thomas Kolb 2022-02-17 21:39:18 +01:00
parent 0ed7109335
commit 1787d0d351
7 changed files with 117 additions and 22 deletions

View File

@ -29,6 +29,8 @@ set(sources
src/layer1/rx.h
src/layer1/whitening.c
src/layer1/whitening.h
src/layer1/modcod.c
src/layer1/modcod.h
)
include_directories(

34
impl/src/layer1/modcod.c Normal file
View File

@ -0,0 +1,34 @@
#include <stddef.h>
#include "modcod.h"
modem modcod_create_modem(modcod_t modcod)
{
static const modulation_scheme mod_mapping[MODCOD_LIST_SIZE] = {
LIQUID_MODEM_QAM16,
LIQUID_MODEM_QPSK,
};
if(modcod < MODCOD_LIST_SIZE) {
return modem_create(mod_mapping[modcod]);
} else {
return NULL;
}
}
fec modcod_create_fec(modcod_t modcod)
{
static const fec_scheme fec_mapping[MODCOD_LIST_SIZE] = {
LIQUID_FEC_CONV_V27P34,
LIQUID_FEC_CONV_V27P34,
};
if(modcod < MODCOD_LIST_SIZE) {
return fec_create(fec_mapping[modcod], NULL);
} else {
return NULL;
}
}

23
impl/src/layer1/modcod.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef LAYER1_MODCOD_H
#define LAYER1_MODCOD_H
#include <liquid/liquid.h>
// up to 32 ModCods are supported. Do not reuse indices, as that will break
// backwards compatibility! Only append new ModCods at the end of the list!
typedef enum
{
MODCOD_16QAM_CONV_V27P34,
MODCOD_QPSK_CONV_V27P34,
MODCOD_LIST_SIZE
} modcod_t;
#define MODCOD_DEFAULT MODCOD_16QAM_CONV_V27P34
modem modcod_create_modem(modcod_t modcod);
fec modcod_create_fec(modcod_t modcod);
#define MODCOD_IS_VALID(modcod) ((modcod) < MODCOD_LIST_SIZE)
#endif // LAYER1_MODCOD_H

View File

@ -14,12 +14,11 @@ result_t packet_mod_init(packet_mod_ctx_t *ctx)
ctx->length = 0;
ctx->fec = fec_create(PAYLOAD_CHANNEL_CODE, NULL);
ctx->modem = modem_create(PAYLOAD_MODULATION);
ctx->hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
ctx->hdr_modem = modem_create(HEADER_MODULATION);
ctx->modcod = MODCOD_DEFAULT;
ctx->state = NOT_STARTED;
return OK;
@ -50,9 +49,6 @@ result_t packet_mod_free(packet_mod_ctx_t *ctx)
ctx->state = NOT_STARTED;
ctx->length = 0;
fec_destroy(ctx->fec);
modem_destroy(ctx->modem);
fec_destroy(ctx->hdr_fec);
modem_destroy(ctx->hdr_modem);
@ -60,6 +56,18 @@ result_t packet_mod_free(packet_mod_ctx_t *ctx)
}
result_t packet_mod_set_modcod(packet_mod_ctx_t *ctx, modcod_t modcod)
{
if(ctx->state != NOT_STARTED) {
return ERR_INVALID_STATE;
}
ctx->modcod = modcod;
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) {
@ -100,7 +108,9 @@ result_t packet_mod_encode(packet_mod_ctx_t *ctx)
whitening_apply_in_place(ctx->pkt_bytes, ctx->length);
// apply the FEC
ERR_CHECK_LIQUID(fec_encode(ctx->fec, ctx->length, ctx->pkt_bytes, enc_msg));
fec payload_fec = modcod_create_fec(ctx->modcod);
ERR_CHECK_LIQUID(fec_encode(payload_fec, ctx->length, ctx->pkt_bytes, enc_msg));
fec_destroy(payload_fec);
free(ctx->pkt_bytes);
@ -119,22 +129,27 @@ result_t packet_mod_modulate(packet_mod_ctx_t *ctx)
return ERR_INVALID_STATE;
}
unsigned int bps = modem_get_bps(ctx->modem);
modem payload_modem = modcod_create_modem(ctx->modcod);
unsigned int bps = modem_get_bps(payload_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) {
modem_destroy(payload_modem);
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]));
ERR_CHECK_LIQUID(modem_modulate(payload_modem, msg_sym_idcs[i], &msg_mod[i]));
}
modem_destroy(payload_modem);
free(ctx->pkt_bytes);
ctx->pkt_bytes = NULL;
@ -162,7 +177,8 @@ result_t packet_mod_add_header(packet_mod_ctx_t *ctx)
uint8_t header[4];
// set length
header[0] = (ctx->raw_data_len >> 8) & 0xFF;
header[0] = (ctx->raw_data_len >> 8) & 0x07;
header[0] |= ctx->modcod << 3;
header[1] = (ctx->raw_data_len >> 0) & 0xFF;
// set raw data CRC
header[2] = (ctx->raw_data_crc >> 8) & 0xFF;

View File

@ -6,6 +6,8 @@
#include <liquid/liquid.h>
#include "modcod.h"
#include "results.h"
typedef enum
@ -25,15 +27,14 @@ typedef struct
packet_mod_state_t state;
modem modem;
fec fec;
modem hdr_modem;
fec hdr_fec;
uint16_t raw_data_crc;
uint16_t raw_data_len;
uint8_t modcod;
size_t length;
} packet_mod_ctx_t;
@ -52,6 +53,16 @@ result_t packet_mod_init(packet_mod_ctx_t *ctx);
*/
result_t packet_mod_free(packet_mod_ctx_t *ctx);
/*!\brief Set the modulation and code to use for the next packet.
*
* Must be called before setting the raw data.
*
* \param[inout] ctx The context to use for this operation.
* \param[in] modcod The modulation/code to use.
* \returns An result code (see results.h).
*/
result_t packet_mod_set_modcod(packet_mod_ctx_t *ctx, modcod_t modcod);
/*!\brief Set the raw packet data.
*
* In this step, the CRC of the data is calculated.

View File

@ -12,7 +12,7 @@
#define HEADER_SIZE_BYTES 4
#define FREQ_EST_L 8
#define SHOW_DEBUG_LOG 0
#define SHOW_DEBUG_LOG 1
#if SHOW_DEBUG_LOG
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
@ -150,10 +150,18 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
fec_decode(rx->hdr_fec, sizeof(header), header_enc, header);
uint16_t payload_bps = modem_get_bps(rx->payload_demod);
rx->payload_len_bytes = (((uint16_t)header[0] << 8) | header[1]) & 0x07FF;
rx->payload_crc = ((uint16_t)header[2] << 8) | header[3];
rx->modcod = header[0] >> 3;
// check the received information
if(!MODCOD_IS_VALID(rx->modcod)) {
rx->state = RX_STATE_ACQUISITION;
break;
}
rx->payload_demod = modcod_create_modem(rx->modcod);
uint16_t payload_bps = modem_get_bps(rx->payload_demod);
rx->payload_len_enc_bytes = fec_get_enc_msg_length(PAYLOAD_CHANNEL_CODE, rx->payload_len_bytes);
rx->payload_len_symbols = (rx->payload_len_enc_bytes * 8 + payload_bps - 1) / payload_bps;
@ -194,7 +202,9 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
symbols_int, modem_get_bps(rx->payload_demod), rx->payload_len_symbols,
payload_enc, 8, sizeof(payload_enc), &nsyms));
fec_decode(rx->payload_fec, rx->payload_len_bytes, payload_enc, payload);
fec payload_fec = modcod_create_fec(rx->modcod);
fec_decode(payload_fec, rx->payload_len_bytes, payload_enc, payload);
fec_destroy(payload_fec);
// de-whiten the data
whitening_apply_in_place(payload, rx->payload_len_bytes);
@ -209,6 +219,8 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
} else {
rx->callback(RX_EVT_CHECKSUM_ERROR, payload, rx->payload_len_bytes);
}
modem_destroy(rx->payload_demod);
rx->state = RX_STATE_ACQUISITION;
}
break;
@ -237,11 +249,9 @@ result_t layer1_rx_init(layer1_rx_t *rx, rx_callback_t callback)
// forward error correction objects
rx->hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
rx->payload_fec = fec_create(PAYLOAD_CHANNEL_CODE, NULL);
// demodulators
rx->hdr_demod = modem_create(HEADER_MODULATION);
rx->payload_demod = modem_create(PAYLOAD_MODULATION);
// symbol timing synchronizer
rx->symsync = symsync_crcf_create_rnyquist(LIQUID_FIRFILT_RRC, RRC_SPS, RRC_DELAY, RRC_BETA, 32);
@ -264,10 +274,8 @@ result_t layer1_rx_shutdown(layer1_rx_t *rx)
nco_crcf_destroy(rx->carrier_fine_nco);
fec_destroy(rx->hdr_fec);
fec_destroy(rx->payload_fec);
modem_destroy(rx->hdr_demod);
modem_destroy(rx->payload_demod);
symsync_crcf_destroy(rx->symsync);

View File

@ -3,6 +3,7 @@
#include <liquid/liquid.h>
#include "modcod.h"
#include "correlator.h"
#include "results.h"
@ -32,7 +33,6 @@ typedef struct
// Forward error correction objects
fec hdr_fec;
fec payload_fec;
// Demodulators
modem hdr_demod;
@ -59,6 +59,7 @@ typedef struct
uint16_t payload_len_bytes;
uint16_t payload_crc;
uint16_t payload_len_enc_bytes;
modcod_t modcod;
} layer1_rx_t;