#include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "options.h" #include "jsonlogger.h" #include "debug_structs.h" #include "layer1/tx.h" #include "layer1/rx.h" #include "layer2/tundev.h" #include "sdr/sdr.h" #include "config.h" #define RESULT_CHECK(stmt) { \ result_t res = stmt; \ if(res != OK) { \ fprintf(stderr, "Error %d in %s:%d!\n", res, __FILE__, __LINE__); \ exit(1); \ } \ } static int m_tunfd = -1; static bool m_running = true; static double next_tx_switch_time = 0.0; static rx_stats_t m_rx_stats; static void signal_handler(int signal, siginfo_t *info, void *ctx) { (void)signal; (void)info; (void)ctx; fprintf(stderr, "\nGracefully shutting down on signal %d.\n", signal); m_running = false; } static void block_tx_for(unsigned offset_ms) { next_tx_switch_time = get_hires_time() + (double)offset_ms * 0.001; } void print_complex_array(const char *varname, float complex const *array, size_t len) { fprintf(stderr, "%s=np.array([%f%+fj", varname, crealf(array[0]), cimagf(array[0])); for(size_t k = 1; k < len; k++) { fprintf(stderr, ", %f%+fj", crealf(array[k]), cimagf(array[k])); } fprintf(stderr, "])\n"); } void hexdump(const uint8_t *data, size_t len) { static const char lut[16] = "0123456789ABCDEF"; static const size_t BYTES_PER_LINE = 16; size_t pos = 0; while(pos < len) { size_t bytes_in_line = len - pos; if(bytes_in_line > BYTES_PER_LINE) { bytes_in_line = BYTES_PER_LINE; } for(size_t i = 0; i < BYTES_PER_LINE; i++) { if(i >= bytes_in_line) { fputs(" ", stderr); } else { uint8_t byte = data[pos+i]; fprintf(stderr, "%c%c ", lut[byte >> 4], lut[byte & 0x0F]); } } fputs(" ", stderr); for(size_t i = 0; i < bytes_in_line; i++) { uint8_t byte = data[pos+i]; if(isprint(byte)) { putc(byte, stderr); } else { putc('.', stderr); } } putc('\n', stderr); pos += bytes_in_line; } } void cb_rx(rx_evt_t evt, uint8_t *packet_data, size_t packet_len) { int ret; switch(evt) { case RX_EVT_CHECKSUM_ERROR: //fprintf(stderr, "Received a message of %zu bytes, but decoding failed.\n", packet_len); //fprintf(stderr, "=== FAILED PAYLOAD ===\n"); //hexdump(packet_data, packet_len); //fprintf(stderr, "=======================\n"); m_rx_stats.failed_decodes++; break; case RX_EVT_HEADER_ERROR: m_rx_stats.header_errors++; break; case RX_EVT_PACKET_RECEIVED: //fprintf(stderr, "A message of %zu bytes was decoded successfully.\n", packet_len); //fprintf(stderr, "=== DECODED PAYLOAD (%4zu bytes) ===\n", packet_len); //hexdump(packet_data, packet_len < 64 ? packet_len : 64); //fprintf(stderr, "====================================\n"); m_rx_stats.successful_decodes++; block_tx_for(TX_SWITCH_BACKOFF_END_OF_PACKET_MS); ret = write(m_tunfd, packet_data, packet_len); if(ret < 0) { perror("write"); } break; case RX_EVT_PREAMBLE_FOUND: //fprintf(stderr, "Found preamble!\n"); block_tx_for(TX_SWITCH_BACKOFF_PREAMBLE_MS); m_rx_stats.preambles_found++; break; } jsonlogger_log_rx_stats(&m_rx_stats); } static int debug_fd; static result_t transmit(sdr_ctx_t *sdr, const float complex *samples, size_t len) { size_t to_transmit_rf = len * SDR_OVERSAMPLING; float complex rf_samples[to_transmit_rf]; RESULT_CHECK(sdr_baseband_to_rf(sdr, samples, len, rf_samples, &to_transmit_rf)); result_t result = sdr_transmit(sdr, rf_samples, to_transmit_rf, 100000); fprintf(stderr, "t"); return result; } int main(int argc, char **argv) { layer1_tx_t tx; layer1_rx_t rx; sdr_ctx_t sdr; int parsed_options = options_parse(argc, argv); fprintf(stderr, "%d options parsed.\n", parsed_options); if(parsed_options < 0) { return EXIT_FAILURE; } if(!jsonlogger_init("jsonlog.fifo")) { fprintf(stderr, "Could not initialize JSON logger.\n"); return EXIT_FAILURE; } bool on_air = true; debug_fd = open("/tmp/dump.cf32", O_CREAT | O_WRONLY | O_TRUNC); // ** Initialize ** char devname[IFNAMSIZ] = "hamnet70"; m_tunfd = tundev_open(devname); if(m_tunfd < 0) { return 1; } RESULT_CHECK(sdr_init(&sdr)); RESULT_CHECK(layer1_tx_init(&tx)); RESULT_CHECK(layer1_rx_init(&rx, cb_rx)); // ** Set up signal handling struct sigaction term_action = {0}; term_action.sa_sigaction = signal_handler; if(sigaction(SIGTERM, &term_action, NULL) < 0) { perror("sigaction"); exit(EXIT_FAILURE); } if(sigaction(SIGINT, &term_action, NULL) < 0) { perror("sigaction"); exit(EXIT_FAILURE); } // ** Process packets ** struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = m_tunfd; pfd.events = POLLIN; // start in TX mode to work around SoapyHackRF not setting the correct frequency. RESULT_CHECK(sdr_start_tx(&sdr, 1)); unsigned rx_retries = 0; double old = get_hires_time(); size_t total_samples = 0; double next_stats_print_time = old + 0.5; while(m_running) { double now = get_hires_time(); if((now > next_tx_switch_time) && (on_air || !layer1_rx_is_busy(&rx))) { int ret = poll(&pfd, 1, 0); if(ret < 0) { perror("poll"); break; } else if(ret > 0) { // there is a packet to be read. // check free buffer space (50 ms required corresponding to 5000 baseband symbols) size_t buffer_free_space_samples = sdr_get_tx_buffer_free_space(&sdr); fprintf(stderr, "TX buffer free: %zu\n", buffer_free_space_samples); if(buffer_free_space_samples < 400000) { // sample count for 200 ms at 2 MHz // try again after a short delay fsleep(10e-3); continue; } uint8_t packetbuf[2048]; ret = read(m_tunfd, packetbuf, sizeof(packetbuf)); if(ret < 0) { perror("read"); break; } else if(ret == 0) { // no more data break; } fprintf(stderr, "Transmitting packet with %d bytes.\n", ret); RESULT_CHECK(layer1_tx_reset(&tx)); // ** Modulate packet ** RESULT_CHECK(layer1_tx_add_packet_to_burst(&tx, packetbuf, ret)); RESULT_CHECK(layer1_tx_finalize_burst(&tx)); size_t burst_len = layer1_tx_get_sample_count(&tx); const float complex *whole_burst = layer1_tx_get_sample_data(&tx); dump_array_cf(whole_burst, burst_len, 1.0f, "/tmp/tx.cpx"); // ensure that the buffer is full before TX is turned on to avoid transmitting empty buffers RESULT_CHECK(transmit(&sdr, whole_burst, burst_len)); if(!on_air) { size_t buffer_used_samples = sdr_get_tx_buffer_used_space(&sdr); const size_t min_required_samples = 4*128*1024; int ret2 = poll(&pfd, 1, 0); if(ret2 < 0) { perror("poll"); break; } else if(ret2 != 0 && (buffer_used_samples < min_required_samples)) { // enqueue more packets before starting TX fprintf(stderr, "Pre-buffering more packets: %zd / %zd samples.\n", buffer_used_samples, min_required_samples); continue; } fprintf(stderr, "RX -> TX\n"); RESULT_CHECK(sdr_stop_rx(&sdr)); // transmit packets on the frequency where the last packet was received. layer1_tx_set_carrier_frequency_offset(&tx, layer1_rx_get_coarse_carrier_frequency(&rx)); RESULT_CHECK(sdr_start_tx(&sdr, burst_len * SDR_OVERSAMPLING)); } on_air = true; } else if(on_air) { // ret == 0 fprintf(stderr, "TX -> RX\n"); RESULT_CHECK(sdr_flush_tx_buffer(&sdr)); RESULT_CHECK(sdr_stop_tx(&sdr)); RESULT_CHECK(sdr_start_rx(&sdr)); on_air = false; block_tx_for(TX_SWITCH_BACKOFF_AFTER_RX_ON); } } if(!on_air) { // ** Receive signal ** const size_t CHUNKSIZE_BB = 16384; const size_t CHUNKSIZE_RF = (CHUNKSIZE_BB * SDR_OVERSAMPLING); 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; if(sdr_receive(&sdr, rf_samples, &n_rf_samples, 100000, SDR_OVERSAMPLING) != OK) { rx_retries++; fprintf(stderr, "sdr_receive() failed %d times.\n", rx_retries); if(rx_retries >= 3) { break; } else { continue; } } if(n_rf_samples == 0) { fsleep(1e-3); continue; } total_samples += n_rf_samples; double new = get_hires_time(); if(new >= next_stats_print_time) { double rate = total_samples / (new - old); fprintf(stderr, "\nEstimated rate: %.3f MS/s\n", rate / 1e6); fprintf(stderr, "Receiver statistics:\n"); fprintf(stderr, " Preambles found: %8zd\n", m_rx_stats.preambles_found); fprintf(stderr, " Successful decodes: %8zd (%6.2f %%)\n", m_rx_stats.successful_decodes, m_rx_stats.successful_decodes * 100.0f / m_rx_stats.preambles_found); fprintf(stderr, " Header errors: %8zd (%6.2f %%)\n", m_rx_stats.header_errors, m_rx_stats.header_errors * 100.0f / m_rx_stats.preambles_found); fprintf(stderr, " Failed decodes: %8zd (%6.2f %%)\n", m_rx_stats.failed_decodes, m_rx_stats.failed_decodes * 100.0f / m_rx_stats.preambles_found); next_stats_print_time += 0.5; total_samples = 0; old = new; } rx_retries = 0; fprintf(stderr, "r"); RESULT_CHECK(sdr_rf_to_baseband(&sdr, rf_samples, n_rf_samples, bb_samples, &n_bb_samples)); RESULT_CHECK(layer1_rx_process(&rx, bb_samples, n_bb_samples)); } else { rx_retries = 0; } } close(debug_fd); // ** Cleanup ** layer1_tx_shutdown(&tx); layer1_rx_shutdown(&rx); sdr_destroy(&sdr); jsonlogger_shutdown(); fprintf(stderr, "Done.\n"); }