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/whitening.h
|
||||||
src/layer1/modcod.c
|
src/layer1/modcod.c
|
||||||
src/layer1/modcod.h
|
src/layer1/modcod.h
|
||||||
|
src/layer2/tundev.c
|
||||||
|
src/layer2/tundev.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
@ -51,6 +53,7 @@ target_link_libraries(
|
||||||
m
|
m
|
||||||
fftw3f
|
fftw3f
|
||||||
fec
|
fec
|
||||||
|
SoapySDR
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
|
|
@ -3,8 +3,28 @@
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#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 ***/
|
/*** LAYER 1 CONFIG ***/
|
||||||
|
|
||||||
|
#define SYMBOL_RATE 100e3f
|
||||||
|
|
||||||
#define PAYLOAD_CHANNEL_CODE LIQUID_FEC_CONV_V27P34
|
#define PAYLOAD_CHANNEL_CODE LIQUID_FEC_CONV_V27P34
|
||||||
#define PAYLOAD_MODULATION LIQUID_MODEM_QAM16
|
#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)
|
result_t packet_mod_free(packet_mod_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
switch(ctx->state) {
|
packet_mod_reset(ctx); // frees all malloc'd memory
|
||||||
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;
|
|
||||||
|
|
||||||
fec_destroy(ctx->hdr_fec);
|
fec_destroy(ctx->hdr_fec);
|
||||||
modem_destroy(ctx->hdr_modem);
|
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)
|
result_t packet_mod_set_data(packet_mod_ctx_t *ctx, const unsigned char *data, size_t length)
|
||||||
{
|
{
|
||||||
if(ctx->state != NOT_STARTED) {
|
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);
|
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.
|
/*!\brief Set the raw packet data.
|
||||||
*
|
*
|
||||||
* In this step, the CRC of the data is calculated.
|
* In this step, the CRC of the data is calculated.
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#define AGC_BW_ACQUISITION 2e-2
|
#define AGC_BW_ACQUISITION 2e-2
|
||||||
#define AGC_BW_TRACKING 1e-4
|
#define AGC_BW_TRACKING 1e-4
|
||||||
|
|
||||||
#define SHOW_DEBUG_LOG 1
|
#define SHOW_DEBUG_LOG 0
|
||||||
|
|
||||||
#if SHOW_DEBUG_LOG
|
#if SHOW_DEBUG_LOG
|
||||||
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
|
# 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)
|
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(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;
|
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
|
// encode and modulate the packet
|
||||||
packet_mod_set_data(&tx->pmod, data, length);
|
ERR_CHECK(packet_mod_reset(&tx->pmod));
|
||||||
packet_mod_encode(&tx->pmod);
|
ERR_CHECK(packet_mod_set_data(&tx->pmod, data, length));
|
||||||
packet_mod_modulate(&tx->pmod);
|
ERR_CHECK(packet_mod_encode(&tx->pmod));
|
||||||
packet_mod_add_header(&tx->pmod);
|
ERR_CHECK(packet_mod_modulate(&tx->pmod));
|
||||||
packet_mod_add_preamble(&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
|
// determine number of symbols in the packet
|
||||||
size_t nsyms;
|
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
|
129
impl/src/main.c
129
impl/src/main.c
|
@ -1,13 +1,18 @@
|
||||||
|
#include <linux/if.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "layer1/tx.h"
|
#include "layer1/tx.h"
|
||||||
#include "layer1/rx.h"
|
#include "layer1/rx.h"
|
||||||
|
|
||||||
|
#include "layer2/tundev.h"
|
||||||
|
|
||||||
#define RESULT_CHECK(stmt) { \
|
#define RESULT_CHECK(stmt) { \
|
||||||
result_t res = stmt; \
|
result_t res = stmt; \
|
||||||
if(res != OK) { \
|
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)
|
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]));
|
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)
|
void cb_rx(rx_evt_t evt, uint8_t *packet_data, size_t packet_len)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
switch(evt)
|
switch(evt)
|
||||||
{
|
{
|
||||||
case RX_EVT_CHECKSUM_ERROR:
|
case RX_EVT_CHECKSUM_ERROR:
|
||||||
fprintf(stderr, "Received a message of %zu bytes, but decoding failed.\n", packet_len);
|
fprintf(stderr, "Received a message of %zu bytes, but decoding failed.\n", packet_len);
|
||||||
fprintf(stderr, "=== FAILED PAYLOAD ===\n");
|
//fprintf(stderr, "=== FAILED PAYLOAD ===\n");
|
||||||
fprintf(stderr, "%s\n", packet_data);
|
//fprintf(stderr, "%s\n", packet_data);
|
||||||
fprintf(stderr, "=======================\n");
|
//fprintf(stderr, "=======================\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RX_EVT_PACKET_RECEIVED:
|
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, "%s\n", packet_data);
|
||||||
fprintf(stderr, "=======================\n");
|
fprintf(stderr, "====================================\n");
|
||||||
|
|
||||||
|
ret = write(m_tunfd, packet_data, packet_len);
|
||||||
|
if(ret < 0) {
|
||||||
|
perror("write");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RX_EVT_PREAMBLE_FOUND:
|
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)
|
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;
|
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;
|
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_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);
|
channel_cccf_destroy(channel);
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ typedef enum {
|
||||||
|
|
||||||
# define ERR_CHECK_LIQUID(call) \
|
# define ERR_CHECK_LIQUID(call) \
|
||||||
do { \
|
do { \
|
||||||
liquid_error_code result; \
|
liquid_error_code lq_result; \
|
||||||
if((result = (call)) != LIQUID_OK) { \
|
if((lq_result = (call)) != LIQUID_OK) { \
|
||||||
fprintf(stderr, "Liquid call failed in %s:%d: %s\n", __FILE__, __LINE__, liquid_error_info(result)); \
|
fprintf(stderr, "Liquid call failed in %s:%d: %s\n", __FILE__, __LINE__, liquid_error_info(lq_result)); \
|
||||||
return ERR_LIQUID; \
|
return ERR_LIQUID; \
|
||||||
} \
|
} \
|
||||||
} while(0);
|
} while(0);
|
||||||
|
@ -25,4 +25,13 @@ typedef enum {
|
||||||
# define ERR_CHECK_LIQUID(call) if((call) != LIQUID_OK) { return ERR_LIQUID; }
|
# define ERR_CHECK_LIQUID(call) if((call) != LIQUID_OK) { return ERR_LIQUID; }
|
||||||
#endif
|
#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
|
#endif // RESULTS_H
|
||||||
|
|
Loading…
Reference in a new issue