First complete RX chain
Decoding of the test message works down to ~10 dB SNR. Still missing: - Phase tracking during header and payload. - CRC calculation and check. - Data whitening.
This commit is contained in:
parent
042c05f9f3
commit
947615f5cd
|
@ -37,13 +37,14 @@ int main(void)
|
||||||
uint8_t header[4];
|
uint8_t header[4];
|
||||||
|
|
||||||
fec hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
fec hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
||||||
|
fec payload_fec = fec_create(CHANNEL_CODE, NULL);
|
||||||
|
|
||||||
modem hdr_demod = modem_create(HEADER_MODULATION);
|
modem hdr_demod = modem_create(HEADER_MODULATION);
|
||||||
modem demod = modem_create(MODULATION);
|
modem payload_demod = modem_create(MODULATION);
|
||||||
|
|
||||||
channel_cccf channel = channel_cccf_create();
|
channel_cccf channel = channel_cccf_create();
|
||||||
|
|
||||||
float snr = 20.0f;
|
float snr = 12.0f;
|
||||||
channel_cccf_add_awgn(channel, -snr, snr);
|
channel_cccf_add_awgn(channel, -snr, snr);
|
||||||
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
|
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
|
||||||
|
|
||||||
|
@ -124,6 +125,12 @@ int main(void)
|
||||||
unsigned char symbols_int[16384];
|
unsigned char symbols_int[16384];
|
||||||
unsigned int symbol_counter = 0; // for demodulation
|
unsigned int symbol_counter = 0; // for demodulation
|
||||||
|
|
||||||
|
uint16_t payload_len_symbols;
|
||||||
|
uint16_t payload_len_bytes;
|
||||||
|
uint16_t payload_crc;
|
||||||
|
uint16_t payload_len_enc_bytes;
|
||||||
|
uint16_t payload_bps = modem_get_bps(payload_demod);
|
||||||
|
|
||||||
#define FREQ_EST_L 8
|
#define FREQ_EST_L 8
|
||||||
// General receiver state
|
// General receiver state
|
||||||
rx_state_t rx_state = RX_STATE_ACQUISITION;
|
rx_state_t rx_state = RX_STATE_ACQUISITION;
|
||||||
|
@ -220,20 +227,56 @@ int main(void)
|
||||||
|
|
||||||
fec_decode(hdr_fec, sizeof(header), header_enc, header);
|
fec_decode(hdr_fec, sizeof(header), header_enc, header);
|
||||||
|
|
||||||
uint16_t payload_len = ((uint16_t)header[0] << 8) | header[1];
|
payload_len_bytes = ((uint16_t)header[0] << 8) | header[1];
|
||||||
uint16_t payload_crc = ((uint16_t)header[2] << 8) | header[3];
|
payload_crc = ((uint16_t)header[2] << 8) | header[3];
|
||||||
|
|
||||||
|
payload_len_enc_bytes = fec_get_enc_msg_length(CHANNEL_CODE, payload_len_bytes);
|
||||||
|
payload_len_symbols = (payload_len_enc_bytes * 8 + payload_bps - 1) / payload_bps;
|
||||||
|
|
||||||
printf("=== DECODED HEADER ===\n");
|
printf("=== DECODED HEADER ===\n");
|
||||||
printf("Payload length: %u\n", payload_len);
|
printf("Payload length: %u symbols, %u bytes encoded, %u bytes decoded\n", payload_len_symbols, payload_len_enc_bytes, payload_len_bytes);
|
||||||
printf("CRC16: 0x%04x\n", payload_crc);
|
printf("CRC16: 0x%04x\n", payload_crc);
|
||||||
printf("======================\n");
|
printf("======================\n");
|
||||||
|
|
||||||
dump_array_cf(symbols_cpx, symbol_counter, 1.0f, "/tmp/hdr.cpx");
|
dump_array_cf(symbols_cpx, symbol_counter, 1.0f, "/tmp/hdr.cpx");
|
||||||
|
|
||||||
rx_state = RX_STATE_DATA;
|
rx_state = RX_STATE_DATA;
|
||||||
|
symbol_counter = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RX_STATE_DATA:
|
case RX_STATE_DATA:
|
||||||
|
nco_crcf_mix_down(carrier_fine_nco, symsync_out[symsync_out_len], &mixed_sample);
|
||||||
|
if(symbol_counter < payload_len_symbols) {
|
||||||
|
unsigned int sym_demod;
|
||||||
|
modem_demodulate(payload_demod, mixed_sample, &sym_demod);
|
||||||
|
printf("Sym: %d; Phase error: %f\n", sym_demod, modem_get_demodulator_phase_error(payload_demod));
|
||||||
|
|
||||||
|
symbols_cpx[symbol_counter] = mixed_sample;
|
||||||
|
symbols_int[symbol_counter] = sym_demod;
|
||||||
|
symbol_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(symbol_counter == payload_len_symbols) {
|
||||||
|
unsigned int nsyms;
|
||||||
|
unsigned char payload_enc[16384];
|
||||||
|
unsigned char payload[16384];
|
||||||
|
|
||||||
|
ERR_CHECK_LIQUID(liquid_repack_bytes(
|
||||||
|
symbols_int, modem_get_bps(payload_demod), payload_len_symbols,
|
||||||
|
payload_enc, 8, sizeof(payload_enc), &nsyms));
|
||||||
|
|
||||||
|
fec_decode(payload_fec, payload_len_bytes, payload_enc, payload);
|
||||||
|
|
||||||
|
payload[payload_len_bytes] = '\0';
|
||||||
|
|
||||||
|
printf("=== DECODED PAYLOAD ===\n");
|
||||||
|
printf("%s\n", payload);
|
||||||
|
printf("=======================\n");
|
||||||
|
|
||||||
|
dump_array_cf(symbols_cpx, symbol_counter, 1.0f, "/tmp/payload.cpx");
|
||||||
|
rx_state = RX_STATE_ACQUISITION;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +288,7 @@ int main(void)
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// demodulate
|
// demodulate
|
||||||
unsigned int bps = modem_get_bps(demod);
|
unsigned int bps = modem_get_bps(payload_demod);
|
||||||
|
|
||||||
unsigned char msg_demod_syms[nsyms];
|
unsigned char msg_demod_syms[nsyms];
|
||||||
unsigned char msg_demod[k+1];
|
unsigned char msg_demod[k+1];
|
||||||
|
@ -253,7 +296,7 @@ int main(void)
|
||||||
for(size_t i = 0; i < nsyms; i++) {
|
for(size_t i = 0; i < nsyms; i++) {
|
||||||
unsigned int symbol;
|
unsigned int symbol;
|
||||||
|
|
||||||
modem_demodulate(demod, msg_received[i], &symbol);
|
modem_demodulate(payload_demod, msg_received[i], &symbol);
|
||||||
msg_demod_syms[i] = symbol;
|
msg_demod_syms[i] = symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +331,7 @@ int main(void)
|
||||||
|
|
||||||
fec_destroy(hdr_fec);
|
fec_destroy(hdr_fec);
|
||||||
|
|
||||||
modem_destroy(demod);
|
modem_destroy(payload_demod);
|
||||||
modem_destroy(hdr_demod);
|
modem_destroy(hdr_demod);
|
||||||
|
|
||||||
channel_cccf_destroy(channel);
|
channel_cccf_destroy(channel);
|
||||||
|
|
120
impl/src/packet_demod.c
Normal file
120
impl/src/packet_demod.c
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "correlator.h"
|
||||||
|
#include "preamble.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "packet_demod.h"
|
||||||
|
|
||||||
|
result_t packet_demod_init(packet_demod_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
ctx->pkt_bytes = NULL;
|
||||||
|
ctx->pkt_symbols = NULL;
|
||||||
|
|
||||||
|
ctx->length = 0;
|
||||||
|
|
||||||
|
if(!correlator_init(&ctx->correlator, preamble_get_symbols(), preamble_get_symbol_count())) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->fec = fec_create(CHANNEL_CODE, NULL);
|
||||||
|
ctx->modem = modem_create(MODULATION);
|
||||||
|
|
||||||
|
ctx->hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
||||||
|
ctx->hdr_modem = modem_create(HEADER_MODULATION);
|
||||||
|
|
||||||
|
ctx->state = SEARCHING_PREAMBLE;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t packet_demod_free(packet_demod_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
switch(ctx->state) {
|
||||||
|
case DATA_DEMODULATED:
|
||||||
|
case DATA_DECODED:
|
||||||
|
free(ctx->pkt_bytes);
|
||||||
|
ctx->pkt_bytes = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYMBOLS_SET:
|
||||||
|
free(ctx->pkt_symbols);
|
||||||
|
ctx->pkt_symbols = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEARCHING_PREAMBLE:
|
||||||
|
case HEADER_PARSED:
|
||||||
|
// nothing to do
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->state = SEARCHING_PREAMBLE;
|
||||||
|
ctx->length = 0;
|
||||||
|
|
||||||
|
fec_destroy(ctx->fec);
|
||||||
|
modem_destroy(ctx->modem);
|
||||||
|
|
||||||
|
fec_destroy(ctx->hdr_fec);
|
||||||
|
modem_destroy(ctx->hdr_modem);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool packet_demod_search_preamble(packet_demod_ctx_t *ctx, float complex sample, float *magnitude)
|
||||||
|
{
|
||||||
|
float mag = cabsf(correlator_step(&ctx->correlator, sample));
|
||||||
|
|
||||||
|
if(magnitude != NULL) {
|
||||||
|
*magnitude = mag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (mag > ((float)preamble_get_symbol_count() / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t packet_demod_get_result_b(packet_demod_ctx_t *ctx, unsigned char *data, size_t *length)
|
||||||
|
{
|
||||||
|
if(!((ctx->state == DATA_DECODED) || (ctx->state == DATA_DEMODULATED))) {
|
||||||
|
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_bytes, *length * sizeof(unsigned char));
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t packet_demod_get_result_cf(packet_demod_ctx_t *ctx, float complex *data, size_t *length)
|
||||||
|
{
|
||||||
|
if(ctx->state != SYMBOLS_SET) {
|
||||||
|
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;
|
||||||
|
}
|
179
impl/src/packet_demod.h
Normal file
179
impl/src/packet_demod.h
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
#ifndef PACKET_DEMOD
|
||||||
|
#define PACKET_DEMOD
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
#include "correlator.h"
|
||||||
|
|
||||||
|
#include "results.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SEARCHING_PREAMBLE,
|
||||||
|
HEADER_PARSED,
|
||||||
|
SYMBOLS_SET,
|
||||||
|
DATA_DEMODULATED,
|
||||||
|
DATA_DECODED,
|
||||||
|
} packet_demod_state_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char *pkt_bytes;
|
||||||
|
float complex *pkt_symbols;
|
||||||
|
|
||||||
|
packet_demod_state_t state;
|
||||||
|
|
||||||
|
correlator_ctx_t correlator;
|
||||||
|
|
||||||
|
modem modem;
|
||||||
|
fec fec;
|
||||||
|
|
||||||
|
modem hdr_modem;
|
||||||
|
fec hdr_fec;
|
||||||
|
|
||||||
|
uint16_t raw_data_crc;
|
||||||
|
|
||||||
|
size_t length;
|
||||||
|
} packet_demod_ctx_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*!\brief Initialize the packet modulator context.
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to initialize.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_init(packet_demod_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Free all resources in the given context.
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to free.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_free(packet_demod_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Search for the preamble.
|
||||||
|
*
|
||||||
|
* Does a single correlation step and returns whether the correlation factor
|
||||||
|
* was high enough to indicate the presence of a preamble.
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to use for this operation.
|
||||||
|
* \param[in] sample The received sample.
|
||||||
|
* \param[out] magnitude The magnitude of the correlation (optional, may be NULL).
|
||||||
|
* \returns Whether the magnitude was high enough to indicate a received preamble.
|
||||||
|
*/
|
||||||
|
bool packet_demod_search_preamble(packet_demod_ctx_t *ctx, float complex sample, float *magnitude);
|
||||||
|
|
||||||
|
/*!\brief Set the synchronized packet samples for demodulation.
|
||||||
|
*
|
||||||
|
* This is can only be called after a header has been successfully parsed with
|
||||||
|
* \ref packet_demod_decode_header().
|
||||||
|
*
|
||||||
|
* The stored symbols can be retrieved for verification using \ref
|
||||||
|
* packet_demod_get_result_cf().
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to use for this operation.
|
||||||
|
* \param[in] symbols The complex symbols of the packet. The required
|
||||||
|
* number of symbols is determined by the last
|
||||||
|
* successful call to \ref
|
||||||
|
* packet_demod_decode_header().
|
||||||
|
* \param[in] length The length of the symbols array.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_set_symbols(packet_demod_ctx_t *ctx, const float complex *symbols, size_t length);
|
||||||
|
|
||||||
|
/*!\brief Decode the channel code of the demodulated symbols
|
||||||
|
*
|
||||||
|
* This can only be called after \ref packet_demod_demodulate().
|
||||||
|
*
|
||||||
|
* The resulting data can be retrieved for verification using \ref
|
||||||
|
* packet_demod_get_result_b().
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to use for this operation.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_decode(packet_demod_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Demodulate the packet data.
|
||||||
|
*
|
||||||
|
* This can only be called after \ref packet_demod_set_symbols().
|
||||||
|
*
|
||||||
|
* The resulting data can be retrieved for verification using \ref
|
||||||
|
* packet_demod_get_result_b().
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to use for this operation.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_demodulate(packet_demod_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Demodulate and decode the header.
|
||||||
|
*
|
||||||
|
* If the header is decoded successfully, the packet size and CRC are
|
||||||
|
* extracted. The CRC is stored in dhe packet demod context, the length is
|
||||||
|
* written to the location specified by \ref packet_length.
|
||||||
|
*
|
||||||
|
* This function works in any state and resets the demodulation chain if a
|
||||||
|
* header is successfully decoded.
|
||||||
|
*
|
||||||
|
* \param[inout] ctx The context to use for this operation.
|
||||||
|
* \param[in] symbols The complex header symbols. The length of this array
|
||||||
|
* must be at least the value returned by \ref
|
||||||
|
* packet_demod_get_header_length().
|
||||||
|
* \param[out] packet_length Will be set to the number of symbol required
|
||||||
|
* to decode the packet.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_decode_header(packet_demod_ctx_t *ctx, const float complex *symbols, size_t *packet_length);
|
||||||
|
|
||||||
|
/*!\brief Get the length of the header in symbols.
|
||||||
|
*
|
||||||
|
* \param[in] ctx The context to use for this operation.
|
||||||
|
* \param[out] header_length Will be set to the number of symbol required
|
||||||
|
* to decode the header.
|
||||||
|
* \returns An result code (see results.h).
|
||||||
|
*/
|
||||||
|
result_t packet_demod_get_header_length(packet_demod_ctx_t *ctx, size_t *header_length);
|
||||||
|
|
||||||
|
/*!\brief Get the result data as raw bytes.
|
||||||
|
*
|
||||||
|
* Raw bytes can be retrieved after \ref packet_demod_demodulate() and \ref
|
||||||
|
* packet_demod_decode(). In all other states, this returns ERR_INVALID_STATE.
|
||||||
|
*
|
||||||
|
* \param[in] ctx The context to use for this operation.
|
||||||
|
* \param[out] data A pointer to the memory location where the data should be written.
|
||||||
|
* \param[inout length A pointer to the data length (in array items). The
|
||||||
|
* value shall be set to the size of the data buffer.
|
||||||
|
* After the call, it is set to the required buffer size
|
||||||
|
* (if \ref data is NULL or the length is not sufficient)
|
||||||
|
* or the number of items actually written to \ref data.
|
||||||
|
* \retval OK If the data was copied successfully or \ref data was
|
||||||
|
* NULL. In the latter case, only \ref length is modified
|
||||||
|
* to the required buffer size.
|
||||||
|
* \retval ERR_NO_MEM If the \ref length was not sufficient for the data.
|
||||||
|
* \retval ERR_INVALID_STATE If byte data is unavailable in the current state.
|
||||||
|
*/
|
||||||
|
result_t packet_demod_get_result_b(packet_demod_ctx_t *ctx, unsigned char *data, size_t *length);
|
||||||
|
|
||||||
|
/*!\brief Get the result data as complex numbers.
|
||||||
|
*
|
||||||
|
* Complex numbers can be retrieved after \ref packet_demod_set_symbols(). In
|
||||||
|
* all other states, this returns ERR_INVALID_STATE.
|
||||||
|
*
|
||||||
|
* \param[in] ctx The context to use for this operation.
|
||||||
|
* \param[out] data A pointer to the memory location where the data should be written.
|
||||||
|
* \param[inout length A pointer to the data length (in array items). The
|
||||||
|
* value shall be set to the size of the data buffer.
|
||||||
|
* After the call, it is set to the required buffer size
|
||||||
|
* (if \ref data is NULL or the length is not sufficient)
|
||||||
|
* or the number of items actually written to \ref data.
|
||||||
|
* \retval OK If the data was copied successfully or \ref data was
|
||||||
|
* NULL. In the latter case, only \ref length is modified
|
||||||
|
* to the required buffer size.
|
||||||
|
* \retval ERR_NO_MEM If the \ref length was not sufficient for the data.
|
||||||
|
* \retval ERR_INVALID_STATE If byte data is unavailable in the current state.
|
||||||
|
*/
|
||||||
|
result_t packet_demod_get_result_cf(packet_demod_ctx_t *ctx, float complex *data, size_t *length);
|
||||||
|
|
||||||
|
#endif // PACKET_DEMOD
|
|
@ -72,6 +72,8 @@ result_t packet_mod_set_data(packet_mod_ctx_t *ctx, const unsigned char *data, s
|
||||||
memcpy(ctx->pkt_bytes, data, length);
|
memcpy(ctx->pkt_bytes, data, length);
|
||||||
ctx->length = length;
|
ctx->length = length;
|
||||||
|
|
||||||
|
ctx->raw_data_len = length;
|
||||||
|
|
||||||
ctx->state = DATA_RAW;
|
ctx->state = DATA_RAW;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -153,8 +155,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->length >> 8) & 0xFF;
|
header[0] = (ctx->raw_data_len >> 8) & 0xFF;
|
||||||
header[1] = (ctx->length >> 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;
|
||||||
header[3] = (ctx->raw_data_crc >> 0) & 0xFF;
|
header[3] = (ctx->raw_data_crc >> 0) & 0xFF;
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef struct
|
||||||
fec hdr_fec;
|
fec hdr_fec;
|
||||||
|
|
||||||
uint16_t raw_data_crc;
|
uint16_t raw_data_crc;
|
||||||
|
uint16_t raw_data_len;
|
||||||
|
|
||||||
size_t length;
|
size_t length;
|
||||||
} packet_mod_ctx_t;
|
} packet_mod_ctx_t;
|
||||||
|
|
Loading…
Reference in a new issue