hamnet70/impl/test/layer1/test_loopback.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

164 lines
3.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <liquid/liquid.h>
#include "layer1/tx.h"
#include "layer1/rx.h"
#include "utils.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); \
} \
}
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)
{
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");
break;
case RX_EVT_PREAMBLE_FOUND:
fprintf(stderr, "Found preamble!\n");
break;
}
}
int main(void)
{
layer1_tx_t tx;
layer1_rx_t rx;
// ** Initialize **
srand(time(NULL));
RESULT_CHECK(layer1_tx_init(&tx));
RESULT_CHECK(layer1_rx_init(&rx, cb_rx));
// ** Generate packets **
RESULT_CHECK(layer1_tx_reset(&tx));
uint8_t packetbuf1[] = "Hello World! 1234567890";
size_t l = sizeof(packetbuf1);
fprintf(stderr, "Transmitting packet with %zd bytes.\n", l);
RESULT_CHECK(layer1_tx_add_packet_to_burst(&tx, packetbuf1, l));
uint8_t packetbuf2[] = "This is just a test message. Just writing some stuff here to create a longer packet.";
l = sizeof(packetbuf2);
fprintf(stderr, "Transmitting packet with %zd bytes.\n", l);
RESULT_CHECK(layer1_tx_add_packet_to_burst(&tx, packetbuf2, l));
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, 10e-6f, "/tmp/tx.cpx");
// ** Simulate channel effects **
channel_cccf ch = channel_cccf_create();
channel_cccf_add_awgn(ch, -30, 10);
channel_cccf_add_carrier_offset(ch, 0.05f, 1.23f);
float complex whole_burst_ch[burst_len];
channel_cccf_execute_block(ch, (float complex*)whole_burst, burst_len, whole_burst_ch);
dump_array_cf(whole_burst_ch, burst_len, 10e-6f, "/tmp/rx.cpx");
// ** Receive signal **
RESULT_CHECK(layer1_rx_process(&rx, whole_burst_ch, burst_len));
// ** Cleanup **
layer1_tx_shutdown(&tx);
layer1_rx_shutdown(&rx);
fprintf(stderr, "Done.\n");
}