From 947615f5cdd54764610e4259d5fe8368b69a97c7 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Fri, 11 Feb 2022 17:56:26 +0100 Subject: [PATCH] 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. --- impl/src/main.c | 59 +++++++++++-- impl/src/packet_demod.c | 120 +++++++++++++++++++++++++++ impl/src/packet_demod.h | 179 ++++++++++++++++++++++++++++++++++++++++ impl/src/packet_mod.c | 6 +- impl/src/packet_mod.h | 1 + 5 files changed, 355 insertions(+), 10 deletions(-) create mode 100644 impl/src/packet_demod.c create mode 100644 impl/src/packet_demod.h diff --git a/impl/src/main.c b/impl/src/main.c index 00f2370..0a8f110 100644 --- a/impl/src/main.c +++ b/impl/src/main.c @@ -37,13 +37,14 @@ int main(void) uint8_t header[4]; 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 demod = modem_create(MODULATION); + modem payload_demod = modem_create(MODULATION); channel_cccf channel = channel_cccf_create(); - float snr = 20.0f; + float snr = 12.0f; channel_cccf_add_awgn(channel, -snr, snr); channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f); @@ -124,6 +125,12 @@ int main(void) unsigned char symbols_int[16384]; 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 // General receiver state rx_state_t rx_state = RX_STATE_ACQUISITION; @@ -220,20 +227,56 @@ int main(void) fec_decode(hdr_fec, sizeof(header), header_enc, header); - uint16_t payload_len = ((uint16_t)header[0] << 8) | header[1]; - uint16_t payload_crc = ((uint16_t)header[2] << 8) | header[3]; + payload_len_bytes = ((uint16_t)header[0] << 8) | header[1]; + 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("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("======================\n"); dump_array_cf(symbols_cpx, symbol_counter, 1.0f, "/tmp/hdr.cpx"); + rx_state = RX_STATE_DATA; + symbol_counter = 0; } break; 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; } } @@ -245,7 +288,7 @@ int main(void) #if 0 // 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[k+1]; @@ -253,7 +296,7 @@ int main(void) for(size_t i = 0; i < nsyms; i++) { unsigned int symbol; - modem_demodulate(demod, msg_received[i], &symbol); + modem_demodulate(payload_demod, msg_received[i], &symbol); msg_demod_syms[i] = symbol; } @@ -288,7 +331,7 @@ int main(void) fec_destroy(hdr_fec); - modem_destroy(demod); + modem_destroy(payload_demod); modem_destroy(hdr_demod); channel_cccf_destroy(channel); diff --git a/impl/src/packet_demod.c b/impl/src/packet_demod.c new file mode 100644 index 0000000..354775f --- /dev/null +++ b/impl/src/packet_demod.c @@ -0,0 +1,120 @@ +#include + +#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; +} diff --git a/impl/src/packet_demod.h b/impl/src/packet_demod.h new file mode 100644 index 0000000..186b5bc --- /dev/null +++ b/impl/src/packet_demod.h @@ -0,0 +1,179 @@ +#ifndef PACKET_DEMOD +#define PACKET_DEMOD + +#include +#include + +#include + +#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 diff --git a/impl/src/packet_mod.c b/impl/src/packet_mod.c index 0f380f9..3cf28ad 100644 --- a/impl/src/packet_mod.c +++ b/impl/src/packet_mod.c @@ -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); ctx->length = length; + ctx->raw_data_len = length; + ctx->state = DATA_RAW; return OK; @@ -153,8 +155,8 @@ result_t packet_mod_add_header(packet_mod_ctx_t *ctx) uint8_t header[4]; // set length - header[0] = (ctx->length >> 8) & 0xFF; - header[1] = (ctx->length >> 0) & 0xFF; + header[0] = (ctx->raw_data_len >> 8) & 0xFF; + header[1] = (ctx->raw_data_len >> 0) & 0xFF; // set raw data CRC header[2] = (ctx->raw_data_crc >> 8) & 0xFF; header[3] = (ctx->raw_data_crc >> 0) & 0xFF; diff --git a/impl/src/packet_mod.h b/impl/src/packet_mod.h index 03d1b59..4909a46 100644 --- a/impl/src/packet_mod.h +++ b/impl/src/packet_mod.h @@ -32,6 +32,7 @@ typedef struct fec hdr_fec; uint16_t raw_data_crc; + uint16_t raw_data_len; size_t length; } packet_mod_ctx_t;