#include #include #include #include #include #include "results.h" #include "utils.h" #include "packet_mod.h" #include "config.h" #include "preamble.h" #include "transmission.h" #include "correlator.h" #include "freq_est.h" 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"); } int main(void) { uint8_t msg_org[] = "Hello Liquid! This is the message to transmit. Hopefully it can be decoded correctly..."; fec q = fec_create(CHANNEL_CODE, NULL); modem demod = modem_create(MODULATION); channel_cccf channel = channel_cccf_create(); float snr = 20.0f; channel_cccf_add_awgn(channel, -snr, snr); channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f); 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 float complex msg_received[burst_len]; //memcpy(msg_received, whole_burst, sizeof(whole_burst)); // no noise in channel channel_cccf_execute_block(channel, whole_burst, burst_len, msg_received); dump_array_cf(msg_received, burst_len, 1.0f, "/tmp/rx.cpx"); // create NCO 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); // create symbol synchronizer symsync_crcf symsync = symsync_crcf_create_rnyquist(LIQUID_FIRFILT_RRC, RRC_SPS, RRC_DELAY, RRC_BETA, 32); float complex symsync_out[burst_len]; unsigned int symsync_out_len = 0; #define FREQ_EST_L 8 // 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); // 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 nco_crcf_adjust_frequency(carrier_nco, mean_frequency_error / RRC_SPS * 0.5f); nco_crcf_adjust_phase(carrier_nco, phase_offset); printf("New estimated carrier frequency: %.6f\n", nco_crcf_get_frequency(carrier_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; } 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: case RX_STATE_DATA: break; } } symsync_out_len += out_len; } dump_array_cf(symsync_out, symsync_out_len, 1.0f, "/tmp/synced.cpx"); #if 0 // demodulate unsigned int bps = modem_get_bps(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(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); fec_destroy(q); modem_destroy(demod); channel_cccf_destroy(channel); printf("Done.\n"); }