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:
parent
0ed7109335
commit
1787d0d351
|
@ -29,6 +29,8 @@ set(sources
|
||||||
src/layer1/rx.h
|
src/layer1/rx.h
|
||||||
src/layer1/whitening.c
|
src/layer1/whitening.c
|
||||||
src/layer1/whitening.h
|
src/layer1/whitening.h
|
||||||
|
src/layer1/modcod.c
|
||||||
|
src/layer1/modcod.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|
34
impl/src/layer1/modcod.c
Normal file
34
impl/src/layer1/modcod.c
Normal 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
23
impl/src/layer1/modcod.h
Normal 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
|
|
@ -14,12 +14,11 @@ result_t packet_mod_init(packet_mod_ctx_t *ctx)
|
||||||
|
|
||||||
ctx->length = 0;
|
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_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
||||||
ctx->hdr_modem = modem_create(HEADER_MODULATION);
|
ctx->hdr_modem = modem_create(HEADER_MODULATION);
|
||||||
|
|
||||||
|
ctx->modcod = MODCOD_DEFAULT;
|
||||||
|
|
||||||
ctx->state = NOT_STARTED;
|
ctx->state = NOT_STARTED;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -50,9 +49,6 @@ result_t packet_mod_free(packet_mod_ctx_t *ctx)
|
||||||
ctx->state = NOT_STARTED;
|
ctx->state = NOT_STARTED;
|
||||||
ctx->length = 0;
|
ctx->length = 0;
|
||||||
|
|
||||||
fec_destroy(ctx->fec);
|
|
||||||
modem_destroy(ctx->modem);
|
|
||||||
|
|
||||||
fec_destroy(ctx->hdr_fec);
|
fec_destroy(ctx->hdr_fec);
|
||||||
modem_destroy(ctx->hdr_modem);
|
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)
|
result_t packet_mod_set_data(packet_mod_ctx_t *ctx, const unsigned char *data, size_t length)
|
||||||
{
|
{
|
||||||
if(ctx->state != NOT_STARTED) {
|
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);
|
whitening_apply_in_place(ctx->pkt_bytes, ctx->length);
|
||||||
|
|
||||||
// apply the FEC
|
// 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);
|
free(ctx->pkt_bytes);
|
||||||
|
|
||||||
|
@ -119,22 +129,27 @@ result_t packet_mod_modulate(packet_mod_ctx_t *ctx)
|
||||||
return ERR_INVALID_STATE;
|
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 int nsyms = (ctx->length * 8 + bps/2) / bps;
|
||||||
|
|
||||||
unsigned char msg_sym_idcs[nsyms];
|
unsigned char msg_sym_idcs[nsyms];
|
||||||
|
|
||||||
float complex *msg_mod = malloc(sizeof(float complex) * nsyms);
|
float complex *msg_mod = malloc(sizeof(float complex) * nsyms);
|
||||||
if(!msg_mod) {
|
if(!msg_mod) {
|
||||||
|
modem_destroy(payload_modem);
|
||||||
return ERR_NO_MEM;
|
return ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_CHECK_LIQUID(liquid_repack_bytes(ctx->pkt_bytes, 8, ctx->length, msg_sym_idcs, bps, nsyms, &nsyms));
|
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++) {
|
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);
|
free(ctx->pkt_bytes);
|
||||||
ctx->pkt_bytes = NULL;
|
ctx->pkt_bytes = NULL;
|
||||||
|
|
||||||
|
@ -162,7 +177,8 @@ result_t packet_mod_add_header(packet_mod_ctx_t *ctx)
|
||||||
uint8_t header[4];
|
uint8_t header[4];
|
||||||
|
|
||||||
// set length
|
// 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;
|
header[1] = (ctx->raw_data_len >> 0) & 0xFF;
|
||||||
// set raw data CRC
|
// set raw data CRC
|
||||||
header[2] = (ctx->raw_data_crc >> 8) & 0xFF;
|
header[2] = (ctx->raw_data_crc >> 8) & 0xFF;
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
#include "modcod.h"
|
||||||
|
|
||||||
#include "results.h"
|
#include "results.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
|
@ -25,15 +27,14 @@ typedef struct
|
||||||
|
|
||||||
packet_mod_state_t state;
|
packet_mod_state_t state;
|
||||||
|
|
||||||
modem modem;
|
|
||||||
fec fec;
|
|
||||||
|
|
||||||
modem hdr_modem;
|
modem hdr_modem;
|
||||||
fec hdr_fec;
|
fec hdr_fec;
|
||||||
|
|
||||||
uint16_t raw_data_crc;
|
uint16_t raw_data_crc;
|
||||||
uint16_t raw_data_len;
|
uint16_t raw_data_len;
|
||||||
|
|
||||||
|
uint8_t modcod;
|
||||||
|
|
||||||
size_t length;
|
size_t length;
|
||||||
} packet_mod_ctx_t;
|
} 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);
|
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.
|
/*!\brief Set the raw packet data.
|
||||||
*
|
*
|
||||||
* In this step, the CRC of the data is calculated.
|
* In this step, the CRC of the data is calculated.
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#define HEADER_SIZE_BYTES 4
|
#define HEADER_SIZE_BYTES 4
|
||||||
#define FREQ_EST_L 8
|
#define FREQ_EST_L 8
|
||||||
|
|
||||||
#define SHOW_DEBUG_LOG 0
|
#define SHOW_DEBUG_LOG 1
|
||||||
|
|
||||||
#if SHOW_DEBUG_LOG
|
#if SHOW_DEBUG_LOG
|
||||||
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
|
# 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);
|
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_len_bytes = (((uint16_t)header[0] << 8) | header[1]) & 0x07FF;
|
||||||
rx->payload_crc = ((uint16_t)header[2] << 8) | header[3];
|
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_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;
|
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,
|
symbols_int, modem_get_bps(rx->payload_demod), rx->payload_len_symbols,
|
||||||
payload_enc, 8, sizeof(payload_enc), &nsyms));
|
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
|
// de-whiten the data
|
||||||
whitening_apply_in_place(payload, rx->payload_len_bytes);
|
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 {
|
} else {
|
||||||
rx->callback(RX_EVT_CHECKSUM_ERROR, payload, rx->payload_len_bytes);
|
rx->callback(RX_EVT_CHECKSUM_ERROR, payload, rx->payload_len_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modem_destroy(rx->payload_demod);
|
||||||
rx->state = RX_STATE_ACQUISITION;
|
rx->state = RX_STATE_ACQUISITION;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -237,11 +249,9 @@ result_t layer1_rx_init(layer1_rx_t *rx, rx_callback_t callback)
|
||||||
|
|
||||||
// forward error correction objects
|
// forward error correction objects
|
||||||
rx->hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
rx->hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
||||||
rx->payload_fec = fec_create(PAYLOAD_CHANNEL_CODE, NULL);
|
|
||||||
|
|
||||||
// demodulators
|
// demodulators
|
||||||
rx->hdr_demod = modem_create(HEADER_MODULATION);
|
rx->hdr_demod = modem_create(HEADER_MODULATION);
|
||||||
rx->payload_demod = modem_create(PAYLOAD_MODULATION);
|
|
||||||
|
|
||||||
// symbol timing synchronizer
|
// symbol timing synchronizer
|
||||||
rx->symsync = symsync_crcf_create_rnyquist(LIQUID_FIRFILT_RRC, RRC_SPS, RRC_DELAY, RRC_BETA, 32);
|
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);
|
nco_crcf_destroy(rx->carrier_fine_nco);
|
||||||
|
|
||||||
fec_destroy(rx->hdr_fec);
|
fec_destroy(rx->hdr_fec);
|
||||||
fec_destroy(rx->payload_fec);
|
|
||||||
|
|
||||||
modem_destroy(rx->hdr_demod);
|
modem_destroy(rx->hdr_demod);
|
||||||
modem_destroy(rx->payload_demod);
|
|
||||||
|
|
||||||
symsync_crcf_destroy(rx->symsync);
|
symsync_crcf_destroy(rx->symsync);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
#include "modcod.h"
|
||||||
#include "correlator.h"
|
#include "correlator.h"
|
||||||
|
|
||||||
#include "results.h"
|
#include "results.h"
|
||||||
|
@ -32,7 +33,6 @@ typedef struct
|
||||||
|
|
||||||
// Forward error correction objects
|
// Forward error correction objects
|
||||||
fec hdr_fec;
|
fec hdr_fec;
|
||||||
fec payload_fec;
|
|
||||||
|
|
||||||
// Demodulators
|
// Demodulators
|
||||||
modem hdr_demod;
|
modem hdr_demod;
|
||||||
|
@ -59,6 +59,7 @@ typedef struct
|
||||||
uint16_t payload_len_bytes;
|
uint16_t payload_len_bytes;
|
||||||
uint16_t payload_crc;
|
uint16_t payload_crc;
|
||||||
uint16_t payload_len_enc_bytes;
|
uint16_t payload_len_enc_bytes;
|
||||||
|
modcod_t modcod;
|
||||||
} layer1_rx_t;
|
} layer1_rx_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue