hamnet70/impl/src/main.c

318 lines
6.7 KiB
C

#include <linux/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <ctype.h>
#include <SoapySDR/Logger.h>
#include <liquid/liquid.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "utils.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 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");
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");
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);
break;
}
}
static int debug_fd;
#define CHUNKSIZE_BB 16384
#define CHUNKSIZE_RF (CHUNKSIZE_BB * SDR_OVERSAMPLING)
static result_t transmit_in_chunks(sdr_ctx_t *sdr, const float complex *samples, size_t len)
{
size_t transmitted = 0;
unsigned retry_counter = 0;
float complex rf_samples[CHUNKSIZE_RF];
while(transmitted < len) {
size_t to_transmit_bb = len - transmitted;
if(to_transmit_bb > CHUNKSIZE_BB) {
to_transmit_bb = CHUNKSIZE_BB;
}
RESULT_CHECK(sdr_baseband_to_rf(sdr, samples + transmitted, to_transmit_bb, rf_samples, CHUNKSIZE_RF));
size_t to_transmit_rf = to_transmit_bb * SDR_OVERSAMPLING;
result_t result = sdr_transmit(sdr, rf_samples, to_transmit_rf, 100000);
if(result != OK) {
retry_counter++;
fprintf(stderr, "sdr_transmit failed %d times\n", retry_counter);
if(retry_counter > 3) {
return result;
} else {
continue;
}
}
write(debug_fd, rf_samples, to_transmit_rf*sizeof(rf_samples[0]));
fprintf(stderr, "t");
transmitted += to_transmit_bb;
retry_counter = 0;
}
return OK;
}
int main(void)
{
layer1_tx_t tx;
layer1_rx_t rx;
sdr_ctx_t sdr;
SoapySDR_setLogLevel(SOAPY_SDR_DEBUG);
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));
// ** 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;
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.
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");
if(!on_air) {
RESULT_CHECK(sdr_start_tx(&sdr, burst_len * SDR_OVERSAMPLING));
RESULT_CHECK(sdr_flush_tx_buffer(&sdr));
}
RESULT_CHECK(transmit_in_chunks(&sdr, whole_burst, burst_len));
on_air = true;
} else if(on_air) { // ret == 0
RESULT_CHECK(sdr_flush_tx_buffer(&sdr));
RESULT_CHECK(sdr_start_rx(&sdr));
on_air = false;
block_tx_for(TX_SWITCH_BACKOFF_AFTER_RX_ON);
}
}
if(!on_air) {
// ** 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;
if(sdr_receive(&sdr, rf_samples, &n_rf_samples, 100000) != OK) {
rx_retries++;
fprintf(stderr, "sdr_receive() failed %d times.\n", rx_retries);
if(rx_retries >= 3) {
break;
} else {
continue;
}
}
rx_retries = 0;
fprintf(stderr, "r");
RESULT_CHECK(sdr_rf_to_baseband(&sdr, rf_samples, n_rf_samples, bb_samples, CHUNKSIZE_BB));
n_bb_samples = n_rf_samples / SDR_OVERSAMPLING;
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);
fprintf(stderr, "Done.\n");
}