From 41209c69597292f06745ec4d951af1589ac8b070 Mon Sep 17 00:00:00 2001 From: Aaron Shaw Date: Thu, 26 Aug 2021 01:18:15 +0100 Subject: [PATCH] sx1302: upgrade hal to v2.1.0 upgrade hal to v2.1.0 - bring in latest repo from upstream - update dockerfile - update build script - update sx1302 fixes (see #23) for updated repo - remove some unecessary files and code - update config.json files to add com_type and com_path and fine_timestamping Closes: #24 --- Dockerfile | 7 +- buildfiles/compileSX1302.sh | 9 +- files/configurePktFwd.py | 2 +- lora_templates_sx1302/AS-global_conf.json | 10 +- lora_templates_sx1302/AS1-global_conf.json | 10 +- lora_templates_sx1302/AS2-global_conf.json | 10 +- lora_templates_sx1302/AS3-global_conf.json | 10 +- lora_templates_sx1302/AU-global_conf.json | 10 +- lora_templates_sx1302/CN-global_conf.json | 8 +- lora_templates_sx1302/EU-global_conf.json | 8 +- lora_templates_sx1302/IN-global_conf.json | 8 +- lora_templates_sx1302/KR-global_conf.json | 10 +- lora_templates_sx1302/RU-global_conf.json | 8 +- lora_templates_sx1302/US-global_conf.json | 8 +- sx1302fixes/Makefile | 78 +- sx1302fixes/lora_pkt_fwd.c | 574 ++++++++++-- sx1302fixes/loragw_gps.c | 2 +- sx1302fixes/loragw_hal.c | 985 +++++++++++++++++---- sx1302fixes/test_loragw_gps_i2c.c | 63 +- sx1302fixes/test_loragw_gps_uart.c | 63 +- 20 files changed, 1509 insertions(+), 374 deletions(-) diff --git a/Dockerfile b/Dockerfile index 090e0a1..7587dab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,11 +29,10 @@ RUN apt-get update && \ build-essential \ python3 \ python3-pip \ - python3-venv - + python3-venv && \ # Because the PATH is already updated above, this command creates a new venv AND activates it + python3 -m venv /opt/iotloragateway/dev/venv && \ # Given venv is active, this `pip` refers to the python3 variant -RUN python3 -m venv /opt/iotloragateway/dev/venv && \ pip install --no-cache-dir -r requirements.txt # Copy the buildfiles and sx1302 concentrator fixes @@ -94,7 +93,7 @@ COPY lora_templates_sx1301/EU-global_conf.json global_conf.json WORKDIR /opt/iotloragateway/packet_forwarder/sx1302 # Copy sx1302 hal from builder -COPY --from=builder /opt/iotloragateway/dev/sx1302_hal-1.0.5 . +COPY --from=builder /opt/iotloragateway/dev/sx1302_hal-2.1.0 . # Copy sx1302 regional config templates COPY lora_templates_sx1302 lora_templates_sx1302/ diff --git a/buildfiles/compileSX1302.sh b/buildfiles/compileSX1302.sh index c4bf492..cf3c52e 100755 --- a/buildfiles/compileSX1302.sh +++ b/buildfiles/compileSX1302.sh @@ -2,16 +2,17 @@ cd /opt/iotloragateway/dev || exit -#git clone https://github.com/NebraLtd/sx1302_hal.git -wget https://github.com/NebraLtd/sx1302_hal/archive/V1.0.5.tar.gz -tar -xzvf V1.0.5.tar.gz +wget https://github.com/NebraLtd/sx1302_hal/archive/V2.1.0.tar.gz +tar -xzvf V2.1.0.tar.gz -cd /opt/iotloragateway/dev/sx1302_hal-1.0.5 || exit +cd /opt/iotloragateway/dev/sx1302_hal-2.1.0 || exit #Remove old files rm libloragw/inc/loragw_stts751.h -f rm libloragw/src/loragw_stts751.c -f +rm libloragw/inc/loragw_ad5338r.h -f +rm libloragw/src/loragw_ad5338r.c -f #Copy new files cp ../sx1302fixes/loragw_hal.c libloragw/src/loragw_hal.c -f diff --git a/files/configurePktFwd.py b/files/configurePktFwd.py index 138d4b9..6da1e19 100644 --- a/files/configurePktFwd.py +++ b/files/configurePktFwd.py @@ -111,7 +111,7 @@ def writeRegionConfSx1302(regionId, spi_bus): newGlobal = json.load(regionconfJFile) # Inject SPI Bus - newGlobal['SX130x_conf']['spidev_path'] = "/dev/%s" % spi_bus + newGlobal['SX130x_conf']['com_path'] = "/dev/%s" % spi_bus globalPath = "/opt/iotloragateway/packet_forwarder/sx1302/packet_forwarder/global_conf.json" diff --git a/lora_templates_sx1302/AS-global_conf.json b/lora_templates_sx1302/AS-global_conf.json index a49212b..36b7e60 100644 --- a/lora_templates_sx1302/AS-global_conf.json +++ b/lora_templates_sx1302/AS-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { - "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "fine_timestamp": { + "enable": false, + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/AS1-global_conf.json b/lora_templates_sx1302/AS1-global_conf.json index 9ca5421..48c1511 100644 --- a/lora_templates_sx1302/AS1-global_conf.json +++ b/lora_templates_sx1302/AS1-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { - "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "fine_timestamp": { + "enable": false, + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/AS2-global_conf.json b/lora_templates_sx1302/AS2-global_conf.json index 5d9fc19..ac87b6a 100644 --- a/lora_templates_sx1302/AS2-global_conf.json +++ b/lora_templates_sx1302/AS2-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { - "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "fine_timestamp": { + "enable": false, + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/AS3-global_conf.json b/lora_templates_sx1302/AS3-global_conf.json index 3e57e91..6abab23 100644 --- a/lora_templates_sx1302/AS3-global_conf.json +++ b/lora_templates_sx1302/AS3-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { - "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "fine_timestamp": { + "enable": false, + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/AU-global_conf.json b/lora_templates_sx1302/AU-global_conf.json index 286314d..9537a35 100644 --- a/lora_templates_sx1302/AU-global_conf.json +++ b/lora_templates_sx1302/AU-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { - "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "fine_timestamp": { + "enable": false, + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/CN-global_conf.json b/lora_templates_sx1302/CN-global_conf.json index 933cbf1..a36d1d8 100644 --- a/lora_templates_sx1302/CN-global_conf.json +++ b/lora_templates_sx1302/CN-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { + "fine_timestamp": { "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/EU-global_conf.json b/lora_templates_sx1302/EU-global_conf.json index 0236c4d..ce9e6d4 100644 --- a/lora_templates_sx1302/EU-global_conf.json +++ b/lora_templates_sx1302/EU-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { + "fine_timestamp": { "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/IN-global_conf.json b/lora_templates_sx1302/IN-global_conf.json index a3f96ed..5e08fdf 100644 --- a/lora_templates_sx1302/IN-global_conf.json +++ b/lora_templates_sx1302/IN-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { + "fine_timestamp": { "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/KR-global_conf.json b/lora_templates_sx1302/KR-global_conf.json index 745d13d..6f018cd 100644 --- a/lora_templates_sx1302/KR-global_conf.json +++ b/lora_templates_sx1302/KR-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev0.0", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { - "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "fine_timestamp": { + "enable": false, + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/RU-global_conf.json b/lora_templates_sx1302/RU-global_conf.json index 3275cfc..baa78a3 100644 --- a/lora_templates_sx1302/RU-global_conf.json +++ b/lora_templates_sx1302/RU-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { + "fine_timestamp": { "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/lora_templates_sx1302/US-global_conf.json b/lora_templates_sx1302/US-global_conf.json index c5a0550..cd6dd0c 100644 --- a/lora_templates_sx1302/US-global_conf.json +++ b/lora_templates_sx1302/US-global_conf.json @@ -1,14 +1,14 @@ { "SX130x_conf": { - "spidev_path": "/dev/spidev1.2", + "com_type": "SPI", + "com_path": "/dev/spidev1.2", "lorawan_public": true, "clksrc": 0, "antenna_gain": 0, "full_duplex": false, - "precision_timestamp": { + "fine_timestamp": { "enable": false, - "max_ts_metrics": 255, - "nb_symbols": 1 + "mode": "all_sf" }, "radio_0": { "enable": true, diff --git a/sx1302fixes/Makefile b/sx1302fixes/Makefile index 78481b3..d3fa1e1 100644 --- a/sx1302fixes/Makefile +++ b/sx1302fixes/Makefile @@ -22,7 +22,20 @@ LIBS := -lloragw -ltinymt32 -lrt -lm ### general build targets -all: libloragw.a test_loragw_spi test_loragw_i2c test_loragw_reg test_loragw_hal_tx test_loragw_hal_rx test_loragw_cal test_loragw_capture_ram test_loragw_spi_sx1250 test_loragw_counter test_loragw_gps test_loragw_gps_i2c +all: libloragw.a \ + test_loragw_com \ + test_loragw_i2c \ + test_loragw_reg \ + test_loragw_hal_tx \ + test_loragw_hal_rx \ + test_loragw_cal_sx125x \ + test_loragw_capture_ram \ + test_loragw_com_sx1250 \ + test_loragw_com_sx1261 \ + test_loragw_counter \ + test_loragw_gps \ + test_loragw_toa \ + test_loragw_sx1261_rssi clean: rm -f libloragw.a @@ -60,17 +73,19 @@ inc/config.h: ../VERSION library.cfg @echo "Release version : $(LIBLORAGW_VERSION)" @echo " #define LIBLORAGW_VERSION "\"$(LIBLORAGW_VERSION)\""" >> $@ # Debug options - @echo " #define DEBUG_AUX $(DEBUG_AUX)" >> $@ - @echo " #define DEBUG_SPI $(DEBUG_SPI)" >> $@ - @echo " #define DEBUG_I2C $(DEBUG_I2C)" >> $@ - @echo " #define DEBUG_REG $(DEBUG_REG)" >> $@ - @echo " #define DEBUG_HAL $(DEBUG_HAL)" >> $@ - @echo " #define DEBUG_GPS $(DEBUG_GPS)" >> $@ - @echo " #define DEBUG_GPIO $(DEBUG_GPIO)" >> $@ - @echo " #define DEBUG_LBT $(DEBUG_LBT)" >> $@ - @echo " #define DEBUG_RAD $(DEBUG_RAD)" >> $@ - @echo " #define DEBUG_CAL $(DEBUG_CAL)" >> $@ + @echo " #define DEBUG_AUX $(DEBUG_AUX)" >> $@ + @echo " #define DEBUG_COM $(DEBUG_COM)" >> $@ + @echo " #define DEBUG_MCU $(DEBUG_MCU)" >> $@ + @echo " #define DEBUG_I2C $(DEBUG_I2C)" >> $@ + @echo " #define DEBUG_REG $(DEBUG_REG)" >> $@ + @echo " #define DEBUG_HAL $(DEBUG_HAL)" >> $@ + @echo " #define DEBUG_GPS $(DEBUG_GPS)" >> $@ + @echo " #define DEBUG_GPIO $(DEBUG_GPIO)" >> $@ + @echo " #define DEBUG_LBT $(DEBUG_LBT)" >> $@ + @echo " #define DEBUG_RAD $(DEBUG_RAD)" >> $@ + @echo " #define DEBUG_CAL $(DEBUG_CAL)" >> $@ @echo " #define DEBUG_SX1302 $(DEBUG_SX1302)" >> $@ + @echo " #define DEBUG_FTIME $(DEBUG_FTIME)" >> $@ # end of file @echo "#endif" >> $@ @echo "*** Configuration seems ok ***" @@ -85,12 +100,37 @@ $(OBJDIR)/%.o: src/%.c $(INCLUDES) inc/config.h | $(OBJDIR) ### static library -libloragw.a: $(OBJDIR)/loragw_spi.o $(OBJDIR)/loragw_i2c.o $(OBJDIR)/loragw_aux.o $(OBJDIR)/loragw_reg.o $(OBJDIR)/loragw_sx1250.o $(OBJDIR)/loragw_sx125x.o $(OBJDIR)/loragw_sx1302.o $(OBJDIR)/loragw_cal.o $(OBJDIR)/loragw_debug.o $(OBJDIR)/loragw_hal.o $(OBJDIR)/loragw_gps.o $(OBJDIR)/loragw_sx1302_timestamp.o $(OBJDIR)/loragw_sx1302_rx.o +libloragw.a: $(OBJDIR)/loragw_spi.o \ + $(OBJDIR)/loragw_usb.o \ + $(OBJDIR)/loragw_com.o \ + $(OBJDIR)/loragw_mcu.o \ + $(OBJDIR)/loragw_i2c.o \ + $(OBJDIR)/sx125x_spi.o \ + $(OBJDIR)/sx125x_com.o \ + $(OBJDIR)/sx1250_spi.o \ + $(OBJDIR)/sx1250_usb.o \ + $(OBJDIR)/sx1250_com.o \ + $(OBJDIR)/sx1261_spi.o \ + $(OBJDIR)/sx1261_usb.o \ + $(OBJDIR)/sx1261_com.o \ + $(OBJDIR)/loragw_aux.o \ + $(OBJDIR)/loragw_reg.o \ + $(OBJDIR)/loragw_sx1250.o \ + $(OBJDIR)/loragw_sx1261.o \ + $(OBJDIR)/loragw_sx125x.o \ + $(OBJDIR)/loragw_sx1302.o \ + $(OBJDIR)/loragw_cal.o \ + $(OBJDIR)/loragw_debug.o \ + $(OBJDIR)/loragw_hal.o \ + $(OBJDIR)/loragw_lbt.o \ + $(OBJDIR)/loragw_gps.o \ + $(OBJDIR)/loragw_sx1302_timestamp.o \ + $(OBJDIR)/loragw_sx1302_rx.o $(AR) rcs $@ $^ ### test programs -test_loragw_spi: tst/test_loragw_spi.c libloragw.a +test_loragw_com: tst/test_loragw_com.c libloragw.a $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) test_loragw_i2c: tst/test_loragw_i2c.c libloragw.a @@ -108,10 +148,13 @@ test_loragw_hal_rx: tst/test_loragw_hal_rx.c libloragw.a test_loragw_capture_ram: tst/test_loragw_capture_ram.c libloragw.a $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) -test_loragw_cal: tst/test_loragw_cal.c libloragw.a +test_loragw_cal_sx125x: tst/test_loragw_cal_sx125x.c libloragw.a $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) -test_loragw_spi_sx1250: tst/test_loragw_spi_sx1250.c libloragw.a +test_loragw_com_sx1250: tst/test_loragw_com_sx1250.c libloragw.a + $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) + +test_loragw_com_sx1261: tst/test_loragw_com_sx1261.c libloragw.a $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) test_loragw_counter: tst/test_loragw_counter.c libloragw.a @@ -120,7 +163,10 @@ test_loragw_counter: tst/test_loragw_counter.c libloragw.a test_loragw_gps: tst/test_loragw_gps.c libloragw.a $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) -test_loragw_gps_i2c: tst/test_loragw_gps_i2c.c libloragw.a +test_loragw_toa: tst/test_loragw_toa.c libloragw.a + $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) + +test_loragw_sx1261_rssi: tst/test_loragw_sx1261_rssi.c libloragw.a $(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS) ### EOF diff --git a/sx1302fixes/lora_pkt_fwd.c b/sx1302fixes/lora_pkt_fwd.c index 532b232..cb63c65 100644 --- a/sx1302fixes/lora_pkt_fwd.c +++ b/sx1302fixes/lora_pkt_fwd.c @@ -62,6 +62,8 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #define STRINGIFY(x) #x #define STR(x) STRINGIFY(x) +#define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min) + /* -------------------------------------------------------------------------- */ /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ @@ -70,7 +72,7 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #endif #define JSON_CONF_DEFAULT "/opt/iotloragateway/packet_forwarder/sx1302/packet_forwarder/global_conf.json" -#define JSON_CONF_LOCAL "/opt/iotloragateway/packet_forwarder/sx1302/packet_forwarder/local_conf.json" +#define JSON_CONF_LOCAL "/opt/iotloragateway/packet_forwarder/sx1302/packet_forwarder/local_conf.json" #define DEFAULT_SERVER 127.0.0.1 /* hostname also supported */ #define DEFAULT_PORT_UP 1780 @@ -83,10 +85,10 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #define FETCH_SLEEP_MS 10 /* nb of ms waited when a fetch return no packets */ #define BEACON_POLL_MS 50 /* time in ms between polling of beacon TX status */ -#define PROTOCOL_VERSION 2 /* v1.3 */ +#define PROTOCOL_VERSION 2 /* v1.6 */ #define PROTOCOL_JSON_RXPK_FRAME_FORMAT 1 -#define XERR_INIT_AVG 128 /* nb of measurements the XTAL correction is averaged on as initial value */ +#define XERR_INIT_AVG 16 /* nb of measurements the XTAL correction is averaged on as initial value */ #define XERR_FILT_COEF 256 /* coefficient for low-pass XTAL error tracking */ #define PKT_PUSH_DATA 0 @@ -118,6 +120,18 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #define DEFAULT_BEACON_POWER 14 #define DEFAULT_BEACON_INFODESC 0 +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE TYPES -------------------------------------------------------- */ + +/* spectral scan */ +typedef struct spectral_scan_s { + bool enable; /* enable spectral scan thread */ + uint32_t freq_hz_start; /* first channel frequency, in Hz */ + uint8_t nb_chan; /* number of channels to scan (200kHz between each channel) */ + uint16_t nb_scan; /* number of scan points for each frequency scan */ + uint32_t pace_s; /* number of seconds between 2 scans in the thread */ +} spectral_scan_t; + /* -------------------------------------------------------------------------- */ /* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ @@ -235,6 +249,7 @@ static int8_t antenna_gain = 0; static struct lgw_tx_gain_lut_s txlut[LGW_RF_CHAIN_NB]; /* TX gain table */ static uint32_t tx_freq_min[LGW_RF_CHAIN_NB]; /* lowest frequency supported by TX chain */ static uint32_t tx_freq_max[LGW_RF_CHAIN_NB]; /* highest frequency supported by TX chain */ +static bool tx_enable[LGW_RF_CHAIN_NB] = {false}; /* Is TX enabled for a given RF chain ? */ static uint32_t nb_pkt_log[LGW_IF_CHAIN_NB][8]; /* [CH][SF] */ static uint32_t nb_pkt_received_lora = 0; @@ -243,6 +258,18 @@ static uint32_t nb_pkt_received_fsk = 0; static struct lgw_conf_debug_s debugconf; static uint32_t nb_pkt_received_ref[16]; +/* Interface type */ +static lgw_com_type_t com_type = LGW_COM_SPI; + +/* Spectral Scan */ +static spectral_scan_t spectral_scan_params = { + .enable = false, + .freq_hz_start = 0, + .nb_chan = 0, + .nb_scan = 0, + .pace_s = 10 +}; + /* -------------------------------------------------------------------------- */ /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ @@ -272,6 +299,7 @@ void thread_down(void); void thread_jit(void); void thread_gps(void); void thread_valid(void); +void thread_spectral_scan(void); /* -------------------------------------------------------------------------- */ /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ @@ -296,7 +324,7 @@ static void sig_handler(int sigio) { } static int parse_SX130x_configuration(const char * conf_file) { - int i, j; + int i, j, number; char param_name[32]; /* used to generate variable parameter names */ const char *str; /* used to store string value from JSON object */ const char conf_obj_name[] = "SX130x_conf"; @@ -305,14 +333,23 @@ static int parse_SX130x_configuration(const char * conf_file) { JSON_Object *conf_obj = NULL; JSON_Object *conf_txgain_obj; JSON_Object *conf_ts_obj; - JSON_Array *conf_txlut_array; + JSON_Object *conf_sx1261_obj = NULL; + JSON_Object *conf_scan_obj = NULL; + JSON_Object *conf_lbt_obj = NULL; + JSON_Object *conf_lbtchan_obj = NULL; + JSON_Array *conf_txlut_array = NULL; + JSON_Array *conf_lbtchan_array = NULL; + JSON_Array *conf_demod_array = NULL; struct lgw_conf_board_s boardconf; struct lgw_conf_rxrf_s rfconf; struct lgw_conf_rxif_s ifconf; - struct lgw_conf_timestamp_s tsconf; + struct lgw_conf_demod_s demodconf; + struct lgw_conf_ftime_s tsconf; + struct lgw_conf_sx1261_s sx1261conf; uint32_t sf, bw, fdev; bool sx1250_tx_lut; + size_t size; /* try to parse JSON */ root_val = json_parse_file_with_comments(conf_file); @@ -332,15 +369,27 @@ static int parse_SX130x_configuration(const char * conf_file) { /* set board configuration */ memset(&boardconf, 0, sizeof boardconf); /* initialize configuration structure */ - str = json_object_get_string(conf_obj, "spidev_path"); + str = json_object_get_string(conf_obj, "com_type"); + if (str == NULL) { + MSG("ERROR: com_type must be configured in %s\n", conf_file); + return -1; + } else if (!strncmp(str, "SPI", 3) || !strncmp(str, "spi", 3)) { + boardconf.com_type = LGW_COM_SPI; + } else if (!strncmp(str, "USB", 3) || !strncmp(str, "usb", 3)) { + boardconf.com_type = LGW_COM_USB; + } else { + MSG("ERROR: invalid com type: %s (should be SPI or USB)\n", str); + return -1; + } + com_type = boardconf.com_type; + str = json_object_get_string(conf_obj, "com_path"); if (str != NULL) { - strncpy(boardconf.spidev_path, str, sizeof(boardconf.spidev_path) - 1); - boardconf.spidev_path[sizeof boardconf.spidev_path - 1] = '\0'; /* ensure string termination */ + strncpy(boardconf.com_path, str, sizeof boardconf.com_path); + boardconf.com_path[sizeof boardconf.com_path - 1] = '\0'; /* ensure string termination */ } else { - MSG("ERROR: spidev path must be configured in %s\n", conf_file); + MSG("ERROR: com_path must be configured in %s\n", conf_file); return -1; } - val = json_object_get_value(conf_obj, "lorawan_public"); /* fetch value (if possible) */ if (json_value_get_type(val) == JSONBoolean) { boardconf.lorawan_public = (bool)json_value_get_boolean(val); @@ -362,7 +411,7 @@ static int parse_SX130x_configuration(const char * conf_file) { MSG("WARNING: Data type for full_duplex seems wrong, please check\n"); boardconf.full_duplex = false; } - MSG("INFO: spidev_path %s, lorawan_public %d, clksrc %d, full_duplex %d\n", boardconf.spidev_path, boardconf.lorawan_public, boardconf.clksrc, boardconf.full_duplex); + MSG("INFO: com_type %s, com_path %s, lorawan_public %d, clksrc %d, full_duplex %d\n", (boardconf.com_type == LGW_COM_SPI) ? "SPI" : "USB", boardconf.com_path, boardconf.lorawan_public, boardconf.clksrc, boardconf.full_duplex); /* all parameters parsed, submitting configuration to the HAL */ if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) { MSG("ERROR: Failed to configure board\n"); @@ -382,37 +431,35 @@ static int parse_SX130x_configuration(const char * conf_file) { MSG("INFO: antenna_gain %d dBi\n", antenna_gain); /* set timestamp configuration */ - conf_ts_obj = json_object_get_object(conf_obj, "precision_timestamp"); + conf_ts_obj = json_object_get_object(conf_obj, "fine_timestamp"); if (conf_ts_obj == NULL) { - MSG("INFO: %s does not contain a JSON object for precision timestamp\n", conf_file); + MSG("INFO: %s does not contain a JSON object for fine timestamp\n", conf_file); } else { val = json_object_get_value(conf_ts_obj, "enable"); /* fetch value (if possible) */ if (json_value_get_type(val) == JSONBoolean) { - tsconf.enable_precision_ts = (bool)json_value_get_boolean(val); + tsconf.enable = (bool)json_value_get_boolean(val); } else { - MSG("WARNING: Data type for precision_timestamp.enable seems wrong, please check\n"); - tsconf.enable_precision_ts = false; + MSG("WARNING: Data type for fine_timestamp.enable seems wrong, please check\n"); + tsconf.enable = false; } - if (tsconf.enable_precision_ts == true) { - val = json_object_get_value(conf_ts_obj, "max_ts_metrics"); /* fetch value (if possible) */ - if (json_value_get_type(val) == JSONNumber) { - tsconf.max_ts_metrics = (uint8_t)json_value_get_number(val); - } else { - MSG("WARNING: Data type for precision_timestamp.max_ts_metrics seems wrong, please check\n"); - tsconf.max_ts_metrics = 0xFF; - } - val = json_object_get_value(conf_ts_obj, "nb_symbols"); /* fetch value (if possible) */ - if (json_value_get_type(val) == JSONNumber) { - tsconf.nb_symbols = (uint8_t)json_value_get_number(val); + if (tsconf.enable == true) { + str = json_object_get_string(conf_ts_obj, "mode"); + if (str == NULL) { + MSG("ERROR: fine_timestamp.mode must be configured in %s\n", conf_file); + return -1; + } else if (!strncmp(str, "high_capacity", 13) || !strncmp(str, "HIGH_CAPACITY", 13)) { + tsconf.mode = LGW_FTIME_MODE_HIGH_CAPACITY; + } else if (!strncmp(str, "all_sf", 6) || !strncmp(str, "ALL_SF", 6)) { + tsconf.mode = LGW_FTIME_MODE_ALL_SF; } else { - MSG("WARNING: Data type for precision_timestamp.nb_symbols seems wrong, please check\n"); - tsconf.nb_symbols = 1; + MSG("ERROR: invalid fine timestamp mode: %s (should be high_capacity or all_sf)\n", str); + return -1; } - MSG("INFO: Configuring precision timestamp: max_ts_metrics:%u, nb_symbols:%u\n", tsconf.max_ts_metrics, tsconf.nb_symbols); + MSG("INFO: Configuring precision timestamp with %s mode\n", str); /* all parameters parsed, submitting configuration to the HAL */ - if (lgw_timestamp_setconf(&tsconf) != LGW_HAL_SUCCESS) { - MSG("ERROR: Failed to configure precision timestamp\n"); + if (lgw_ftime_setconf(&tsconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: Failed to configure fine timestamp\n"); return -1; } } else { @@ -420,6 +467,190 @@ static int parse_SX130x_configuration(const char * conf_file) { } } + /* set SX1261 configuration */ + memset(&sx1261conf, 0, sizeof sx1261conf); /* initialize configuration structure */ + conf_sx1261_obj = json_object_get_object(conf_obj, "sx1261_conf"); /* fetch value (if possible) */ + if (conf_sx1261_obj == NULL) { + MSG("INFO: no configuration for SX1261\n"); + } else { + /* Global SX1261 configuration */ + str = json_object_get_string(conf_sx1261_obj, "spi_path"); + if (str != NULL) { + strncpy(sx1261conf.spi_path, str, sizeof sx1261conf.spi_path); + sx1261conf.spi_path[sizeof sx1261conf.spi_path - 1] = '\0'; /* ensure string termination */ + } else { + MSG("INFO: SX1261 spi_path is not configured in %s\n", conf_file); + } + val = json_object_get_value(conf_sx1261_obj, "rssi_offset"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + sx1261conf.rssi_offset = (int8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for sx1261_conf.rssi_offset seems wrong, please check\n"); + sx1261conf.rssi_offset = 0; + } + + /* Spectral Scan configuration */ + conf_scan_obj = json_object_get_object(conf_sx1261_obj, "spectral_scan"); /* fetch value (if possible) */ + if (conf_scan_obj == NULL) { + MSG("INFO: no configuration for Spectral Scan\n"); + } else { + val = json_object_get_value(conf_scan_obj, "enable"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONBoolean) { + /* Enable background spectral scan thread in packet forwarder */ + spectral_scan_params.enable = (bool)json_value_get_boolean(val); + } else { + MSG("WARNING: Data type for spectral_scan.enable seems wrong, please check\n"); + } + if (spectral_scan_params.enable == true) { + /* Enable the sx1261 radio hardware configuration to allow spectral scan */ + sx1261conf.enable = true; + MSG("INFO: Spectral Scan with SX1261 is enabled\n"); + + /* Get Spectral Scan Parameters */ + val = json_object_get_value(conf_scan_obj, "freq_start"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + spectral_scan_params.freq_hz_start = (uint32_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for spectral_scan.freq_start seems wrong, please check\n"); + } + val = json_object_get_value(conf_scan_obj, "nb_chan"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + spectral_scan_params.nb_chan = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for spectral_scan.nb_chan seems wrong, please check\n"); + } + val = json_object_get_value(conf_scan_obj, "nb_scan"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + spectral_scan_params.nb_scan = (uint16_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for spectral_scan.nb_scan seems wrong, please check\n"); + } + val = json_object_get_value(conf_scan_obj, "pace_s"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + spectral_scan_params.pace_s = (uint32_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for spectral_scan.pace_s seems wrong, please check\n"); + } + } + } + + /* LBT configuration */ + conf_lbt_obj = json_object_get_object(conf_sx1261_obj, "lbt"); /* fetch value (if possible) */ + if (conf_lbt_obj == NULL) { + MSG("INFO: no configuration for LBT\n"); + } else { + val = json_object_get_value(conf_lbt_obj, "enable"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONBoolean) { + sx1261conf.lbt_conf.enable = (bool)json_value_get_boolean(val); + } else { + MSG("WARNING: Data type for lbt.enable seems wrong, please check\n"); + } + if (sx1261conf.lbt_conf.enable == true) { + /* Enable the sx1261 radio hardware configuration to allow spectral scan */ + sx1261conf.enable = true; + MSG("INFO: Listen-Before-Talk with SX1261 is enabled\n"); + + val = json_object_get_value(conf_lbt_obj, "rssi_target"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + sx1261conf.lbt_conf.rssi_target = (int8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for lbt.rssi_target seems wrong, please check\n"); + sx1261conf.lbt_conf.rssi_target = 0; + } + /* set LBT channels configuration */ + conf_lbtchan_array = json_object_get_array(conf_lbt_obj, "channels"); + if (conf_lbtchan_array != NULL) { + sx1261conf.lbt_conf.nb_channel = json_array_get_count(conf_lbtchan_array); + MSG("INFO: %u LBT channels configured\n", sx1261conf.lbt_conf.nb_channel); + } + for (i = 0; i < (int)sx1261conf.lbt_conf.nb_channel; i++) { + /* Sanity check */ + if (i >= LGW_LBT_CHANNEL_NB_MAX) { + MSG("ERROR: LBT channel %d not supported, skip it\n", i); + break; + } + /* Get LBT channel configuration object from array */ + conf_lbtchan_obj = json_array_get_object(conf_lbtchan_array, i); + + /* Channel frequency */ + val = json_object_dotget_value(conf_lbtchan_obj, "freq_hz"); /* fetch value (if possible) */ + if (val != NULL) { + if (json_value_get_type(val) == JSONNumber) { + sx1261conf.lbt_conf.channels[i].freq_hz = (uint32_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for lbt.channels[%d].freq_hz seems wrong, please check\n", i); + sx1261conf.lbt_conf.channels[i].freq_hz = 0; + } + } else { + MSG("ERROR: no frequency defined for LBT channel %d\n", i); + return -1; + } + + /* Channel bandiwdth */ + val = json_object_dotget_value(conf_lbtchan_obj, "bandwidth"); /* fetch value (if possible) */ + if (val != NULL) { + if (json_value_get_type(val) == JSONNumber) { + bw = (uint32_t)json_value_get_number(val); + switch(bw) { + case 500000: sx1261conf.lbt_conf.channels[i].bandwidth = BW_500KHZ; break; + case 250000: sx1261conf.lbt_conf.channels[i].bandwidth = BW_250KHZ; break; + case 125000: sx1261conf.lbt_conf.channels[i].bandwidth = BW_125KHZ; break; + default: sx1261conf.lbt_conf.channels[i].bandwidth = BW_UNDEFINED; + } + } else { + MSG("WARNING: Data type for lbt.channels[%d].freq_hz seems wrong, please check\n", i); + sx1261conf.lbt_conf.channels[i].bandwidth = BW_UNDEFINED; + } + } else { + MSG("ERROR: no bandiwdth defined for LBT channel %d\n", i); + return -1; + } + + /* Channel scan time */ + val = json_object_dotget_value(conf_lbtchan_obj, "scan_time_us"); /* fetch value (if possible) */ + if (val != NULL) { + if (json_value_get_type(val) == JSONNumber) { + if ((uint16_t)json_value_get_number(val) == 128) { + sx1261conf.lbt_conf.channels[i].scan_time_us = LGW_LBT_SCAN_TIME_128_US; + } else if ((uint16_t)json_value_get_number(val) == 5000) { + sx1261conf.lbt_conf.channels[i].scan_time_us = LGW_LBT_SCAN_TIME_5000_US; + } else { + MSG("ERROR: scan time not supported for LBT channel %d, must be 128 or 5000\n", i); + return -1; + } + } else { + MSG("WARNING: Data type for lbt.channels[%d].scan_time_us seems wrong, please check\n", i); + sx1261conf.lbt_conf.channels[i].scan_time_us = 0; + } + } else { + MSG("ERROR: no scan_time_us defined for LBT channel %d\n", i); + return -1; + } + + /* Channel transmit time */ + val = json_object_dotget_value(conf_lbtchan_obj, "transmit_time_ms"); /* fetch value (if possible) */ + if (val != NULL) { + if (json_value_get_type(val) == JSONNumber) { + sx1261conf.lbt_conf.channels[i].transmit_time_ms = (uint16_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for lbt.channels[%d].transmit_time_ms seems wrong, please check\n", i); + sx1261conf.lbt_conf.channels[i].transmit_time_ms = 0; + } + } else { + MSG("ERROR: no transmit_time_ms defined for LBT channel %d\n", i); + return -1; + } + } + } + } + + /* all parameters parsed, submitting configuration to the HAL */ + if (lgw_sx1261_setconf(&sx1261conf) != LGW_HAL_SUCCESS) { + MSG("ERROR: Failed to configure the SX1261 radio\n"); + return -1; + } + } + /* set configuration for RF chains */ for (i = 0; i < LGW_RF_CHAIN_NB; ++i) { memset(&rfconf, 0, sizeof rfconf); /* initialize configuration structure */ @@ -477,6 +708,7 @@ static int parse_SX130x_configuration(const char * conf_file) { val = json_object_dotget_value(conf_obj, param_name); if (json_value_get_type(val) == JSONBoolean) { rfconf.tx_enable = (bool)json_value_get_boolean(val); + tx_enable[i] = rfconf.tx_enable; /* update global context for later check */ if (rfconf.tx_enable == true) { /* tx is enabled on this rf chain, we need its frequency range */ snprintf(param_name, sizeof param_name, "radio_%i.tx_freq_min", i); @@ -592,6 +824,36 @@ static int parse_SX130x_configuration(const char * conf_file) { } } + /* set configuration for demodulators */ + memset(&demodconf, 0, sizeof demodconf); /* initialize configuration structure */ + val = json_object_get_value(conf_obj, "chan_multiSF_All"); /* fetch value (if possible) */ + if (json_value_get_type(val) != JSONObject) { + MSG("INFO: no configuration for LoRa multi-SF spreading factors enabling\n"); + } else { + conf_demod_array = json_object_dotget_array(conf_obj, "chan_multiSF_All.spreading_factor_enable"); + if ((conf_demod_array != NULL) && ((size = json_array_get_count(conf_demod_array)) <= LGW_MULTI_NB)) { + for (i = 0; i < (int)size; i++) { + number = json_array_get_number(conf_demod_array, i); + if (number < 5 || number > 12) { + MSG("WARNING: failed to parse chan_multiSF_All.spreading_factor_enable (wrong value at idx %d)\n", i); + demodconf.multisf_datarate = 0xFF; /* enable all SFs */ + break; + } else { + /* set corresponding bit in the bitmask SF5 is LSB -> SF12 is MSB */ + demodconf.multisf_datarate |= (1 << (number - 5)); + } + } + } else { + MSG("WARNING: failed to parse chan_multiSF_All.spreading_factor_enable\n"); + demodconf.multisf_datarate = 0xFF; /* enable all SFs */ + } + /* all parameters parsed, submitting configuration to the HAL */ + if (lgw_demod_setconf(&demodconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: invalid configuration for demodulation parameters\n"); + return -1; + } + } + /* set configuration for Lora multi-SF channels (bandwidth cannot be set) */ for (i = 0; i < LGW_MULTI_NB; ++i) { memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */ @@ -784,7 +1046,7 @@ static int parse_gateway_configuration(const char * conf_file) { /* server hostname or IP address (optional) */ str = json_object_get_string(conf_obj, "server_address"); if (str != NULL) { - strncpy(serv_addr, str, sizeof(serv_addr) - 1); + strncpy(serv_addr, str, sizeof serv_addr); serv_addr[sizeof serv_addr - 1] = '\0'; /* ensure string termination */ MSG("INFO: server hostname or IP address is configured to \"%s\"\n", serv_addr); } @@ -842,7 +1104,7 @@ static int parse_gateway_configuration(const char * conf_file) { /* GPS module TTY path (optional) */ str = json_object_get_string(conf_obj, "gps_tty_path"); if (str != NULL) { - strncpy(gps_tty_path, str, sizeof(gps_tty_path) - 1); + strncpy(gps_tty_path, str, sizeof gps_tty_path); gps_tty_path[sizeof gps_tty_path - 1] = '\0'; /* ensure string termination */ MSG("INFO: GPS serial port path is configured to \"%s\"\n", gps_tty_path); } @@ -1000,7 +1262,7 @@ static int parse_debug_configuration(const char * conf_file) { /* Get log file configuration */ str = json_object_get_string(conf_obj, "log_file"); if (str != NULL) { - strncpy(debugconf.log_file_name, str, sizeof(debugconf.log_file_name) - 1); + strncpy(debugconf.log_file_name, str, sizeof debugconf.log_file_name); debugconf.log_file_name[sizeof debugconf.log_file_name - 1] = '\0'; /* ensure string termination */ MSG("INFO: setting debug log file name to %s\n", debugconf.log_file_name); } @@ -1179,6 +1441,7 @@ int main(int argc, char ** argv) pthread_t thrid_gps; pthread_t thrid_valid; pthread_t thrid_jit; + pthread_t thrid_ss; /* network socket creation */ struct addrinfo hints; @@ -1221,7 +1484,7 @@ int main(int argc, char ** argv) uint32_t trig_tstamp; uint32_t inst_tstamp; uint64_t eui; -// float temperature; + float temperature; /* statistics variable */ time_t t; @@ -1361,7 +1624,7 @@ int main(int argc, char ** argv) /* look for server address w/ downstream port */ i = getaddrinfo(serv_addr, serv_port_down, &hints, &result); if (i != 0) { - MSG("ERROR: [down] getaddrinfo on address %s (port %s) returned %s\n", serv_addr, serv_port_up, gai_strerror(i)); + MSG("ERROR: [down] getaddrinfo on address %s (port %s) returned %s\n", serv_addr, serv_port_down, gai_strerror(i)); exit(EXIT_FAILURE); } @@ -1372,7 +1635,7 @@ int main(int argc, char ** argv) else break; /* success, get out of loop */ } if (q == NULL) { - MSG("ERROR: [down] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up); + MSG("ERROR: [down] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_down); i = 1; for (q=result; q!=NULL; q=q->ai_next) { getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST); @@ -1390,10 +1653,12 @@ int main(int argc, char ** argv) } freeaddrinfo(result); - /* Board reset */ - if (system("./reset_lgw.sh start") != 0) { - printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); - exit(EXIT_FAILURE); + if (com_type == LGW_COM_SPI) { + /* Board reset */ + if (system("./reset_lgw.sh start") != 0) { + printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); + exit(EXIT_FAILURE); + } } for (l = 0; l < LGW_IF_CHAIN_NB; l++) { @@ -1420,30 +1685,39 @@ int main(int argc, char ** argv) } /* spawn threads to manage upstream and downstream */ - i = pthread_create( &thrid_up, NULL, (void * (*)(void *))thread_up, NULL); + i = pthread_create(&thrid_up, NULL, (void * (*)(void *))thread_up, NULL); if (i != 0) { MSG("ERROR: [main] impossible to create upstream thread\n"); exit(EXIT_FAILURE); } - i = pthread_create( &thrid_down, NULL, (void * (*)(void *))thread_down, NULL); + i = pthread_create(&thrid_down, NULL, (void * (*)(void *))thread_down, NULL); if (i != 0) { MSG("ERROR: [main] impossible to create downstream thread\n"); exit(EXIT_FAILURE); } - i = pthread_create( &thrid_jit, NULL, (void * (*)(void *))thread_jit, NULL); + i = pthread_create(&thrid_jit, NULL, (void * (*)(void *))thread_jit, NULL); if (i != 0) { MSG("ERROR: [main] impossible to create JIT thread\n"); exit(EXIT_FAILURE); } + /* spawn thread for background spectral scan */ + if (spectral_scan_params.enable == true) { + i = pthread_create(&thrid_ss, NULL, (void * (*)(void *))thread_spectral_scan, NULL); + if (i != 0) { + MSG("ERROR: [main] impossible to create Spectral Scan thread\n"); + exit(EXIT_FAILURE); + } + } + /* spawn thread to manage GPS */ if (gps_enabled == true) { - i = pthread_create( &thrid_gps, NULL, (void * (*)(void *))thread_gps, NULL); + i = pthread_create(&thrid_gps, NULL, (void * (*)(void *))thread_gps, NULL); if (i != 0) { MSG("ERROR: [main] impossible to create GPS thread\n"); exit(EXIT_FAILURE); } - i = pthread_create( &thrid_valid, NULL, (void * (*)(void *))thread_valid, NULL); + i = pthread_create(&thrid_valid, NULL, (void * (*)(void *))thread_valid, NULL); if (i != 0) { MSG("ERROR: [main] impossible to create validation thread\n"); exit(EXIT_FAILURE); @@ -1611,34 +1885,49 @@ int main(int argc, char ** argv) } else { printf("# GPS sync is disabled\n"); } - /* + pthread_mutex_lock(&mx_concent); i = lgw_get_temperature(&temperature); + pthread_mutex_unlock(&mx_concent); if (i != LGW_HAL_SUCCESS) { - printf("### Concentrator temperature unknown ###\n"); +// printf("### Concentrator temperature unknown ###\n"); } else { - printf("### Concentrator temperature: %.0f C ###\n", temperature); +// printf("### Concentrator temperature: %.0f C ###\n", temperature); } - */ printf("##### END #####\n"); /* generate a JSON report (will be sent to server by upstream thread) */ pthread_mutex_lock(&mx_stat_rep); if (((gps_enabled == true) && (coord_ok == true)) || (gps_fake_enable == true)) { - snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"lati\":%.5f,\"long\":%.5f,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u}", stat_timestamp, cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok); + snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"lati\":%.5f,\"long\":%.5f,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u,\"temp\":%.1f}", stat_timestamp, cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok, temperature); } else { - snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u}", stat_timestamp, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok); + snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u,\"temp\":%.1f}", stat_timestamp, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok, temperature); } report_ready = true; pthread_mutex_unlock(&mx_stat_rep); } - /* wait for upstream thread to finish (1 fetch cycle max) */ - pthread_join(thrid_up, NULL); - pthread_cancel(thrid_down); /* don't wait for downstream thread */ - pthread_cancel(thrid_jit); /* don't wait for jit thread */ + /* wait for all threads with a COM with the concentrator board to finish (1 fetch cycle max) */ + i = pthread_join(thrid_up, NULL); + if (i != 0) { + printf("ERROR: failed to join upstream thread with %d - %s\n", i, strerror(errno)); + } + i = pthread_join(thrid_down, NULL); + if (i != 0) { + printf("ERROR: failed to join downstream thread with %d - %s\n", i, strerror(errno)); + } + i = pthread_join(thrid_jit, NULL); + if (i != 0) { + printf("ERROR: failed to join JIT thread with %d - %s\n", i, strerror(errno)); + } + if (spectral_scan_params.enable == true) { + i = pthread_join(thrid_ss, NULL); + if (i != 0) { + printf("ERROR: failed to join Spectral Scan thread with %d - %s\n", i, strerror(errno)); + } + } if (gps_enabled == true) { - pthread_cancel(thrid_gps); /* don't wait for GPS thread */ - pthread_cancel(thrid_valid); /* don't wait for validation thread */ + pthread_cancel(thrid_gps); /* don't wait for GPS thread, no access to concentrator board */ + pthread_cancel(thrid_valid); /* don't wait for validation thread, no access to concentrator board */ i = lgw_gps_disable(gps_tty_fd); if (i == LGW_HAL_SUCCESS) { @@ -1662,10 +1951,12 @@ int main(int argc, char ** argv) } } - /* Board reset */ - if (system("./reset_lgw.sh stop") != 0) { - printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); - exit(EXIT_FAILURE); + if (com_type == LGW_COM_SPI) { + /* Board reset */ + if (system("./reset_lgw.sh stop") != 0) { + printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); + exit(EXIT_FAILURE); + } } MSG("INFO: Exiting packet forwarder program\n"); @@ -1889,6 +2180,17 @@ void thread_up(void) { } } + /* Fine timestamp */ + if (p->ftime_received == true) { + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"ftime\":%u", p->ftime); + if (j > 0) { + buff_index += j; + } else { + MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4)); + exit(EXIT_FAILURE); + } + } + /* Packet concentrator channel, RF chain & RX frequency, 34-36 useful chars */ j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf,\"mid\":%2u", p->if_chain, p->rf_chain, ((double)p->freq_hz / 1e6), p->modem_id); if (j > 0) { @@ -2442,7 +2744,7 @@ void thread_down(void) { /* listen to packets and process them until a new PULL request must be sent */ recv_time = send_time; - while ((int)difftimespec(recv_time, send_time) < keepalive_time) { + while (((int)difftimespec(recv_time, send_time) < keepalive_time) && !exit_sig && !quit_sig) { /* try to receive a datagram */ msg_len = recv(sock_down, (void *)buff_down, (sizeof buff_down)-1, 0); @@ -2682,6 +2984,12 @@ void thread_down(void) { txpkt.no_crc = (bool)json_value_get_boolean(val); } + /* Parse "No header" flag (optional field) */ + val = json_object_get_value(txpk_obj,"nhdr"); + if (val != NULL) { + txpkt.no_header = (bool)json_value_get_boolean(val); + } + /* parse target frequency (mandatory) */ val = json_object_get_value(txpk_obj,"freq"); if (val == NULL) { @@ -2699,6 +3007,11 @@ void thread_down(void) { continue; } txpkt.rf_chain = (uint8_t)json_value_get_number(val); + if (tx_enable[txpkt.rf_chain] == false) { + MSG("WARNING: [down] TX is not enabled on RF chain %u, TX aborted\n", txpkt.rf_chain); + json_value_free(root_val); + continue; + } /* parse TX power (optional field) */ val = json_object_get_value(txpk_obj,"powe"); @@ -3000,9 +3313,15 @@ void thread_jit(void) { /* send packet to concentrator */ pthread_mutex_lock(&mx_concent); /* may have to wait for a fetch to finish */ + if (spectral_scan_params.enable == true) { + result = lgw_spectral_scan_abort(); + if (result != LGW_HAL_SUCCESS) { + MSG("WARNING: [jit%d] lgw_spectral_scan_abort failed\n", i); + } + } result = lgw_send(&pkt); pthread_mutex_unlock(&mx_concent); /* free concentrator ASAP */ - if (result == LGW_HAL_ERROR) { + if (result != LGW_HAL_SUCCESS) { pthread_mutex_lock(&mx_meas_dw); meas_nb_tx_fail += 1; pthread_mutex_unlock(&mx_meas_dw); @@ -3025,6 +3344,8 @@ void thread_jit(void) { } } } + + MSG("\nINFO: End of JIT thread\n"); } static void modify_os_time(const uint32_t ppm_tstamp) @@ -3067,7 +3388,7 @@ static void modify_os_time(const uint32_t ppm_tstamp) char buf[128] = {0}; t = time(NULL); local = localtime(&t); - strftime(buf, 64, "%Y-%m-%d %H:%M:%S", local); + strftime(buf, 64, "%Y-%m-%d %H:%M:%S", local); MSG("INFO: [modify_os_time] System time has been synchronized via GPS, %s\n", buf); } } @@ -3300,9 +3621,126 @@ void thread_valid(void) { // fprintf(log_file,"%.18lf,\"track\"\n", xtal_correct); // DEBUG } } - // printf("Time ref: %s, XTAL correct: %s (%.15lf)\n", ref_valid_local?"valid":"invalid", xtal_correct_ok?"valid":"invalid", xtal_correct); // DEBUG + + //printf("Time ref: %s, XTAL correct: %s (%.15lf)\n", ref_valid_local?"valid":"invalid", xtal_correct_ok?"valid":"invalid", xtal_correct); // DEBUG } MSG("\nINFO: End of validation thread\n"); } +/* -------------------------------------------------------------------------- */ +/* --- THREAD 6: BACKGROUND SPECTRAL SCAN --------- */ + +void thread_spectral_scan(void) { + int i, x; + uint32_t freq_hz = spectral_scan_params.freq_hz_start; + uint32_t freq_hz_stop = spectral_scan_params.freq_hz_start + spectral_scan_params.nb_chan * 200E3; + int16_t levels[LGW_SPECTRAL_SCAN_RESULT_SIZE]; + uint16_t results[LGW_SPECTRAL_SCAN_RESULT_SIZE]; + struct timeval tm_start; + lgw_spectral_scan_status_t status; + uint8_t tx_status = TX_FREE; + bool spectral_scan_started; + bool exit_thread = false; + + /* main loop task */ + while (!exit_sig && !quit_sig) { + /* Pace the scan thread (1 sec min), and avoid waiting several seconds when exit */ + for (i = 0; i < (int)(spectral_scan_params.pace_s ? spectral_scan_params.pace_s : 1); i++) { + if (exit_sig || quit_sig) { + exit_thread = true; + break; + } + wait_ms(1000); + } + if (exit_thread == true) { + break; + } + + spectral_scan_started = false; + + /* Start spectral scan (if no downlink programmed) */ + pthread_mutex_lock(&mx_concent); + /* -- Check if there is a downlink programmed */ + for (i = 0; i < LGW_RF_CHAIN_NB; i++) { + if (tx_enable[i] == true) { + x = lgw_status((uint8_t)i, TX_STATUS, &tx_status); + if (x != LGW_HAL_SUCCESS) { + printf("ERROR: failed to get TX status on chain %d\n", i); + } else { + if (tx_status == TX_SCHEDULED || tx_status == TX_EMITTING) { + printf("INFO: skip spectral scan (downlink programmed on RF chain %d)\n", i); + break; /* exit for loop */ + } + } + } + } + if (tx_status != TX_SCHEDULED && tx_status != TX_EMITTING) { + x = lgw_spectral_scan_start(freq_hz, spectral_scan_params.nb_scan); + if (x != 0) { + printf("ERROR: spectral scan start failed\n"); + pthread_mutex_unlock(&mx_concent); + continue; /* main while loop */ + } + spectral_scan_started = true; + } + pthread_mutex_unlock(&mx_concent); + + if (spectral_scan_started == true) { + /* Wait for scan to be completed */ + status = LGW_SPECTRAL_SCAN_STATUS_UNKNOWN; + timeout_start(&tm_start); + do { + /* handle timeout */ + if (timeout_check(tm_start, 2000) != 0) { + printf("ERROR: %s: TIMEOUT on Spectral Scan\n", __FUNCTION__); + break; /* do while */ + } + + /* get spectral scan status */ + pthread_mutex_lock(&mx_concent); + x = lgw_spectral_scan_get_status(&status); + pthread_mutex_unlock(&mx_concent); + if (x != 0) { + printf("ERROR: spectral scan status failed\n"); + break; /* do while */ + } + + /* wait a bit before checking status again */ + wait_ms(10); + } while (status != LGW_SPECTRAL_SCAN_STATUS_COMPLETED && status != LGW_SPECTRAL_SCAN_STATUS_ABORTED); + + if (status == LGW_SPECTRAL_SCAN_STATUS_COMPLETED) { + /* Get spectral scan results */ + memset(levels, 0, sizeof levels); + memset(results, 0, sizeof results); + pthread_mutex_lock(&mx_concent); + x = lgw_spectral_scan_get_results(levels, results); + pthread_mutex_unlock(&mx_concent); + if (x != 0) { + printf("ERROR: spectral scan get results failed\n"); + continue; /* main while loop */ + } + + /* print results */ + printf("SPECTRAL SCAN - %u Hz: ", freq_hz); + for (i = 0; i < LGW_SPECTRAL_SCAN_RESULT_SIZE; i++) { + printf("%u ", results[i]); + } + printf("\n"); + + /* Next frequency to scan */ + freq_hz += 200000; /* 200kHz channels */ + if (freq_hz >= freq_hz_stop) { + freq_hz = spectral_scan_params.freq_hz_start; + } + } else if (status == LGW_SPECTRAL_SCAN_STATUS_ABORTED) { + printf("INFO: %s: spectral scan has been aborted\n", __FUNCTION__); + } else { + printf("ERROR: %s: spectral scan status us unexpected 0x%02X\n", __FUNCTION__, status); + } + } + } + printf("\nINFO: End of Spectral Scan thread\n"); +} + /* --- EOF ------------------------------------------------------------------ */ diff --git a/sx1302fixes/loragw_gps.c b/sx1302fixes/loragw_gps.c index 210b599..f7f6d11 100644 --- a/sx1302fixes/loragw_gps.c +++ b/sx1302fixes/loragw_gps.c @@ -23,7 +23,7 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #include /* bool type */ #include /* printf fprintf */ #include /* memcpy */ -#include +#include /* strerrno */ #include /* struct timespec */ #include /* open */ diff --git a/sx1302fixes/loragw_hal.c b/sx1302fixes/loragw_hal.c index 1703393..21bee7b 100644 --- a/sx1302fixes/loragw_hal.c +++ b/sx1302fixes/loragw_hal.c @@ -23,25 +23,29 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #define _XOPEN_SOURCE 500 #endif +#define _GNU_SOURCE /* needed for qsort_r to be defined */ +#include /* qsort_r */ + #include /* C99 types */ #include /* bool type */ #include /* printf fprintf */ #include /* memcpy */ -#include /* pow, cell */ -#include #include /* symlink, unlink */ -#include #include #include "loragw_reg.h" #include "loragw_hal.h" #include "loragw_aux.h" -#include "loragw_spi.h" +#include "loragw_com.h" #include "loragw_i2c.h" +#include "loragw_lbt.h" #include "loragw_sx1250.h" #include "loragw_sx125x.h" +#include "loragw_sx1261.h" #include "loragw_sx1302.h" - +#include "loragw_sx1302_timestamp.h" +// #include "loragw_stts751.h" +// #include "loragw_ad5338r.h" #include "loragw_debug.h" /* -------------------------------------------------------------------------- */ @@ -54,9 +58,9 @@ License: Revised BSD License, see LICENSE.TXT file include in the project #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #if DEBUG_HAL == 1 - #define DEBUG_MSG(str) fprintf(stderr, str) - #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) - #define DEBUG_ARRAY(a,b,c) for(a=0;acount_us - p2->count_us) <= 24) && + (p1->if_chain == p2->if_chain) && + (p1->datarate == p2->datarate) && + (p1->size == p2->size) && + (memcmp(p1->payload, p2->payload, p1->size) == 0)) { + + return true; + } + } + + return false; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static int remove_pkt(struct lgw_pkt_rx_s * p, uint8_t * nb_pkt, uint8_t pkt_index) { + /* Check input parameters */ + CHECK_NULL(p); + CHECK_NULL(nb_pkt); + if (pkt_index > ((*nb_pkt) - 1)) { + printf("ERROR: failed to remove packet index %u\n", pkt_index); + return -1; + } + + /* Remove pkt from array, by replacing it with last packet of array */ + if (pkt_index == ((*nb_pkt) - 1)) { + /* If we remove last element, just decrement nb packet counter */ + /* Do nothing */ + } else { + /* Copy last packet onto the packet to be removed */ + memcpy(p + pkt_index, p + (*nb_pkt) - 1, sizeof(struct lgw_pkt_rx_s)); + } + + *nb_pkt -= 1; + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int compare_pkt_tmst(const void *a, const void *b, void *arg) +{ + struct lgw_pkt_rx_s *p = (struct lgw_pkt_rx_s *)a; + struct lgw_pkt_rx_s *q = (struct lgw_pkt_rx_s *)b; + int *counter = (int *)arg; + int p_count, q_count; + + p_count = p->count_us; + q_count = q->count_us; + + if (p_count > q_count) { + *counter = *counter + 1; + } + + return (p_count - q_count); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static int merge_packets(struct lgw_pkt_rx_s * p, uint8_t * nb_pkt) { + uint8_t cpt; + int j, k, pkt_dup_idx, x; +#if DEBUG_HAL == 1 + int pkt_idx; +#endif + bool dup_restart = false; + int counter_qsort_swap = 0; + + /* Check input parameters */ + CHECK_NULL(p); + CHECK_NULL(nb_pkt); + + /* Init number of packets in array before merge */ + cpt = *nb_pkt; + + /* --------------------------------------------- */ + /* ---------- For Debug only - START ----------- */ + if (cpt > 0) { + DEBUG_MSG("<----- Searching for DUPLICATEs ------\n"); + } + for (j = 0; j < cpt; j++) { + DEBUG_PRINTF(" %d: tmst=%u SF=%u CRC_status=%d freq=%u chan=%u", j, p[j].count_us, p[j].datarate, p[j].status, p[j].freq_hz, p[j].if_chain); + if (p[j].ftime_received == true) { + DEBUG_PRINTF(" ftime=%u\n", p[j].ftime); + } else { + DEBUG_MSG (" ftime=NONE\n"); + } + } + /* ---------- For Debug only - END ------------- */ + /* --------------------------------------------- */ + + /* Remove duplicates */ + j = 0; + while (j < cpt) { + for (k = (j+1); k < cpt; k++) { + /* Searching for duplicated packets: + -- count_us should be equal or can have up to 24µs of difference (3 samples) + -- channel should be same + -- datarate should be same + -- payload should be same + */ + if (is_same_pkt( &p[j], &p[k])) { + /* We keep the packet which has CRC checked */ + if ((p[j].status == STAT_CRC_OK) && (p[k].status == STAT_CRC_BAD)) { + pkt_dup_idx = k; +#if DEBUG_HAL == 1 + pkt_idx = j; +#endif + } else if ((p[j].status == STAT_CRC_BAD) && (p[k].status == STAT_CRC_OK)) { + pkt_dup_idx = j; +#if DEBUG_HAL == 1 + pkt_idx = k; +#endif + } else { + /* we keep the packet which has a fine timestamp */ + if (p[j].ftime_received == true) { + pkt_dup_idx = k; +#if DEBUG_HAL == 1 + pkt_idx = j; +#endif + } else { + pkt_dup_idx = j; +#if DEBUG_HAL == 1 + pkt_idx = k; +#endif + } + /* sanity check */ + if (((p[j].ftime_received == true) && (p[k].ftime_received == true)) || + ((p[j].ftime_received == false) && (p[k].ftime_received == false))) { + DEBUG_MSG("WARNING: both duplicates have fine timestamps, or none has ? TBC\n"); + } + } + /* pkt_dup_idx contains the index to be deleted */ + DEBUG_PRINTF("duplicate found %d:%d, deleting %d\n", pkt_idx, pkt_dup_idx, pkt_dup_idx); + /* Remove duplicated packet from packet array */ + x = remove_pkt(p, &cpt, pkt_dup_idx); + if (x != 0) { + printf("ERROR: failed to remove packet from array (%d)\n", x); + } + dup_restart = true; + break; + } + } + if (dup_restart == true) { + /* Duplicate found, restart searching for duplicate from first element */ + j = 0; + dup_restart = false; +#if 0 + printf( "restarting search for duplicate\n" ); /* Too verbose */ +#endif + } else { + /* No duplicate found, continue... */ + j += 1; +#if 0 + printf( "no duplicate found\n" ); /* Too verbose */ +#endif + } + } + + /* Sort the packet array by ascending counter_us value */ + qsort_r(p, cpt, sizeof(p[0]), compare_pkt_tmst, &counter_qsort_swap); + DEBUG_PRINTF("%d elements swapped during sorting...\n", counter_qsort_swap); + + /* --------------------------------------------- */ + /* ---------- For Debug only - START ----------- */ + if (cpt > 0) { + DEBUG_MSG("--\n"); + } + for (j = 0; j < cpt; j++) { + DEBUG_PRINTF(" %d: tmst=%u SF=%d CRC_status=%d freq=%u chan=%u", j, p[j].count_us, p[j].datarate, p[j].status, p[j].freq_hz, p[j].if_chain); + if (p[j].ftime_received == true) { + DEBUG_PRINTF(" ftime=%u\n", p[j].ftime); + } else { + DEBUG_MSG (" ftime=NONE\n"); + } + } + if (cpt > 0) { + DEBUG_MSG( " ------------------------------------>\n\n" ); + } + /* ---------- For Debug only - END ------------- */ + /* --------------------------------------------- */ + + /* Update number of packets contained in packet array */ + *nb_pkt = cpt; + + return 0; +} + /* -------------------------------------------------------------------------- */ /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ @@ -230,17 +457,25 @@ int lgw_board_setconf(struct lgw_conf_board_s * conf) { return LGW_HAL_ERROR; } + /* Check input parameters */ + if ((conf->com_type != LGW_COM_SPI) && (conf->com_type != LGW_COM_USB)) { + DEBUG_MSG("ERROR: WRONG COM TYPE\n"); + return LGW_HAL_ERROR; + } + /* set internal config according to parameters */ CONTEXT_LWAN_PUBLIC = conf->lorawan_public; CONTEXT_BOARD.clksrc = conf->clksrc; CONTEXT_BOARD.full_duplex = conf->full_duplex; - strncpy(CONTEXT_SPI, conf->spidev_path, sizeof CONTEXT_SPI); - CONTEXT_SPI[sizeof CONTEXT_SPI - 1] = '\0'; /* ensure string termination */ + CONTEXT_COM_TYPE = conf->com_type; + strncpy(CONTEXT_COM_PATH, conf->com_path, sizeof CONTEXT_COM_PATH); + CONTEXT_COM_PATH[sizeof CONTEXT_COM_PATH - 1] = '\0'; /* ensure string termination */ - DEBUG_PRINTF("Note: board configuration: spidev_path: %s, lorawan_public:%d, clksrc:%d, full_duplex:%d\n", CONTEXT_SPI, - CONTEXT_LWAN_PUBLIC, - CONTEXT_BOARD.clksrc, - CONTEXT_BOARD.full_duplex); + DEBUG_PRINTF("Note: board configuration: com_type: %s, com_path: %s, lorawan_public:%d, clksrc:%d, full_duplex:%d\n", (CONTEXT_COM_TYPE == LGW_COM_SPI) ? "SPI" : "USB", + CONTEXT_COM_PATH, + CONTEXT_LWAN_PUBLIC, + CONTEXT_BOARD.clksrc, + CONTEXT_BOARD.full_duplex); return LGW_HAL_SUCCESS; } @@ -474,6 +709,16 @@ int lgw_rxif_setconf(uint8_t if_chain, struct lgw_conf_rxif_s * conf) { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +int lgw_demod_setconf(struct lgw_conf_demod_s * conf) { + CHECK_NULL(conf); + + CONTEXT_DEMOD.multisf_datarate = conf->multisf_datarate; + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + int lgw_txgain_setconf(uint8_t rf_chain, struct lgw_tx_gain_lut_s * conf) { int i; @@ -506,7 +751,7 @@ int lgw_txgain_setconf(uint8_t rf_chain, struct lgw_tx_gain_lut_s * conf) { return LGW_HAL_ERROR; } if (conf->lut[i].pwr_idx > 22) { - DEBUG_MSG("ERROR: TX gain LUT: SX1250 power iundex must not exceed 22\n"); + DEBUG_MSG("ERROR: TX gain LUT: SX1250 power index must not exceed 22\n"); return LGW_HAL_ERROR; } @@ -529,12 +774,43 @@ int lgw_txgain_setconf(uint8_t rf_chain, struct lgw_tx_gain_lut_s * conf) { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -int lgw_timestamp_setconf(struct lgw_conf_timestamp_s * conf) { +int lgw_ftime_setconf(struct lgw_conf_ftime_s * conf) { + CHECK_NULL(conf); + + CONTEXT_FINE_TIMESTAMP.enable = conf->enable; + CONTEXT_FINE_TIMESTAMP.mode = conf->mode; + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_sx1261_setconf(struct lgw_conf_sx1261_s * conf) { + int i; + CHECK_NULL(conf); - CONTEXT_TIMESTAMP.enable_precision_ts = conf->enable_precision_ts; - CONTEXT_TIMESTAMP.max_ts_metrics = conf->max_ts_metrics; - CONTEXT_TIMESTAMP.nb_symbols = conf->nb_symbols; + /* Set the SX1261 global conf */ + CONTEXT_SX1261.enable = conf->enable; + strncpy(CONTEXT_SX1261.spi_path, conf->spi_path, sizeof CONTEXT_SX1261.spi_path); + CONTEXT_SX1261.spi_path[sizeof CONTEXT_SX1261.spi_path - 1] = '\0'; /* ensure string termination */ + CONTEXT_SX1261.rssi_offset = conf->rssi_offset; + + /* Set the LBT conf */ + CONTEXT_SX1261.lbt_conf.enable = conf->lbt_conf.enable; + CONTEXT_SX1261.lbt_conf.rssi_target = conf->lbt_conf.rssi_target; + CONTEXT_SX1261.lbt_conf.nb_channel = conf->lbt_conf.nb_channel; + for (i = 0; i < CONTEXT_SX1261.lbt_conf.nb_channel; i++) { + if (conf->lbt_conf.channels[i].bandwidth != BW_125KHZ && conf->lbt_conf.channels[i].bandwidth != BW_250KHZ) { + printf("ERROR: bandwidth not supported for LBT channel %d\n", i); + return LGW_HAL_ERROR; + } + if (conf->lbt_conf.channels[i].scan_time_us != LGW_LBT_SCAN_TIME_128_US && conf->lbt_conf.channels[i].scan_time_us != LGW_LBT_SCAN_TIME_5000_US) { + printf("ERROR: scan_time_us not supported for LBT channel %d\n", i); + return LGW_HAL_ERROR; + } + CONTEXT_SX1261.lbt_conf.channels[i] = conf->lbt_conf.channels[i]; + } return LGW_HAL_SUCCESS; } @@ -571,18 +847,27 @@ int lgw_debug_setconf(struct lgw_conf_debug_s * conf) { int lgw_start(void) { int i, err; - int reg_stat; + uint8_t fw_version_agc; + + DEBUG_PRINTF(" --- %s\n", "IN"); if (CONTEXT_STARTED == true) { DEBUG_MSG("Note: LoRa concentrator already started, restarting it now\n"); } - reg_stat = lgw_connect(CONTEXT_SPI); - if (reg_stat == LGW_REG_ERROR) { + err = lgw_connect(CONTEXT_COM_TYPE, CONTEXT_COM_PATH); + if (err == LGW_REG_ERROR) { DEBUG_MSG("ERROR: FAIL TO CONNECT BOARD\n"); return LGW_HAL_ERROR; } + /* Set all GPIOs to 0 */ + err = sx1302_set_gpio(0x00); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to set all GPIOs to 0\n"); + return LGW_HAL_ERROR; + } + /* Calibrate radios */ err = sx1302_radio_calibrate(&CONTEXT_RF_CHAIN[0], CONTEXT_BOARD.clksrc, &CONTEXT_TX_GAIN_LUT[0]); if (err != LGW_REG_SUCCESS) { @@ -593,95 +878,188 @@ int lgw_start(void) { /* Setup radios for RX */ for (i = 0; i < LGW_RF_CHAIN_NB; i++) { if (CONTEXT_RF_CHAIN[i].enable == true) { - sx1302_radio_reset(i, CONTEXT_RF_CHAIN[i].type); + /* Reset the radio */ + err = sx1302_radio_reset(i, CONTEXT_RF_CHAIN[i].type); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to reset radio %d\n", i); + return LGW_HAL_ERROR; + } + + /* Setup the radio */ switch (CONTEXT_RF_CHAIN[i].type) { case LGW_RADIO_TYPE_SX1250: - sx1250_setup(i, CONTEXT_RF_CHAIN[i].freq_hz, CONTEXT_RF_CHAIN[i].single_input_mode); + err = sx1250_setup(i, CONTEXT_RF_CHAIN[i].freq_hz, CONTEXT_RF_CHAIN[i].single_input_mode); break; case LGW_RADIO_TYPE_SX1255: case LGW_RADIO_TYPE_SX1257: - sx125x_setup(i, CONTEXT_BOARD.clksrc, true, CONTEXT_RF_CHAIN[i].type, CONTEXT_RF_CHAIN[i].freq_hz); + err = sx125x_setup(i, CONTEXT_BOARD.clksrc, true, CONTEXT_RF_CHAIN[i].type, CONTEXT_RF_CHAIN[i].freq_hz); break; default: - DEBUG_PRINTF("ERROR: RADIO TYPE NOT SUPPORTED (RF_CHAIN %d)\n", i); + printf("ERROR: RADIO TYPE NOT SUPPORTED (RF_CHAIN %d)\n", i); return LGW_HAL_ERROR; } - sx1302_radio_set_mode(i, CONTEXT_RF_CHAIN[i].type); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to setup radio %d\n", i); + return LGW_HAL_ERROR; + } + + /* Set radio mode */ + err = sx1302_radio_set_mode(i, CONTEXT_RF_CHAIN[i].type); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to set mode for radio %d\n", i); + return LGW_HAL_ERROR; + } } } /* Select the radio which provides the clock to the sx1302 */ - sx1302_radio_clock_select(CONTEXT_BOARD.clksrc); + err = sx1302_radio_clock_select(CONTEXT_BOARD.clksrc); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to get clock from radio %u\n", CONTEXT_BOARD.clksrc); + return LGW_HAL_ERROR; + } /* Release host control on radio (will be controlled by AGC) */ - sx1302_radio_host_ctrl(false); + err = sx1302_radio_host_ctrl(false); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to release control over radios\n"); + return LGW_HAL_ERROR; + } /* Basic initialization of the sx1302 */ - sx1302_init(&CONTEXT_TIMESTAMP); + err = sx1302_init(&CONTEXT_FINE_TIMESTAMP); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to initialize SX1302\n"); + return LGW_HAL_ERROR; + } /* Configure PA/LNA LUTs */ - sx1302_pa_lna_lut_configure(); + err = sx1302_pa_lna_lut_configure(&CONTEXT_BOARD); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 PA/LNA LUT\n"); + return LGW_HAL_ERROR; + } /* Configure Radio FE */ - sx1302_radio_fe_configure(); + err = sx1302_radio_fe_configure(); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 radio frontend\n"); + return LGW_HAL_ERROR; + } /* Configure the Channelizer */ - sx1302_channelizer_configure(CONTEXT_IF_CHAIN, false); + err = sx1302_channelizer_configure(CONTEXT_IF_CHAIN, false); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 channelizer\n"); + return LGW_HAL_ERROR; + } - /* configure LoRa 'multi' demodulators */ - sx1302_lora_correlator_configure(); - sx1302_lora_modem_configure(CONTEXT_RF_CHAIN[0].freq_hz); /* TODO: freq_hz used to confiogure freq to time drift, based on RF0 center freq only */ + /* configure LoRa 'multi-sf' modems */ + err = sx1302_lora_correlator_configure(CONTEXT_IF_CHAIN, &(CONTEXT_DEMOD)); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 LoRa modem correlators\n"); + return LGW_HAL_ERROR; + } + err = sx1302_lora_modem_configure(CONTEXT_RF_CHAIN[0].freq_hz); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 LoRa modems\n"); + return LGW_HAL_ERROR; + } - /* configure LoRa 'stand-alone' modem */ + /* configure LoRa 'single-sf' modem */ if (CONTEXT_IF_CHAIN[8].enable == true) { - sx1302_lora_service_correlator_configure(&(CONTEXT_LORA_SERVICE)); - sx1302_lora_service_modem_configure(&(CONTEXT_LORA_SERVICE), CONTEXT_RF_CHAIN[0].freq_hz); /* TODO: freq_hz used to confiogure freq to time drift, based on RF0 center freq only */ + err = sx1302_lora_service_correlator_configure(&(CONTEXT_LORA_SERVICE)); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 LoRa Service modem correlators\n"); + return LGW_HAL_ERROR; + } + err = sx1302_lora_service_modem_configure(&(CONTEXT_LORA_SERVICE), CONTEXT_RF_CHAIN[0].freq_hz); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 LoRa Service modem\n"); + return LGW_HAL_ERROR; + } } /* configure FSK modem */ if (CONTEXT_IF_CHAIN[9].enable == true) { - sx1302_fsk_configure(&(CONTEXT_FSK)); + err = sx1302_fsk_configure(&(CONTEXT_FSK)); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 FSK modem\n"); + return LGW_HAL_ERROR; + } } /* configure syncword */ - sx1302_lora_syncword(CONTEXT_LWAN_PUBLIC, CONTEXT_LORA_SERVICE.datarate); + err = sx1302_lora_syncword(CONTEXT_LWAN_PUBLIC, CONTEXT_LORA_SERVICE.datarate); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 LoRa syncword\n"); + return LGW_HAL_ERROR; + } /* enable demodulators - to be done before starting AGC/ARB */ - sx1302_modem_enable(); + err = sx1302_modem_enable(); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to enable SX1302 modems\n"); + return LGW_HAL_ERROR; + } - /* Load firmware */ + /* Load AGC firmware */ switch (CONTEXT_RF_CHAIN[CONTEXT_BOARD.clksrc].type) { case LGW_RADIO_TYPE_SX1250: DEBUG_MSG("Loading AGC fw for sx1250\n"); - if (sx1302_agc_load_firmware(agc_firmware_sx1250) != LGW_HAL_SUCCESS) { + err = sx1302_agc_load_firmware(agc_firmware_sx1250); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to load AGC firmware for sx1250\n"); return LGW_HAL_ERROR; } + fw_version_agc = FW_VERSION_AGC_SX1250; break; + case LGW_RADIO_TYPE_SX1255: case LGW_RADIO_TYPE_SX1257: DEBUG_MSG("Loading AGC fw for sx125x\n"); - if (sx1302_agc_load_firmware(agc_firmware_sx125x) != LGW_HAL_SUCCESS) { + err = sx1302_agc_load_firmware(agc_firmware_sx125x); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to load AGC firmware for sx125x\n"); return LGW_HAL_ERROR; } + fw_version_agc = FW_VERSION_AGC_SX125X; break; default: - break; + printf("ERROR: failed to load AGC firmware, radio type not supported (%d)\n", CONTEXT_RF_CHAIN[CONTEXT_BOARD.clksrc].type); + return LGW_HAL_ERROR; } - if (sx1302_agc_start(FW_VERSION_AGC, CONTEXT_RF_CHAIN[CONTEXT_BOARD.clksrc].type, SX1302_AGC_RADIO_GAIN_AUTO, SX1302_AGC_RADIO_GAIN_AUTO, (CONTEXT_BOARD.full_duplex == true) ? 1 : 0) != LGW_HAL_SUCCESS) { + err = sx1302_agc_start(fw_version_agc, CONTEXT_RF_CHAIN[CONTEXT_BOARD.clksrc].type, SX1302_AGC_RADIO_GAIN_AUTO, SX1302_AGC_RADIO_GAIN_AUTO, CONTEXT_BOARD.full_duplex, CONTEXT_SX1261.lbt_conf.enable); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to start AGC firmware\n"); return LGW_HAL_ERROR; } + + /* Load ARB firmware */ DEBUG_MSG("Loading ARB fw\n"); - if (sx1302_arb_load_firmware(arb_firmware) != LGW_HAL_SUCCESS) { + err = sx1302_arb_load_firmware(arb_firmware); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to load ARB firmware\n"); return LGW_HAL_ERROR; } - if (sx1302_arb_start(FW_VERSION_ARB) != LGW_HAL_SUCCESS) { + err = sx1302_arb_start(FW_VERSION_ARB, &CONTEXT_FINE_TIMESTAMP); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to start ARB firmware\n"); return LGW_HAL_ERROR; } /* static TX configuration */ - sx1302_tx_configure(CONTEXT_RF_CHAIN[CONTEXT_BOARD.clksrc].type); + err = sx1302_tx_configure(CONTEXT_RF_CHAIN[CONTEXT_BOARD.clksrc].type); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to configure SX1302 TX path\n"); + return LGW_HAL_ERROR; + } /* enable GPS */ - sx1302_gps_enable(true); + err = sx1302_gps_enable(true); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to enable GPS on sx1302\n"); + return LGW_HAL_ERROR; + } /* For debug logging */ #if HAL_DEBUG_FILE_LOG @@ -714,43 +1092,120 @@ int lgw_start(void) { /* Configure the pseudo-random generator (For Debug) */ dbg_init_random(); -#if 0 - /* Configure a GPIO to be toggled for debug purpose */ - dbg_init_gpio(); -#endif +// if (CONTEXT_COM_TYPE == LGW_COM_SPI) { +// /* Find the temperature sensor on the known supported ports */ +// for (i = 0; i < (int)(sizeof I2C_PORT_TEMP_SENSOR); i++) { +// ts_addr = I2C_PORT_TEMP_SENSOR[i]; +// err = i2c_linuxdev_open(I2C_DEVICE, ts_addr, &ts_fd); +// if (err != LGW_I2C_SUCCESS) { +// printf("ERROR: failed to open I2C for temperature sensor on port 0x%02X\n", ts_addr); +// return LGW_HAL_ERROR; +// } +// +// err = stts751_configure(ts_fd, ts_addr); +// if (err != LGW_I2C_SUCCESS) { +// printf("INFO: no temperature sensor found on port 0x%02X\n", ts_addr); +// i2c_linuxdev_close(ts_fd); +// ts_fd = -1; +// } else { +// printf("INFO: found temperature sensor on port 0x%02X\n", ts_addr); +// break; +// } +// } +// if (i == sizeof I2C_PORT_TEMP_SENSOR) { +// printf("ERROR: no temperature sensor found.\n"); +// return LGW_HAL_ERROR; +// } +// +// /* Configure ADC AD338R for full duplex (CN490 reference design) */ +// if (CONTEXT_BOARD.full_duplex == true) { +// err = i2c_linuxdev_open(I2C_DEVICE, I2C_PORT_DAC_AD5338R, &ad_fd); +// if (err != LGW_I2C_SUCCESS) { +// printf("ERROR: failed to open I2C for ad5338r\n"); +// return LGW_HAL_ERROR; +// } +// +// err = ad5338r_configure(ad_fd, I2C_PORT_DAC_AD5338R); +// if (err != LGW_I2C_SUCCESS) { +// printf("ERROR: failed to configure ad5338r\n"); +// i2c_linuxdev_close(ad_fd); +// ad_fd = -1; +// return LGW_HAL_ERROR; +// } +// +// /* Turn off the PA: set DAC output to 0V */ +// uint8_t volt_val[AD5338R_CMD_SIZE] = { 0x39, (uint8_t)VOLTAGE2HEX_H(0), (uint8_t)VOLTAGE2HEX_L(0) }; +// err = ad5338r_write(ad_fd, I2C_PORT_DAC_AD5338R, volt_val); +// if (err != LGW_I2C_SUCCESS) { +// printf("ERROR: AD5338R: failed to set DAC output to 0V\n"); +// return LGW_HAL_ERROR; +// } +// printf("INFO: AD5338R: Set DAC output to 0x%02X 0x%02X\n", (uint8_t)VOLTAGE2HEX_H(0), (uint8_t)VOLTAGE2HEX_L(0)); +// } +// } + + /* Connect to the external sx1261 for LBT or Spectral Scan */ + if (CONTEXT_SX1261.enable == true) { + err = sx1261_connect(CONTEXT_COM_TYPE, (CONTEXT_COM_TYPE == LGW_COM_SPI) ? CONTEXT_SX1261.spi_path : NULL); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to connect to the sx1261 radio (LBT/Spectral Scan)\n"); + return LGW_HAL_ERROR; + } - /* Try to configure temperature sensor STTS751-0DP3F */ -/* - ts_addr = I2C_PORT_TEMP_SENSOR_0; - i2c_linuxdev_open(I2C_DEVICE, ts_addr, &ts_fd); - err = stts751_configure(ts_fd, ts_addr); - if (err != LGW_I2C_SUCCESS) { - i2c_linuxdev_close(ts_fd); - ts_fd = -1; - // * Not found, try to configure temperature sensor STTS751-1DP3F - ts_addr = I2C_PORT_TEMP_SENSOR_1; - i2c_linuxdev_open(I2C_DEVICE, ts_addr, &ts_fd); - err = stts751_configure(ts_fd, ts_addr); - if (err != LGW_I2C_SUCCESS) { - printf("ERROR: failed to configure the temperature sensor\n"); + err = sx1261_load_pram(); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to patch sx1261 radio for LBT/Spectral Scan\n"); + return LGW_HAL_ERROR; + } + + err = sx1261_calibrate(CONTEXT_RF_CHAIN[0].freq_hz); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to calibrate sx1261 radio\n"); + return LGW_HAL_ERROR; + } + + err = sx1261_setup(); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to setup sx1261 radio\n"); return LGW_HAL_ERROR; } } -*/ + + /* Set CONFIG_DONE GPIO to 1 (turn on the corresponding LED) */ + err = sx1302_set_gpio(0x01); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: failed to set CONFIG_DONE GPIO\n"); + return LGW_HAL_ERROR; + } + /* set hal state */ CONTEXT_STARTED = true; + DEBUG_PRINTF(" --- %s\n", "OUT"); + return LGW_HAL_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_stop(void) { - int i, err; + int i, x, err = LGW_HAL_SUCCESS; - DEBUG_MSG("INFO: aborting TX\n"); + DEBUG_PRINTF(" --- %s\n", "IN"); + + if (CONTEXT_STARTED == false) { + DEBUG_MSG("Note: LoRa concentrator was not started...\n"); + return LGW_HAL_SUCCESS; + } + + /* Abort current TX if needed */ for (i = 0; i < LGW_RF_CHAIN_NB; i++) { - lgw_abort_tx(i); + DEBUG_PRINTF("INFO: aborting TX on chain %u\n", i); + x = lgw_abort_tx(i); + if (x != LGW_HAL_SUCCESS) { + printf("WARNING: failed to get abort TX on chain %u\n", i); + err = LGW_HAL_ERROR; + } } /* Close log file */ @@ -760,35 +1215,53 @@ int lgw_stop(void) { } DEBUG_MSG("INFO: Disconnecting\n"); - lgw_disconnect(); + x = lgw_disconnect(); + if (x != LGW_HAL_SUCCESS) { + printf("ERROR: failed to disconnect concentrator\n"); + err = LGW_HAL_ERROR; + } - DEBUG_MSG("INFO: Closing I2C\n"); - //err = i2c_linuxdev_close(ts_fd); - //if (err != 0) { - // printf("ERROR: failed to close I2C device (err=%i)\n", err); - //} +// if (CONTEXT_COM_TYPE == LGW_COM_SPI) { +// DEBUG_MSG("INFO: Closing I2C for temperature sensor\n"); +// x = i2c_linuxdev_close(ts_fd); +// if (x != 0) { +// printf("ERROR: failed to close I2C temperature sensor device (err=%i)\n", x); +// err = LGW_HAL_ERROR; +// } +// +// if (CONTEXT_BOARD.full_duplex == true) { +// DEBUG_MSG("INFO: Closing I2C for AD5338R\n"); +// x = i2c_linuxdev_close(ad_fd); +// if (x != 0) { +// printf("ERROR: failed to close I2C AD5338R device (err=%i)\n", x); +// err = LGW_HAL_ERROR; +// } +// } +// } CONTEXT_STARTED = false; - return LGW_HAL_SUCCESS; + + DEBUG_PRINTF(" --- %s\n", "OUT"); + + return err; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) { int res; - uint8_t nb_pkt_fetched = 0; - uint16_t nb_pkt_found = 0; - uint16_t nb_pkt_left = 0; - //float current_temperature, rssi_temperature_offset; + uint8_t nb_pkt_fetched = 0; + uint8_t nb_pkt_found = 0; + uint8_t nb_pkt_left = 0; float current_temperature = 30.0; float rssi_temperature_offset; + /* performances variables */ + struct timeval tm; - /* Check that AGC/ARB firmwares are not corrupted, and update internal counter */ - /* WARNING: this needs to be called regularly by the upper layer */ - res = sx1302_update(); - if (res != LGW_REG_SUCCESS) { - return LGW_HAL_ERROR; - } + DEBUG_PRINTF(" --- %s\n", "IN"); + + /* Record function start time */ + _meas_time_start(&tm); /* Get packets from SX1302, if any */ res = sx1302_fetch(&nb_pkt_fetched); @@ -796,7 +1269,17 @@ int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) { printf("ERROR: failed to fetch packets from SX1302\n"); return LGW_HAL_ERROR; } + + /* Update internal counter */ + /* WARNING: this needs to be called regularly by the upper layer */ + res = sx1302_update(); + if (res != LGW_REG_SUCCESS) { + return LGW_HAL_ERROR; + } + + /* Exit now if no packet fetched */ if (nb_pkt_fetched == 0) { + _meas_time_stop(1, tm, __FUNCTION__); return 0; } if (nb_pkt_fetched > max_pkt) { @@ -805,18 +1288,21 @@ int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) { } /* Apply RSSI temperature compensation */ - //res = stts751_get_temperature(ts_fd, ts_addr, ¤t_temperature); - //if (res != LGW_I2C_SUCCESS) { - // printf("ERROR: failed to get current temperature\n"); - // return LGW_HAL_ERROR; - //} +// res = lgw_get_temperature(¤t_temperature); +// if (res != LGW_I2C_SUCCESS) { +// printf("ERROR: failed to get current temperature\n"); +// return LGW_HAL_ERROR; +// } /* Iterate on the RX buffer to get parsed packets */ for (nb_pkt_found = 0; nb_pkt_found < ((nb_pkt_fetched <= max_pkt) ? nb_pkt_fetched : max_pkt); nb_pkt_found++) { /* Get packet and move to next one */ res = sx1302_parse(&lgw_context, &pkt_data[nb_pkt_found]); - if (res != LGW_REG_SUCCESS) { - printf("ERROR: failed to parse fetched packet %d, aborting...\n", nb_pkt_found); + if (res == LGW_REG_WARNING) { + printf("WARNING: parsing error on packet %d, discarding fetched packets\n", nb_pkt_found); + return LGW_HAL_SUCCESS; + } else if (res == LGW_REG_ERROR) { + printf("ERROR: fatal parsing error on packet %d, aborting...\n", nb_pkt_found); return LGW_HAL_ERROR; } @@ -832,15 +1318,39 @@ int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) { DEBUG_PRINTF("INFO: nb pkt found:%u left:%u\n", nb_pkt_found, nb_pkt_left); + /* Remove duplicated packets generated by double demod when precision timestamp is enabled */ + if ((nb_pkt_found > 0) && (CONTEXT_FINE_TIMESTAMP.enable == true)) { + res = merge_packets(pkt_data, &nb_pkt_found); + if (res != 0) { + printf("WARNING: failed to remove duplicated packets\n"); + } + + DEBUG_PRINTF("INFO: nb pkt found:%u (after de-duplicating)\n", nb_pkt_found); + } + + _meas_time_stop(1, tm, __FUNCTION__); + + DEBUG_PRINTF(" --- %s\n", "OUT"); + return nb_pkt_found; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_send(struct lgw_pkt_tx_s * pkt_data) { + int err; + bool lbt_tx_allowed; + /* performances variables */ + struct timeval tm; + + DEBUG_PRINTF(" --- %s\n", "IN"); + + /* Record function start time */ + _meas_time_start(&tm); + /* check if the concentrator is running */ if (CONTEXT_STARTED == false) { - DEBUG_MSG("ERROR: CONCENTRATOR IS NOT RUNNING, START IT BEFORE SENDING\n"); + printf("ERROR: CONCENTRATOR IS NOT RUNNING, START IT BEFORE SENDING\n"); return LGW_HAL_ERROR; } @@ -848,66 +1358,139 @@ int lgw_send(struct lgw_pkt_tx_s * pkt_data) { /* check input range (segfault prevention) */ if (pkt_data->rf_chain >= LGW_RF_CHAIN_NB) { - DEBUG_MSG("ERROR: INVALID RF_CHAIN TO SEND PACKETS\n"); + printf("ERROR: INVALID RF_CHAIN TO SEND PACKETS\n"); return LGW_HAL_ERROR; } /* check input variables */ if (CONTEXT_RF_CHAIN[pkt_data->rf_chain].tx_enable == false) { - DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED FOR TX ON SELECTED BOARD\n"); + printf("ERROR: SELECTED RF_CHAIN IS DISABLED FOR TX ON SELECTED BOARD\n"); return LGW_HAL_ERROR; } if (CONTEXT_RF_CHAIN[pkt_data->rf_chain].enable == false) { - DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED\n"); + printf("ERROR: SELECTED RF_CHAIN IS DISABLED\n"); return LGW_HAL_ERROR; } if (!IS_TX_MODE(pkt_data->tx_mode)) { - DEBUG_MSG("ERROR: TX_MODE NOT SUPPORTED\n"); + printf("ERROR: TX_MODE NOT SUPPORTED\n"); return LGW_HAL_ERROR; } if (pkt_data->modulation == MOD_LORA) { if (!IS_LORA_BW(pkt_data->bandwidth)) { - DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA TX\n"); + printf("ERROR: BANDWIDTH NOT SUPPORTED BY LORA TX\n"); return LGW_HAL_ERROR; } if (!IS_LORA_DR(pkt_data->datarate)) { - DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY LORA TX\n"); + printf("ERROR: DATARATE NOT SUPPORTED BY LORA TX\n"); return LGW_HAL_ERROR; } if (!IS_LORA_CR(pkt_data->coderate)) { - DEBUG_MSG("ERROR: CODERATE NOT SUPPORTED BY LORA TX\n"); + printf("ERROR: CODERATE NOT SUPPORTED BY LORA TX\n"); return LGW_HAL_ERROR; } if (pkt_data->size > 255) { - DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR LORA TX\n"); + printf("ERROR: PAYLOAD LENGTH TOO BIG FOR LORA TX\n"); return LGW_HAL_ERROR; } } else if (pkt_data->modulation == MOD_FSK) { if((pkt_data->f_dev < 1) || (pkt_data->f_dev > 200)) { - DEBUG_MSG("ERROR: TX FREQUENCY DEVIATION OUT OF ACCEPTABLE RANGE\n"); + printf("ERROR: TX FREQUENCY DEVIATION OUT OF ACCEPTABLE RANGE\n"); return LGW_HAL_ERROR; } if(!IS_FSK_DR(pkt_data->datarate)) { - DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n"); + printf("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n"); return LGW_HAL_ERROR; } if (pkt_data->size > 255) { - DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR FSK TX\n"); + printf("ERROR: PAYLOAD LENGTH TOO BIG FOR FSK TX\n"); return LGW_HAL_ERROR; } } else if (pkt_data->modulation == MOD_CW) { /* do nothing */ } else { - DEBUG_MSG("ERROR: INVALID TX MODULATION\n"); + printf("ERROR: INVALID TX MODULATION\n"); + return LGW_HAL_ERROR; + } + + /* Set PA gain with AD5338R when using full duplex CN490 ref design */ +// if (CONTEXT_BOARD.full_duplex == true) { +// uint8_t volt_val[AD5338R_CMD_SIZE] = {0x39, VOLTAGE2HEX_H(2.51), VOLTAGE2HEX_L(2.51)}; /* set to 2.51V */ +// err = ad5338r_write(ad_fd, I2C_PORT_DAC_AD5338R, volt_val); +// if (err != LGW_I2C_SUCCESS) { +// printf("ERROR: failed to set voltage by ad5338r\n"); +// return LGW_HAL_ERROR; +// } +// printf("INFO: AD5338R: Set DAC output to 0x%02X 0x%02X\n", (uint8_t)VOLTAGE2HEX_H(2.51), (uint8_t)VOLTAGE2HEX_L(2.51)); +// } + + /* Start Listen-Before-Talk */ + if (CONTEXT_SX1261.lbt_conf.enable == true) { + err = lgw_lbt_start(&CONTEXT_SX1261, pkt_data); + if (err != 0) { + printf("ERROR: failed to start LBT\n"); + return LGW_HAL_ERROR; + } + } + + /* Send the TX request to the concentrator */ + err = sx1302_send(CONTEXT_RF_CHAIN[pkt_data->rf_chain].type, &CONTEXT_TX_GAIN_LUT[pkt_data->rf_chain], CONTEXT_LWAN_PUBLIC, &CONTEXT_FSK, pkt_data); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: %s: Failed to send packet\n", __FUNCTION__); + + if (CONTEXT_SX1261.lbt_conf.enable == true) { + err = lgw_lbt_stop(); + if (err != 0) { + printf("ERROR: %s: Failed to stop LBT\n", __FUNCTION__); + } + } + return LGW_HAL_ERROR; } - return sx1302_send(CONTEXT_RF_CHAIN[pkt_data->rf_chain].type, &CONTEXT_TX_GAIN_LUT[pkt_data->rf_chain], CONTEXT_LWAN_PUBLIC, &CONTEXT_FSK, pkt_data); + _meas_time_stop(1, tm, __FUNCTION__); + + /* Stop Listen-Before-Talk */ + if (CONTEXT_SX1261.lbt_conf.enable == true) { + err = lgw_lbt_tx_status(pkt_data->rf_chain, &lbt_tx_allowed); + if (err != 0) { + printf("ERROR: %s: Failed to get LBT TX status, TX aborted\n", __FUNCTION__); + err = sx1302_tx_abort(pkt_data->rf_chain); + if (err != 0) { + printf("ERROR: %s: Failed to abort TX\n", __FUNCTION__); + } + err = lgw_lbt_stop(); + if (err != 0) { + printf("ERROR: %s: Failed to stop LBT\n", __FUNCTION__); + } + return LGW_HAL_ERROR; + } + if (lbt_tx_allowed == true) { + printf("LBT: packet is allowed to be transmitted\n"); + } else { + printf("LBT: (ERROR) packet is NOT allowed to be transmitted\n"); + } + + err = lgw_lbt_stop(); + if (err != 0) { + printf("ERROR: %s: Failed to stop LBT\n", __FUNCTION__); + return LGW_HAL_ERROR; + } + } + + DEBUG_PRINTF(" --- %s\n", "OUT"); + + if (CONTEXT_SX1261.lbt_conf.enable == true && lbt_tx_allowed == false) { + return LGW_LBT_NOT_ALLOWED; + } else { + return LGW_HAL_SUCCESS; + } } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_status(uint8_t rf_chain, uint8_t select, uint8_t *code) { + DEBUG_PRINTF(" --- %s\n", "IN"); + /* check input variables */ CHECK_NULL(code); if (rf_chain >= LGW_RF_CHAIN_NB) { @@ -933,6 +1516,8 @@ int lgw_status(uint8_t rf_chain, uint8_t select, uint8_t *code) { return LGW_HAL_ERROR; } + DEBUG_PRINTF(" --- %s\n", "OUT"); + //DEBUG_PRINTF("INFO: STATUS %u\n", *code); return LGW_HAL_SUCCESS; } @@ -940,6 +1525,10 @@ int lgw_status(uint8_t rf_chain, uint8_t select, uint8_t *code) { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_abort_tx(uint8_t rf_chain) { + int err; + + DEBUG_PRINTF(" --- %s\n", "IN"); + /* check input variables */ if (rf_chain >= LGW_RF_CHAIN_NB) { DEBUG_MSG("ERROR: NOT A VALID RF_CHAIN NUMBER\n"); @@ -947,50 +1536,82 @@ int lgw_abort_tx(uint8_t rf_chain) { } /* Abort current TX */ - return sx1302_tx_abort(rf_chain); + err = sx1302_tx_abort(rf_chain); + + DEBUG_PRINTF(" --- %s\n", "OUT"); + + return err; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_get_trigcnt(uint32_t* trig_cnt_us) { + DEBUG_PRINTF(" --- %s\n", "IN"); + CHECK_NULL(trig_cnt_us); *trig_cnt_us = sx1302_timestamp_counter(true); + DEBUG_PRINTF(" --- %s\n", "OUT"); + return LGW_HAL_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_get_instcnt(uint32_t* inst_cnt_us) { + DEBUG_PRINTF(" --- %s\n", "IN"); + CHECK_NULL(inst_cnt_us); *inst_cnt_us = sx1302_timestamp_counter(false); + DEBUG_PRINTF(" --- %s\n", "OUT"); + return LGW_HAL_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_get_eui(uint64_t* eui) { + DEBUG_PRINTF(" --- %s\n", "IN"); + CHECK_NULL(eui); if (sx1302_get_eui(eui) != LGW_REG_SUCCESS) { return LGW_HAL_ERROR; } + + DEBUG_PRINTF(" --- %s\n", "OUT"); + return LGW_HAL_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int lgw_get_temperature(float* temperature) { +// int err = LGW_HAL_ERROR; + +// DEBUG_PRINTF(" --- %s\n", "IN"); + CHECK_NULL(temperature); *temperature = 30.0; - //if (stts751_get_temperature(ts_fd, ts_addr, temperature) != LGW_I2C_SUCCESS) { - // return LGW_HAL_ERROR; - //} - return LGW_HAL_SUCCESS; +// switch (CONTEXT_COM_TYPE) { +// case LGW_COM_SPI: +// err = stts751_get_temperature(ts_fd, ts_addr, temperature); +// break; +// case LGW_COM_USB: +// err = lgw_com_get_temperature(temperature); +// break; +// default: +// printf("ERROR(%s:%d): wrong communication type (SHOULD NOT HAPPEN)\n", __FUNCTION__, __LINE__); +// break; +// } +// +// DEBUG_PRINTF(" --- %s\n", "OUT"); +// +// return err; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -1001,58 +1622,21 @@ const char* lgw_version_info() { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -uint32_t lgw_time_on_air(struct lgw_pkt_tx_s *packet) { - int32_t val; - uint8_t SF, H, DE; - uint16_t BW; - uint32_t payloadSymbNb, Tpacket; - double Tsym, Tpreamble, Tpayload, Tfsk; +uint32_t lgw_time_on_air(const struct lgw_pkt_tx_s *packet) { + double t_fsk; + uint32_t toa_ms, toa_us; + + DEBUG_PRINTF(" --- %s\n", "IN"); if (packet == NULL) { - DEBUG_MSG("ERROR: Failed to compute time on air, wrong parameter\n"); + printf("ERROR: Failed to compute time on air, wrong parameter\n"); return 0; } if (packet->modulation == MOD_LORA) { - /* Get bandwidth */ - val = lgw_bw_getval(packet->bandwidth); - if (val != -1) { - BW = (uint16_t)(val / 1E3); - } else { - DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported bandwidth (0x%02X)\n", packet->bandwidth); - return 0; - } - - /* Get datarate */ - val = lgw_sf_getval(packet->datarate); - if (val != -1) { - SF = (uint8_t)val; - /* TODO: update formula for SF5/SF6 */ - if (SF < 7) { - DEBUG_MSG("WARNING: clipping time on air computing to SF7 for SF5/SF6\n"); - SF = 7; - } - } else { - DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported datarate (0x%02X)\n", packet->datarate); - return 0; - } - - /* Duration of 1 symbol */ - Tsym = pow(2, SF) / BW; - - /* Duration of preamble */ - Tpreamble = ((double)(packet->preamble) + 4.25) * Tsym; - - /* Duration of payload */ - H = (packet->no_header==false) ? 0 : 1; /* header is always enabled, except for beacons */ - DE = (SF >= 11) ? 1 : 0; /* Low datarate optimization enabled for SF11 and SF12 */ - - payloadSymbNb = 8 + (ceil((double)(8*packet->size - 4*SF + 28 + 16 - 20*H) / (double)(4*(SF - 2*DE))) * (packet->coderate + 4)); /* Explicitely cast to double to keep precision of the division */ - - Tpayload = payloadSymbNb * Tsym; - - /* Duration of packet */ - Tpacket = Tpreamble + Tpayload; + toa_us = lora_packet_time_on_air(packet->bandwidth, packet->datarate, packet->coderate, packet->preamble, packet->no_header, packet->no_crc, packet->size, NULL, NULL, NULL); + toa_ms = (uint32_t)( (double)toa_us / 1000.0 + 0.5 ); + DEBUG_PRINTF("INFO: LoRa packet ToA: %u ms\n", toa_ms); } else if (packet->modulation == MOD_FSK) { /* PREAMBLE + SYNC_WORD + PKT_LEN + PKT_PAYLOAD + CRC PREAMBLE: default 5 bytes @@ -1061,16 +1645,61 @@ uint32_t lgw_time_on_air(struct lgw_pkt_tx_s *packet) { PKT_PAYLOAD: x bytes CRC: 0 or 2 bytes */ - Tfsk = (8 * (double)(packet->preamble + CONTEXT_FSK.sync_word_size + 1 + packet->size + ((packet->no_crc == true) ? 0 : 2)) / (double)packet->datarate) * 1E3; + t_fsk = (8 * (double)(packet->preamble + CONTEXT_FSK.sync_word_size + 1 + packet->size + ((packet->no_crc == true) ? 0 : 2)) / (double)packet->datarate) * 1E3; /* Duration of packet */ - Tpacket = (uint32_t)Tfsk + 1; /* add margin for rounding */ + toa_ms = (uint32_t)t_fsk + 1; /* add margin for rounding */ } else { - Tpacket = 0; - DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported modulation (0x%02X)\n", packet->modulation); + toa_ms = 0; + printf("ERROR: Cannot compute time on air for this packet, unsupported modulation (0x%02X)\n", packet->modulation); } - return Tpacket; + DEBUG_PRINTF(" --- %s\n", "OUT"); + + return toa_ms; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_spectral_scan_start(uint32_t freq_hz, uint16_t nb_scan) { + int err; + + if (CONTEXT_SX1261.enable != true) { + printf("ERROR: sx1261 is not enabled, no spectral scan\n"); + return LGW_HAL_ERROR; + } + + err = sx1261_set_rx_params(freq_hz, BW_125KHZ); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: Failed to set RX params for Spectral Scan\n"); + return LGW_HAL_ERROR; + } + + err = sx1261_spectral_scan_start(nb_scan); + if (err != LGW_REG_SUCCESS) { + printf("ERROR: start spectral scan failed\n"); + return LGW_HAL_ERROR; + } + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_spectral_scan_get_status(lgw_spectral_scan_status_t * status) { + return sx1261_spectral_scan_status(status); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_spectral_scan_get_results(int16_t levels_dbm[static LGW_SPECTRAL_SCAN_RESULT_SIZE], uint16_t results[static LGW_SPECTRAL_SCAN_RESULT_SIZE]) { + return sx1261_spectral_scan_get_results(CONTEXT_SX1261.rssi_offset, levels_dbm, results); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_spectral_scan_abort() { + return sx1261_spectral_scan_abort(); } /* --- EOF ------------------------------------------------------------------ */ diff --git a/sx1302fixes/test_loragw_gps_i2c.c b/sx1302fixes/test_loragw_gps_i2c.c index 6c08c06..95385b9 100644 --- a/sx1302fixes/test_loragw_gps_i2c.c +++ b/sx1302fixes/test_loragw_gps_i2c.c @@ -43,7 +43,8 @@ License: Revised BSD License, see LICENSE.TXT file include in the project /* -------------------------------------------------------------------------- */ /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ -#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0" +#define COM_TYPE_DEFAULT LGW_COM_SPI +#define COM_PATH_DEFAULT "/dev/spidev0.0" /* -------------------------------------------------------------------------- */ /* --- PRIVATE VARIABLES ---------------------------------------------------- */ @@ -65,10 +66,13 @@ static void gps_process_coords(void); void usage(void) { //printf("Library version information: %s\n", lgw_version_info()); - printf( "Available options:\n"); - printf( " -h print this help\n"); - printf( " -k Concentrator clock source (Radio A or Radio B) [0..1]\n"); - printf( " -r Radio type (1255, 1257, 1250)\n"); + printf("Available options:\n"); + printf(" -h print this help\n"); + printf(" -u set COM type as USB (default is SPI)\n"); + printf(" -d COM path to be used to connect the concentrator\n"); + printf(" => default path (SPI): " COM_PATH_DEFAULT "\n"); + printf(" -k Concentrator clock source (Radio A or Radio B) [0..1]\n"); + printf(" -r Radio type (1255, 1257, 1250)\n"); } static void sig_handler(int sigio) { @@ -170,8 +174,9 @@ static void gps_process_coords(void) { int main(int argc, char **argv) { /* SPI interfaces */ - const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT; - const char * spidev_path = spidev_path_default; + const char com_path_default[] = COM_PATH_DEFAULT; + const char * com_path = com_path_default; + lgw_com_type_t com_type = COM_TYPE_DEFAULT; struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ @@ -180,7 +185,7 @@ int main(int argc, char **argv) /* concentrator variables */ uint8_t clocksource = 0; - lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE; + lgw_radio_type_t radio_type = LGW_RADIO_TYPE_SX1250; struct lgw_conf_board_s boardconf; struct lgw_conf_rxrf_s rfconf; @@ -193,12 +198,20 @@ int main(int argc, char **argv) enum gps_msg latest_msg; /* keep track of latest NMEA/UBX message parsed */ /* parse command line options */ - while ((i = getopt (argc, argv, "hk:r:")) != -1) { + while ((i = getopt (argc, argv, "hk:r:d:u")) != -1) { switch (i) { case 'h': usage(); return -1; break; + case 'd': + if (optarg != NULL) { + com_path = optarg; + } + break; + case 'u': + com_type = LGW_COM_USB; + break; case 'r': /* Radio type */ i = sscanf(optarg, "%u", &arg_u); if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) { @@ -234,13 +247,6 @@ int main(int argc, char **argv) } } - /* Check arguments */ - if (radio_type == LGW_RADIO_TYPE_NONE) { - printf("ERROR: radio type must be specified\n"); - usage(); - exit(EXIT_FAILURE); - } - /* configure signal handling */ sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; @@ -253,10 +259,12 @@ int main(int argc, char **argv) printf("Beginning of test for loragw_gps.c\n"); printf("*** Library version information ***\n%s\n***\n", lgw_version_info()); - /* Board reset */ - if (system("./reset_lgw.sh start") != 0) { - printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); - exit(EXIT_FAILURE); + if (com_type == LGW_COM_SPI) { + /* Board reset */ + if (system("./reset_lgw.sh start") != 0) { + printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); + exit(EXIT_FAILURE); + } } /* Open and configure GPS */ @@ -272,8 +280,9 @@ int main(int argc, char **argv) boardconf.lorawan_public = true; boardconf.clksrc = clocksource; boardconf.full_duplex = false; - strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path); - boardconf.spidev_path[sizeof boardconf.spidev_path - 1] = '\0'; /* ensure string termination */ + boardconf.com_type = com_type; + strncpy(boardconf.com_path, com_path, sizeof boardconf.com_path); + boardconf.com_path[sizeof boardconf.com_path - 1] = '\0'; /* ensure string termination */ if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) { printf("ERROR: failed to configure board\n"); return EXIT_FAILURE; @@ -405,10 +414,12 @@ int main(int argc, char **argv) lgw_stop(); } - /* Board reset */ - if (system("./reset_lgw.sh stop") != 0) { - printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); - exit(EXIT_FAILURE); + if (com_type == LGW_COM_SPI) { + /* Board reset */ + if (system("./reset_lgw.sh stop") != 0) { + printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); + exit(EXIT_FAILURE); + } } printf("\nEnd of test for loragw_gps.c\n"); diff --git a/sx1302fixes/test_loragw_gps_uart.c b/sx1302fixes/test_loragw_gps_uart.c index df9627b..b686cd0 100644 --- a/sx1302fixes/test_loragw_gps_uart.c +++ b/sx1302fixes/test_loragw_gps_uart.c @@ -43,7 +43,8 @@ License: Revised BSD License, see LICENSE.TXT file include in the project /* -------------------------------------------------------------------------- */ /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ -#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0" +#define COM_TYPE_DEFAULT LGW_COM_SPI +#define COM_PATH_DEFAULT "/dev/spidev0.0" /* -------------------------------------------------------------------------- */ /* --- PRIVATE VARIABLES ---------------------------------------------------- */ @@ -65,10 +66,13 @@ static void gps_process_coords(void); void usage(void) { //printf("Library version information: %s\n", lgw_version_info()); - printf( "Available options:\n"); - printf( " -h print this help\n"); - printf( " -k Concentrator clock source (Radio A or Radio B) [0..1]\n"); - printf( " -r Radio type (1255, 1257, 1250)\n"); + printf("Available options:\n"); + printf(" -h print this help\n"); + printf(" -u set COM type as USB (default is SPI)\n"); + printf(" -d COM path to be used to connect the concentrator\n"); + printf(" => default path (SPI): " COM_PATH_DEFAULT "\n"); + printf(" -k Concentrator clock source (Radio A or Radio B) [0..1]\n"); + printf(" -r Radio type (1255, 1257, 1250)\n"); } static void sig_handler(int sigio) { @@ -170,8 +174,9 @@ static void gps_process_coords(void) { int main(int argc, char **argv) { /* SPI interfaces */ - const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT; - const char * spidev_path = spidev_path_default; + const char com_path_default[] = COM_PATH_DEFAULT; + const char * com_path = com_path_default; + lgw_com_type_t com_type = COM_TYPE_DEFAULT; struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ @@ -180,7 +185,7 @@ int main(int argc, char **argv) /* concentrator variables */ uint8_t clocksource = 0; - lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE; + lgw_radio_type_t radio_type = LGW_RADIO_TYPE_SX1250; struct lgw_conf_board_s boardconf; struct lgw_conf_rxrf_s rfconf; @@ -193,12 +198,20 @@ int main(int argc, char **argv) enum gps_msg latest_msg; /* keep track of latest NMEA/UBX message parsed */ /* parse command line options */ - while ((i = getopt (argc, argv, "hk:r:")) != -1) { + while ((i = getopt (argc, argv, "hk:r:d:u")) != -1) { switch (i) { case 'h': usage(); return -1; break; + case 'd': + if (optarg != NULL) { + com_path = optarg; + } + break; + case 'u': + com_type = LGW_COM_USB; + break; case 'r': /* Radio type */ i = sscanf(optarg, "%u", &arg_u); if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) { @@ -234,13 +247,6 @@ int main(int argc, char **argv) } } - /* Check arguments */ - if (radio_type == LGW_RADIO_TYPE_NONE) { - printf("ERROR: radio type must be specified\n"); - usage(); - exit(EXIT_FAILURE); - } - /* configure signal handling */ sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; @@ -253,10 +259,12 @@ int main(int argc, char **argv) printf("Beginning of test for loragw_gps.c\n"); printf("*** Library version information ***\n%s\n***\n", lgw_version_info()); - /* Board reset */ - if (system("./reset_lgw.sh start") != 0) { - printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); - exit(EXIT_FAILURE); + if (com_type == LGW_COM_SPI) { + /* Board reset */ + if (system("./reset_lgw.sh start") != 0) { + printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); + exit(EXIT_FAILURE); + } } /* Open and configure GPS */ @@ -272,8 +280,9 @@ int main(int argc, char **argv) boardconf.lorawan_public = true; boardconf.clksrc = clocksource; boardconf.full_duplex = false; - strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path); - boardconf.spidev_path[sizeof boardconf.spidev_path - 1] = '\0'; /* ensure string termination */ + boardconf.com_type = com_type; + strncpy(boardconf.com_path, com_path, sizeof boardconf.com_path); + boardconf.com_path[sizeof boardconf.com_path - 1] = '\0'; /* ensure string termination */ if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) { printf("ERROR: failed to configure board\n"); return EXIT_FAILURE; @@ -405,10 +414,12 @@ int main(int argc, char **argv) lgw_stop(); } - /* Board reset */ - if (system("./reset_lgw.sh stop") != 0) { - printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); - exit(EXIT_FAILURE); + if (com_type == LGW_COM_SPI) { + /* Board reset */ + if (system("./reset_lgw.sh stop") != 0) { + printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n"); + exit(EXIT_FAILURE); + } } printf("\nEnd of test for loragw_gps.c\n");