hamnet70/impl/src/main.c
Thomas Kolb f5a367464f Optimized one-shot frequency synchronization
- Ramp-up length increased to 128 symbols (here is room for
  improvement!)
- Try to detect the frequency once during ramp-up. To do so, every
  second symbol is inverted (to remove the +/-1 symbol toggling) and the
  phase difference between neigboring resulting symbols is checked. When
  it is low enough for all symbols, the frequency is estimated and
  corrected. When frequency estimation was done, it is not retried for a
  number of incoming symbols in order to allow the timing estimator to
  converge again.
- This approach was verified in a simulated loopback test with frequency
  offset and AWGN.
2023-05-17 22:28:18 +02:00

328 lines
6.9 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;
double old = get_hires_time();
size_t total_samples = 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;
}
}
double new = get_hires_time();
total_samples += n_rf_samples;
double rate = total_samples / (new - old);
fprintf(stderr, "\nEstimated rate: %.3f MS/s\n", rate / 1e6);
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");
}