Integrate TUN device interface

Packets are now read from the TUN device and transmitted. The signal
goes through a channel emulator and is then received + decoded. If
decoding is successful, the result is written back to the TUN device
(which does not make much sense, but works for testing).

Unfortunately, there still seems to be a problem in the receiver: packet
loss is very high even at extremely high SNRs.
This commit is contained in:
Thomas Kolb 2022-02-27 20:21:14 +01:00
parent 890baf577f
commit 934b18e382
10 changed files with 257 additions and 77 deletions

View file

@ -31,6 +31,8 @@ set(sources
src/layer1/whitening.h
src/layer1/modcod.c
src/layer1/modcod.h
src/layer2/tundev.c
src/layer2/tundev.h
)
include_directories(
@ -51,6 +53,7 @@ target_link_libraries(
m
fftw3f
fec
SoapySDR
)
add_subdirectory(test)

View file

@ -3,8 +3,28 @@
#include <liquid/liquid.h>
/*** SDR CONFIG ***/
#define SDR_IS_FULL_DUPLEX 0
#define SDR_RX_SAMPLING_RATE 1e6f
#define SDR_TX_SAMPLING_RATE RX_SAMPLING_RATE
// actually transmitted or received signal frequencies, NOT the SDR center frequency.
#define SDR_TX_FREQ 434.100e6f
#define SDR_RX_FREQ 434.100e6f
// shift applied in the baseband, to get rid of SDR DC peak. If the value here
// is not 0, software mixing will be done on the received signal.
#define SDR_TX_IF_SHIFT 0.000e6f
#define SDR_RX_IF_SHIFT 0.150e6f
// NOTE: the SDR center frequency will be SDR_RX_FREQ - SDR_RX_IF_SHIFT.
/*** LAYER 1 CONFIG ***/
#define SYMBOL_RATE 100e3f
#define PAYLOAD_CHANNEL_CODE LIQUID_FEC_CONV_V27P34
#define PAYLOAD_MODULATION LIQUID_MODEM_QAM16

View file

@ -27,27 +27,7 @@ result_t packet_mod_init(packet_mod_ctx_t *ctx)
result_t packet_mod_free(packet_mod_ctx_t *ctx)
{
switch(ctx->state) {
case DATA_RAW:
case DATA_CODED:
free(ctx->pkt_bytes);
ctx->pkt_bytes = NULL;
break;
case DATA_MODULATED:
case HEADER_ADDED:
case PREAMBLE_ADDED:
free(ctx->pkt_symbols);
ctx->pkt_symbols = NULL;
break;
case NOT_STARTED:
// nothing to do
break;
}
ctx->state = NOT_STARTED;
ctx->length = 0;
packet_mod_reset(ctx); // frees all malloc'd memory
fec_destroy(ctx->hdr_fec);
modem_destroy(ctx->hdr_modem);
@ -68,6 +48,25 @@ result_t packet_mod_set_modcod(packet_mod_ctx_t *ctx, modcod_t modcod)
}
result_t packet_mod_reset(packet_mod_ctx_t *ctx)
{
ctx->state = NOT_STARTED;
ctx->length = 0;
if(ctx->pkt_bytes) {
free(ctx->pkt_bytes);
ctx->pkt_bytes = NULL;
}
if(ctx->pkt_symbols) {
free(ctx->pkt_symbols);
ctx->pkt_symbols = NULL;
}
return OK;
}
result_t packet_mod_set_data(packet_mod_ctx_t *ctx, const unsigned char *data, size_t length)
{
if(ctx->state != NOT_STARTED) {

View file

@ -63,6 +63,15 @@ result_t packet_mod_free(packet_mod_ctx_t *ctx);
*/
result_t packet_mod_set_modcod(packet_mod_ctx_t *ctx, modcod_t modcod);
/*!\brief Clears all data from the modulator.
*
* After this call, the packet modulator can accept a new packet. Configuration (e.g. modulation type) is not changed.
*
* \param[inout] ctx The context to use for this operation.
* \returns An result code (see results.h).
*/
result_t packet_mod_reset(packet_mod_ctx_t *ctx);
/*!\brief Set the raw packet data.
*
* In this step, the CRC of the data is calculated.

View file

@ -16,7 +16,7 @@
#define AGC_BW_ACQUISITION 2e-2
#define AGC_BW_TRACKING 1e-4
#define SHOW_DEBUG_LOG 1
#define SHOW_DEBUG_LOG 0
#if SHOW_DEBUG_LOG
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)

View file

@ -74,7 +74,8 @@ result_t layer1_tx_reset(layer1_tx_t *tx)
result_t layer1_tx_add_packet_to_burst(layer1_tx_t *tx, const uint8_t *data, size_t length)
{
if(tx->samples_used == 0) {
if(!resize_buffer(tx, 4096 + transmission_calculate_ramp_up_length(&tx->transmission))) {
if((tx->samples_allocated == 0)
&& !resize_buffer(tx, 4096 + transmission_calculate_ramp_up_length(&tx->transmission))) {
return ERR_NO_MEM;
}
@ -91,11 +92,12 @@ result_t layer1_tx_add_packet_to_burst(layer1_tx_t *tx, const uint8_t *data, siz
}
// encode and modulate the packet
packet_mod_set_data(&tx->pmod, data, length);
packet_mod_encode(&tx->pmod);
packet_mod_modulate(&tx->pmod);
packet_mod_add_header(&tx->pmod);
packet_mod_add_preamble(&tx->pmod);
ERR_CHECK(packet_mod_reset(&tx->pmod));
ERR_CHECK(packet_mod_set_data(&tx->pmod, data, length));
ERR_CHECK(packet_mod_encode(&tx->pmod));
ERR_CHECK(packet_mod_modulate(&tx->pmod));
ERR_CHECK(packet_mod_add_header(&tx->pmod));
ERR_CHECK(packet_mod_add_preamble(&tx->pmod));
// determine number of symbols in the packet
size_t nsyms;

84
impl/src/layer2/tundev.c Normal file
View file

@ -0,0 +1,84 @@
#include <linux/if.h>
#include <linux/if_tun.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include "tundev.h"
int tundev_open(char *dev)
{
int tunfd = open("/dev/net/tun", O_RDWR);
if(tunfd < 0) {
perror("tundev: open");
return -1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
int ret = ioctl(tunfd, TUNSETIFF, &ifr);
if(ret < 0) {
perror("tundev: ioctl");
goto error;
}
strcpy(dev, ifr.ifr_name);
fprintf(stderr, "tundev: Successfully allocated tun device %s\n", dev);
return tunfd;
fprintf(stderr, "tundev: Closing device.\n");
close(tunfd);
return 0;
error:
close(tunfd);
return -1;
}
result_t tundev_close(int tundev)
{
if(close(tundev) < 0) {
return ERR_SYSCALL;
} else {
return OK;
}
}
result_t tundev_read_loop(int tunfd, tundev_read_callback_t callback)
{
uint8_t buf[2048];
int ret;
do {
ret = read(tunfd, buf, sizeof(buf));
if(ret <= 0) {
break;
}
fprintf(stderr, "tundev: Read %d bytes from tun device.\n", ret);
} while(callback(buf, ret));
if(ret == 0) {
fprintf(stderr, "tundev: no more data.\n");
return OK;
} else {
perror("tundev: read");
return ERR_SYSCALL;
}
}

17
impl/src/layer2/tundev.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef LAYER2_TUNDEV_H
#define LAYER2_TUNDEV_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "results.h"
typedef bool (*tundev_read_callback_t)(uint8_t *packet, size_t len);
int tundev_open(char *dev);
result_t tundev_close(int tundev);
result_t tundev_read_loop(int tunfd, tundev_read_callback_t callback);
#endif // LAYER2_TUNDEV_H

View file

@ -1,13 +1,18 @@
#include <linux/if.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <liquid/liquid.h>
#include <unistd.h>
#include "utils.h"
#include "layer1/tx.h"
#include "layer1/rx.h"
#include "layer2/tundev.h"
#define RESULT_CHECK(stmt) { \
result_t res = stmt; \
if(res != OK) { \
@ -16,6 +21,9 @@
} \
}
static int m_tunfd = -1;
static bool m_running = true;
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]));
@ -28,19 +36,26 @@ void print_complex_array(const char *varname, float complex const *array, size_t
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");
fprintf(stderr, "%s\n", packet_data);
fprintf(stderr, "=======================\n");
//fprintf(stderr, "=== FAILED PAYLOAD ===\n");
//fprintf(stderr, "%s\n", packet_data);
//fprintf(stderr, "=======================\n");
break;
case RX_EVT_PACKET_RECEIVED:
fprintf(stderr, "=== DECODED PAYLOAD ===\n");
fprintf(stderr, "=== DECODED PAYLOAD (%4zu bytes) ===\n", packet_len);
fprintf(stderr, "%s\n", packet_data);
fprintf(stderr, "=======================\n");
fprintf(stderr, "====================================\n");
ret = write(m_tunfd, packet_data, packet_len);
if(ret < 0) {
perror("write");
}
break;
case RX_EVT_PREAMBLE_FOUND:
@ -52,53 +67,75 @@ void cb_rx(rx_evt_t evt, uint8_t *packet_data, size_t packet_len)
int main(void)
{
uint8_t msg_org[] = "Hello Liquid! This is the message to transmit. Hopefully it can be decoded correctly...";
// ** Modulate packet **
layer1_tx_t tx;
RESULT_CHECK(layer1_tx_init(&tx));
RESULT_CHECK(layer1_tx_add_packet_to_burst(&tx, msg_org, sizeof(msg_org)));
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");
// ** Apply channel distortions **
channel_cccf channel = channel_cccf_create();
float snr = 10.0f;
channel_cccf_add_awgn(channel, -snr, snr);
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
channel_cccf_add_shadowing(channel, 1.00f, 0.1f);
// channel
float complex msg_received[burst_len];
//memcpy(msg_received, whole_burst, sizeof(whole_burst)); // no noise in channel
channel_cccf_execute_block(channel, whole_burst, burst_len, msg_received);
// scale the entire signal to give the AGC something to do
for(size_t i = 0; i < burst_len; i++) {
msg_received[i] *= 0.02f;
}
dump_array_cf(msg_received, burst_len, 1.0f, "/tmp/rx.cpx");
// ** RX starts here **
layer1_rx_t rx;
// ** Initialize **
char devname[IFNAMSIZ] = "hamnet70";
m_tunfd = tundev_open(devname);
if(m_tunfd < 0) {
return 1;
}
RESULT_CHECK(layer1_tx_init(&tx));
RESULT_CHECK(layer1_rx_init(&rx, cb_rx));
RESULT_CHECK(layer1_rx_process(&rx, msg_received, burst_len));
// channel emulation
channel_cccf channel = channel_cccf_create();
// cleanup
float snr = 15.0f;
channel_cccf_add_awgn(channel, -snr, snr);
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
//channel_cccf_add_shadowing(channel, 1.00f, 0.1f);
// ** Process packets **
uint8_t packetbuf[2048];
while(m_running) {
int 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");
// ** Apply channel distortions **
float complex msg_received[burst_len];
channel_cccf_execute_block(channel, whole_burst, burst_len, msg_received);
// scale the entire signal to give the AGC something to do
for(size_t i = 0; i < burst_len; i++) {
msg_received[i] *= 0.02f;
}
dump_array_cf(msg_received, burst_len, 1.0f, "/tmp/rx.cpx");
// ** Receive signal **
RESULT_CHECK(layer1_rx_process(&rx, msg_received, burst_len));
}
// ** Cleanup **
channel_cccf_destroy(channel);

View file

@ -15,9 +15,9 @@ typedef enum {
# define ERR_CHECK_LIQUID(call) \
do { \
liquid_error_code result; \
if((result = (call)) != LIQUID_OK) { \
fprintf(stderr, "Liquid call failed in %s:%d: %s\n", __FILE__, __LINE__, liquid_error_info(result)); \
liquid_error_code lq_result; \
if((lq_result = (call)) != LIQUID_OK) { \
fprintf(stderr, "Liquid call failed in %s:%d: %s\n", __FILE__, __LINE__, liquid_error_info(lq_result)); \
return ERR_LIQUID; \
} \
} while(0);
@ -25,4 +25,13 @@ typedef enum {
# define ERR_CHECK_LIQUID(call) if((call) != LIQUID_OK) { return ERR_LIQUID; }
#endif
#define ERR_CHECK(call) \
do { \
result_t err_check_result = call; \
if(err_check_result != OK) { \
fprintf(stderr, "Error detected at %s:%d: %d\n", __FILE__, __LINE__, err_check_result); \
return err_check_result; \
} \
} while(0);
#endif // RESULTS_H