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/whitening.c
|
||||
src/layer1/whitening.h
|
||||
src/layer1/modcod.c
|
||||
src/layer1/modcod.h
|
||||
)
|
||||
|
||||
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->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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue