hamnet70/impl/src/main.c

365 lines
12 KiB
C
Raw Normal View History

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <liquid/liquid.h>
2022-02-01 21:35:43 +01:00
#include "results.h"
#include "utils.h"
#include "packet_mod.h"
#include "config.h"
#include "preamble.h"
#include "transmission.h"
2022-02-01 21:35:43 +01:00
#include "correlator.h"
#include "freq_est.h"
2022-02-12 22:06:49 +01:00
#include "whitening.h"
2022-02-01 21:35:43 +01:00
typedef enum {
RX_STATE_ACQUISITION,
RX_STATE_HEADER,
RX_STATE_DATA,
} rx_state_t;
void print_complex_array(const char *varname, float complex const *array, size_t len)
{
printf("%s=np.array([%f%+fj", varname, crealf(array[0]), cimagf(array[0]));
for(size_t k = 1; k < len; k++) {
printf(", %f%+fj", crealf(array[k]), cimagf(array[k]));
}
printf("])\n");
}
2022-02-12 21:58:53 +01:00
void update_nco_pll(nco_crcf nco, float phase_error)
{
static const float pll_alpha = 0.05f; // phase adjustment factor
static const float pll_beta = (pll_alpha * pll_alpha) / 2.0f; // frequency adjustment factor
nco_crcf_adjust_phase(nco, pll_alpha * phase_error);
nco_crcf_adjust_frequency(nco, pll_beta * phase_error);
}
int main(void)
{
uint8_t msg_org[] = "Hello Liquid! This is the message to transmit. Hopefully it can be decoded correctly...";
2022-02-10 22:33:51 +01:00
uint8_t header[4];
2022-02-10 22:33:51 +01:00
fec hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
2022-02-12 21:58:53 +01:00
fec payload_fec = fec_create(PAYLOAD_CHANNEL_CODE, NULL);
2022-02-10 22:33:51 +01:00
modem hdr_demod = modem_create(HEADER_MODULATION);
2022-02-12 21:58:53 +01:00
modem payload_demod = modem_create(PAYLOAD_MODULATION);
channel_cccf channel = channel_cccf_create();
2022-02-12 21:58:53 +01:00
float snr = 7.0f;
channel_cccf_add_awgn(channel, -snr, snr);
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
2022-02-10 22:33:51 +01:00
// precalculate encoded header length in symbols
unsigned int hdr_len_enc_bytes = fec_get_enc_msg_length(HEADER_CHANNEL_CODE, sizeof(header));
unsigned int hdr_bps = modem_get_bps(hdr_demod);
unsigned int hdr_len_symbols = (hdr_len_enc_bytes * 8 + hdr_bps - 1) / hdr_bps;
packet_mod_ctx_t pmod;
packet_mod_init(&pmod);
packet_mod_set_data(&pmod, msg_org, sizeof(msg_org));
packet_mod_encode(&pmod);
packet_mod_modulate(&pmod);
packet_mod_add_header(&pmod);
packet_mod_add_preamble(&pmod);
size_t nsyms;
packet_mod_get_result_cf(&pmod, NULL, &nsyms); // determine number of symbols for allocation
float complex msg_mod[nsyms];
packet_mod_get_result_cf(&pmod, msg_mod, &nsyms); // get the data
size_t burst_len = 0;
float complex whole_burst[65536];
size_t len;
transmission_ctx_t transm;
transmission_init(&transm);
len = 65536-burst_len;
if(transmission_ramp_up(&transm, whole_burst+burst_len, &len) != OK) {
printf("Ramp-up requires %zd symbols at offset %zd.\n", len, burst_len);
return 1;
}
burst_len += len;
len = 65536-burst_len;
if(transmission_filter_packet(&transm, msg_mod, nsyms, whole_burst+burst_len, &len) != OK) {
printf("Packet requires %zd symbols at offset %zd.\n", len, burst_len);
return 1;
}
burst_len += len;
len = 65536-burst_len;
if(transmission_ramp_down(&transm, whole_burst+burst_len, &len) != OK) {
printf("Ramp-down requires %zd symbols at offset %zd.\n", len, burst_len);
return 1;
}
burst_len += len;
dump_array_cf(whole_burst, burst_len, 1.0f, "/tmp/tx.cpx");
// channel
2022-01-29 22:36:41 +01:00
float complex msg_received[burst_len];
2022-01-29 22:36:41 +01:00
//memcpy(msg_received, whole_burst, sizeof(whole_burst)); // no noise in channel
2022-01-29 22:36:41 +01:00
channel_cccf_execute_block(channel, whole_burst, burst_len, msg_received);
dump_array_cf(msg_received, burst_len, 1.0f, "/tmp/rx.cpx");
2022-02-10 22:33:51 +01:00
// create NCOs for carrier frequency compensation
nco_crcf carrier_nco = nco_crcf_create(LIQUID_NCO);
nco_crcf_set_frequency(carrier_nco, 0.00f);
nco_crcf_set_phase(carrier_nco, 0.0f);
2022-02-10 22:33:51 +01:00
nco_crcf carrier_fine_nco = nco_crcf_create(LIQUID_NCO);
nco_crcf_set_frequency(carrier_fine_nco, 0.00f);
nco_crcf_set_phase(carrier_fine_nco, 0.0f);
2022-01-29 22:36:41 +01:00
// create symbol synchronizer
symsync_crcf symsync = symsync_crcf_create_rnyquist(LIQUID_FIRFILT_RRC, RRC_SPS, RRC_DELAY, RRC_BETA, 32);
2022-01-29 22:36:41 +01:00
float complex symsync_out[burst_len];
unsigned int symsync_out_len = 0;
2022-02-10 22:33:51 +01:00
float complex symbols_cpx[16384];
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
2022-02-01 21:35:43 +01:00
// General receiver state
rx_state_t rx_state = RX_STATE_ACQUISITION;
// Correlator for preamble search
correlator_ctx_t preamble_correlator;
correlator_init(&preamble_correlator, preamble_get_symbols(), preamble_get_symbol_count());
for(unsigned int i = 0; i < burst_len; i++) {
// Mix the input signal with the carrier NCO, which oscillates at the
// frequency estimated so far.
float complex mixed_sample;
nco_crcf_step(carrier_nco);
nco_crcf_mix_down(carrier_nco, msg_received[i], &mixed_sample);
2022-02-01 21:35:43 +01:00
// run the timing synchronizer (works even with shifted frequency)
unsigned int out_len;
symsync_crcf_execute(symsync, &mixed_sample, 1, symsync_out + symsync_out_len, &out_len);
if(out_len != 0) {
float complex corr_out;
switch(rx_state) {
// Try to acquire packets by synchronizing the frequency
// (symbol-independent search) and correlating the preamble.
case RX_STATE_ACQUISITION:
// preamble search
corr_out = correlator_step(&preamble_correlator, symsync_out[symsync_out_len]);
if(cabsf(corr_out) > 0.5f * preamble_get_symbol_count()) {
// Preamble found!
printf("Preamble found at symbol %u: %.3f > %.3f\n", i/RRC_SPS, cabsf(corr_out), 0.5f * preamble_get_symbol_count());
float phase_offset;
float mean_frequency_error = correlator_get_mean_frequency_deviation(&preamble_correlator, FREQ_EST_L, &phase_offset);
printf("Phase offset: %.6f rad\n", phase_offset);
printf("Preamble frequency deviation: %.6f rad/symbol\n", mean_frequency_error);
// adjust the frequency and phase of the NCO with the estimations from the preamble
2022-02-10 22:33:51 +01:00
nco_crcf_set_frequency(carrier_fine_nco, mean_frequency_error / RRC_SPS * 0.5f);
nco_crcf_set_phase(carrier_fine_nco, phase_offset);
2022-02-10 22:33:51 +01:00
printf("New estimated carrier frequency: %.6f + %.6f\n", nco_crcf_get_frequency(carrier_nco), nco_crcf_get_frequency(carrier_fine_nco));
float complex input_history[preamble_get_symbol_count()];
correlator_get_input_history(&preamble_correlator, input_history);
printf("import numpy as np\n");
printf("import matplotlib.pyplot as pp\n");
print_complex_array("pre", preamble_get_symbols(), preamble_get_symbol_count());
print_complex_array("recv", input_history, preamble_get_symbol_count());
printf("pp.plot(np.angle(recv * pre.conj()))\n");
printf("pp.show()\n");
// receive and decode the header
rx_state = RX_STATE_HEADER;
2022-02-10 22:33:51 +01:00
symbol_counter = 0;
} else {
// preamble not found.
// Run the frequency estimator in blocks of FREQ_EST_L samples.
// This is an implementation that works with unknown BPSK symbols
// and therefore can be used during ramp-up and preamble.
if(((i/RRC_SPS) % FREQ_EST_L) == 0) {
float freq_est = freq_est_unknown_bpsk(symsync_out + symsync_out_len - FREQ_EST_L, FREQ_EST_L, NULL);
float freq_adjustment = (freq_est / RRC_SPS) * 0.3f;
nco_crcf_adjust_frequency(carrier_nco, freq_adjustment);
printf("Frequency adjustment: %.6f - carrier frequency: %.6f\n", freq_adjustment, nco_crcf_get_frequency(carrier_nco));
}
}
break;
case RX_STATE_HEADER:
2022-02-10 22:33:51 +01:00
nco_crcf_mix_down(carrier_fine_nco, symsync_out[symsync_out_len], &mixed_sample);
if(symbol_counter < hdr_len_symbols) {
unsigned int sym_demod;
modem_demodulate(hdr_demod, mixed_sample, &sym_demod);
2022-02-12 21:58:53 +01:00
float phase_error = modem_get_demodulator_phase_error(hdr_demod);
printf("Sym: %d; Phase error: %f\n", sym_demod, phase_error);
update_nco_pll(carrier_fine_nco, phase_error);
2022-02-10 22:33:51 +01:00
symbols_cpx[symbol_counter] = mixed_sample;
symbols_int[symbol_counter] = sym_demod;
symbol_counter++;
}
if(symbol_counter == hdr_len_symbols) {
unsigned int nsyms;
unsigned char header_enc[4];
ERR_CHECK_LIQUID(liquid_repack_bytes(
symbols_int, modem_get_bps(hdr_demod), hdr_len_symbols,
header_enc, 8, sizeof(header_enc), &nsyms));
fec_decode(hdr_fec, sizeof(header), header_enc, header);
payload_len_bytes = ((uint16_t)header[0] << 8) | header[1];
payload_crc = ((uint16_t)header[2] << 8) | header[3];
2022-02-12 21:58:53 +01:00
payload_len_enc_bytes = fec_get_enc_msg_length(PAYLOAD_CHANNEL_CODE, payload_len_bytes);
payload_len_symbols = (payload_len_enc_bytes * 8 + payload_bps - 1) / payload_bps;
2022-02-10 22:33:51 +01:00
printf("=== DECODED HEADER ===\n");
printf("Payload length: %u symbols, %u bytes encoded, %u bytes decoded\n", payload_len_symbols, payload_len_enc_bytes, payload_len_bytes);
2022-02-10 22:33:51 +01:00
printf("CRC16: 0x%04x\n", payload_crc);
printf("======================\n");
dump_array_cf(symbols_cpx, symbol_counter, 1.0f, "/tmp/hdr.cpx");
2022-02-10 22:33:51 +01:00
rx_state = RX_STATE_DATA;
symbol_counter = 0;
2022-02-10 22:33:51 +01:00
}
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);
2022-02-12 21:58:53 +01:00
float phase_error = modem_get_demodulator_phase_error(payload_demod);
printf("Sym: %d; Phase error: %f\n", sym_demod, phase_error);
update_nco_pll(carrier_fine_nco, phase_error);
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);
2022-02-12 22:06:49 +01:00
// de-whiten the data
whitening_apply_in_place(payload, payload_len_bytes);
2022-02-12 21:58:53 +01:00
int valid = crc_validate_message(PAYLOAD_CRC_SCHEME, payload, payload_len_bytes, payload_crc);
payload[payload_len_bytes] = '\0';
2022-02-12 21:58:53 +01:00
printf("=== DECODED PAYLOAD (valid: %d) ===\n", valid);
printf("%s\n", payload);
2022-02-12 21:58:53 +01:00
printf("==================================\n");
dump_array_cf(symbols_cpx, symbol_counter, 1.0f, "/tmp/payload.cpx");
rx_state = RX_STATE_ACQUISITION;
}
break;
}
}
symsync_out_len += out_len;
}
dump_array_cf(symsync_out, symsync_out_len, 1.0f, "/tmp/synced.cpx");
2022-01-29 22:36:41 +01:00
#if 0
// demodulate
unsigned int bps = modem_get_bps(payload_demod);
unsigned char msg_demod_syms[nsyms];
unsigned char msg_demod[k+1];
for(size_t i = 0; i < nsyms; i++) {
unsigned int symbol;
modem_demodulate(payload_demod, msg_received[i], &symbol);
msg_demod_syms[i] = symbol;
}
unsigned int received_bytes;
liquid_repack_bytes(msg_demod_syms, bps, nsyms, msg_demod, 8, sizeof(msg_demod), &received_bytes);
//assert(received_bytes == k);
// decode
uint8_t msg_dec[sizeof(msg_org)];
//memcpy(msg_dec, msg_enc, sizeof(msg_dec));
fec_decode(q, sizeof(msg_dec), msg_demod, msg_dec);
// compare original to decoded message
for(size_t i = 0; i < sizeof(msg_org); i++)
{
printf("%02x => %02x", msg_org[i], msg_dec[i]);
if(msg_org[i] != msg_dec[i]) {
printf(" <<< !!!\n");
} else {
printf("\n");
}
}
unsigned int bit_errors = count_bit_errors_array(msg_org, msg_dec, sizeof(msg_org));
printf("%u bit errors detected.\n", bit_errors);
#endif
nco_crcf_destroy(carrier_nco);
symsync_crcf_destroy(symsync);
2022-02-10 22:33:51 +01:00
fec_destroy(hdr_fec);
modem_destroy(payload_demod);
2022-02-10 22:33:51 +01:00
modem_destroy(hdr_demod);
channel_cccf_destroy(channel);
printf("Done.\n");
}