/* * SPDX-License-Identifier: GPL-3.0-or-later * * Copyright (C) 2024 Thomas Kolb */ #include #include #include #include #include "logger.h" #include "layer1/rx.h" #include "layer2/packet_structs.h" #include "config.h" #define RESULT_CHECK(stmt) { \ result_t res = stmt; \ if(res != OK) { \ LOG(LVL_ERR, "Error %d in %s:%d!", res, __FILE__, __LINE__); \ exit(1); \ } \ } #define CHUNKSIZE_INPUT 256000 #define CHUNKSIZE_RF (CHUNKSIZE_INPUT/2) #define CHUNKSIZE_BB (CHUNKSIZE_RF/SDR_OVERSAMPLING) #define JSONLOGGER 0 #if JSONLOGGER #include "jsonlogger.h" #endif static rx_stats_t m_rx_stats; static result_t sdr_rf_to_baseband(nco_crcf nco, firdecim_crcf decim, const float complex *rf_samples, size_t nrf, float complex *bb_samples, size_t *nbb) { if((*nbb * SDR_OVERSAMPLING) < nrf) { LOG(LVL_ERR, "sdr_rf_to_baseband: result would not fit in output: %zd * %d < %zd", *nbb, SDR_OVERSAMPLING, nrf); return ERR_SIZE; } *nbb = nrf / SDR_OVERSAMPLING; for(size_t i = 0; i < *nbb; i++) { float complex tmp[SDR_OVERSAMPLING]; assert(i*SDR_OVERSAMPLING < nrf); nco_crcf_mix_block_down(nco, (complex float*)(rf_samples + i * SDR_OVERSAMPLING), tmp, SDR_OVERSAMPLING); firdecim_crcf_execute(decim, tmp, bb_samples + i); } return OK; } void cb_rx(rx_evt_t evt, const layer1_rx_t *rx, uint8_t *packet_data, size_t packet_len) { (void)packet_data; (void)packet_len; (void)rx; unsigned int data_size; switch(evt) { case RX_EVT_DECODE_FAILED: //LOG(LVL_WARN, "Received a message of %zu bytes, but decoding failed.", packet_len); //LOG(LVL_INFO, "=== FAILED PAYLOAD ==="); //hexdump(packet_data, packet_len); //LOG(LVL_INFO, "======================="); m_rx_stats.failed_decodes++; break; case RX_EVT_HEADER_ERROR: m_rx_stats.header_errors++; break; case RX_EVT_PACKET_RECEIVED: //LOG(LVL_INFO, "A message of %zu bytes was decoded successfully.", packet_len); //LOG(LVL_INFO, "=== DECODED PAYLOAD (%4zu bytes) ===", packet_len); //hexdump(packet_data, packet_len < 64 ? packet_len : 64); //LOG(LVL_INFO, "===================================="); data_size = packet_len - crc_sizeof_key(PAYLOAD_CRC_SCHEME); // CRC check if(!crc_check_key(PAYLOAD_CRC_SCHEME, packet_data, data_size)) { LOG(LVL_ERR, "Payload CRC check failed!"); } else { m_rx_stats.successful_decodes++; } // decode and dump the layer 2 header layer2_packet_header_t header; if(!layer2_decode_packet_header(packet_data, data_size, &header)) { LOG(LVL_ERR, "Header decoding failed!"); break; } layer2_dump_packet_header(LVL_DUMP, &header); break; case RX_EVT_PREAMBLE_FOUND: //LOG(LVL_INFO, "Found preamble!"); m_rx_stats.preambles_found++; break; case RX_EVT_PACKET_DEBUG_INFO_COMPLETE: #if JSONLOGGER jsonlogger_log_rx_packet_info(&rx->packet_debug_info); #endif break; } #if JSONLOGGER jsonlogger_log_rx_stats(&m_rx_stats); #endif } int main(int argc, char **argv) { logger_init(); if(argc < 2) { LOG(LVL_FATAL, "Not enough arguments!"); LOG(LVL_INFO, "usage: %s ", argv[0]); LOG(LVL_INFO, "dump-file: HackRF dump in 8-bit signed interleaved I/Q format."); return 1; } layer1_rx_t rx; FILE *inputfile; float rbuf[CHUNKSIZE_INPUT]; // ** Initialize ** #if JSONLOGGER if(!jsonlogger_init("jsonlog_test.fifo")) { LOG(LVL_FATAL, "Could not initialize JSON logger."); return EXIT_FAILURE; } #endif firdecim_crcf decim = firdecim_crcf_create_kaiser(SDR_OVERSAMPLING, 9, 60.0f); nco_crcf rx_nco = nco_crcf_create(LIQUID_NCO); nco_crcf_set_frequency(rx_nco, 2 * 3.14159 * (SDR_RX_IF_SHIFT + 31e3) / SDR_RX_SAMPLING_RATE); inputfile = fopen(argv[1], "rb"); if(!inputfile) { LOG(LVL_FATAL, "Could not open input file!"); return 1; } RESULT_CHECK(layer1_rx_init(&rx, cb_rx)); // ** Process packets ** size_t nread; while((nread = fread(rbuf, sizeof(float), CHUNKSIZE_INPUT, inputfile)) != 0) { assert(nread % 2 == 0); LOG(LVL_DEBUG, "Read %zu samples.", nread/2); // ** Receive signal ** float complex rf_samples[CHUNKSIZE_RF]; float complex bb_samples[CHUNKSIZE_BB]; size_t n_rf_samples = CHUNKSIZE_RF; size_t n_bb_samples = CHUNKSIZE_BB; for(size_t iout = 0; iout < CHUNKSIZE_RF; iout++) { //rf_samples[iout] = 0.0078125f * (rbuf[2*iout + 0] + I * rbuf[2*iout + 1]); rf_samples[iout] = rbuf[2*iout + 0] + I * rbuf[2*iout + 1]; } RESULT_CHECK(sdr_rf_to_baseband(rx_nco, decim, rf_samples, n_rf_samples, bb_samples, &n_bb_samples)); RESULT_CHECK(layer1_rx_process(&rx, bb_samples, n_bb_samples)); LOG(LVL_INFO, "Receiver statistics:"); LOG(LVL_INFO, " Preambles found: %8zd", m_rx_stats.preambles_found); LOG(LVL_INFO, " Successful decodes: %8zd (%6.2f %%)", m_rx_stats.successful_decodes, m_rx_stats.successful_decodes * 100.0f / m_rx_stats.preambles_found); LOG(LVL_INFO, " Header errors: %8zd (%6.2f %%)", m_rx_stats.header_errors, m_rx_stats.header_errors * 100.0f / m_rx_stats.preambles_found); LOG(LVL_INFO, " Failed decodes: %8zd (%6.2f %%)", m_rx_stats.failed_decodes, m_rx_stats.failed_decodes * 100.0f / m_rx_stats.preambles_found); } // ** Cleanup ** layer1_rx_shutdown(&rx); #if JSONLOGGER jsonlogger_shutdown(); #endif LOG(LVL_INFO, "Done."); logger_shutdown(); }