From 934b18e3820d3b8a1252c1ae47204663a622092d Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Sun, 27 Feb 2022 20:21:14 +0100 Subject: [PATCH] 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. --- impl/CMakeLists.txt | 3 + impl/src/config.h | 20 ++++++ impl/src/layer1/packet_mod.c | 41 ++++++----- impl/src/layer1/packet_mod.h | 9 +++ impl/src/layer1/rx.c | 2 +- impl/src/layer1/tx.c | 14 ++-- impl/src/layer2/tundev.c | 84 +++++++++++++++++++++++ impl/src/layer2/tundev.h | 17 +++++ impl/src/main.c | 129 ++++++++++++++++++++++------------- impl/src/results.h | 15 +++- 10 files changed, 257 insertions(+), 77 deletions(-) create mode 100644 impl/src/layer2/tundev.c create mode 100644 impl/src/layer2/tundev.h diff --git a/impl/CMakeLists.txt b/impl/CMakeLists.txt index 7bfcbd7..20e28eb 100644 --- a/impl/CMakeLists.txt +++ b/impl/CMakeLists.txt @@ -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) diff --git a/impl/src/config.h b/impl/src/config.h index a1ddb0b..54033c3 100644 --- a/impl/src/config.h +++ b/impl/src/config.h @@ -3,8 +3,28 @@ #include +/*** 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 diff --git a/impl/src/layer1/packet_mod.c b/impl/src/layer1/packet_mod.c index e522304..7357b03 100644 --- a/impl/src/layer1/packet_mod.c +++ b/impl/src/layer1/packet_mod.c @@ -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) { diff --git a/impl/src/layer1/packet_mod.h b/impl/src/layer1/packet_mod.h index 16555a9..3720c0d 100644 --- a/impl/src/layer1/packet_mod.h +++ b/impl/src/layer1/packet_mod.h @@ -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. diff --git a/impl/src/layer1/rx.c b/impl/src/layer1/rx.c index 45a58bf..ffe339f 100644 --- a/impl/src/layer1/rx.c +++ b/impl/src/layer1/rx.c @@ -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__) diff --git a/impl/src/layer1/tx.c b/impl/src/layer1/tx.c index 30a31b5..e1044d0 100644 --- a/impl/src/layer1/tx.c +++ b/impl/src/layer1/tx.c @@ -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; diff --git a/impl/src/layer2/tundev.c b/impl/src/layer2/tundev.c new file mode 100644 index 0000000..600aa1f --- /dev/null +++ b/impl/src/layer2/tundev.c @@ -0,0 +1,84 @@ +#include +#include + +#include +#include +#include + +#include +#include + +#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; + } +} diff --git a/impl/src/layer2/tundev.h b/impl/src/layer2/tundev.h new file mode 100644 index 0000000..180a0bf --- /dev/null +++ b/impl/src/layer2/tundev.h @@ -0,0 +1,17 @@ +#ifndef LAYER2_TUNDEV_H +#define LAYER2_TUNDEV_H + +#include +#include +#include + +#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 diff --git a/impl/src/main.c b/impl/src/main.c index 5ddb820..564b390 100644 --- a/impl/src/main.c +++ b/impl/src/main.c @@ -1,13 +1,18 @@ +#include #include #include #include #include #include +#include + #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); diff --git a/impl/src/results.h b/impl/src/results.h index 3219e46..9f0667e 100644 --- a/impl/src/results.h +++ b/impl/src/results.h @@ -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