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:
parent
890baf577f
commit
934b18e382
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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
84
impl/src/layer2/tundev.c
Normal 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
17
impl/src/layer2/tundev.h
Normal 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
|
|
@ -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,14 +67,49 @@ 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...";
|
||||
layer1_tx_t tx;
|
||||
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));
|
||||
|
||||
// channel emulation
|
||||
channel_cccf channel = channel_cccf_create();
|
||||
|
||||
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 **
|
||||
|
||||
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_add_packet_to_burst(&tx, packetbuf, ret));
|
||||
RESULT_CHECK(layer1_tx_finalize_burst(&tx));
|
||||
|
||||
size_t burst_len = layer1_tx_get_sample_count(&tx);
|
||||
|
@ -69,18 +119,8 @@ int main(void)
|
|||
|
||||
// ** 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
|
||||
|
@ -90,15 +130,12 @@ int main(void)
|
|||
|
||||
dump_array_cf(msg_received, burst_len, 1.0f, "/tmp/rx.cpx");
|
||||
|
||||
// ** RX starts here **
|
||||
|
||||
layer1_rx_t rx;
|
||||
|
||||
RESULT_CHECK(layer1_rx_init(&rx, cb_rx));
|
||||
// ** Receive signal **
|
||||
|
||||
RESULT_CHECK(layer1_rx_process(&rx, msg_received, burst_len));
|
||||
}
|
||||
|
||||
// cleanup
|
||||
// ** Cleanup **
|
||||
|
||||
channel_cccf_destroy(channel);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue