├── .gitignore ├── LICENSE ├── Makefile ├── VERSION ├── fpga ├── SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_863_v33.hex ├── SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_915_v33.hex ├── SX1301_FPGA_NOTCH_PROG_SPECTRAL_SCAN_v31.hex └── readme.md ├── libloragw ├── 99-libftdi.rules ├── Makefile ├── inc │ ├── loragw_aux.h │ ├── loragw_fpga.h │ ├── loragw_gps.h │ ├── loragw_hal.h │ ├── loragw_lbt.h │ ├── loragw_radio.h │ ├── loragw_reg.h │ ├── loragw_spi.h │ ├── loragw_sx125x.h │ ├── loragw_sx1272_fsk.h │ ├── loragw_sx1272_lora.h │ ├── loragw_sx1276_fsk.h │ └── loragw_sx1276_lora.h ├── library.cfg ├── readme.md ├── src │ ├── agc_fw.var │ ├── arb_fw.var │ ├── cal_fw.var │ ├── loragw_aux.c │ ├── loragw_fpga.c │ ├── loragw_gps.c │ ├── loragw_hal.c │ ├── loragw_lbt.c │ ├── loragw_radio.c │ ├── loragw_reg.c │ └── loragw_spi.native.c └── tst │ ├── test_loragw_cal.c │ ├── test_loragw_gps.c │ ├── test_loragw_hal.c │ ├── test_loragw_reg.c │ └── test_loragw_spi.c ├── readme.md ├── reset_lgw.sh ├── util_lbt_test ├── Makefile ├── readme.md └── src │ └── util_lbt_test.c ├── util_pkt_logger ├── Makefile ├── global_conf.json ├── inc │ └── parson.h ├── local_conf.json ├── readme.md └── src │ ├── parson.c │ └── util_pkt_logger.c ├── util_spectral_scan ├── Makefile ├── readme.md └── src │ └── util_spectral_scan.c ├── util_spi_stress ├── Makefile ├── readme.md └── src │ └── util_spi_stress.c ├── util_tx_continuous ├── Makefile ├── readme.md └── src │ └── util_tx_continuous.c └── util_tx_test ├── Makefile ├── readme.md └── src └── util_tx_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | *.bak 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, SEMTECH S.A. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Semtech corporation nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | 27 | --- For the parson library used by the packet logger --- 28 | 29 | Parson ( http://kgabis.github.com/parson/ ) 30 | Copyright (c) 2012 Krzysztof Gabis 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in 40 | all copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 48 | THE SOFTWARE. 49 | 50 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ### Environment constants 2 | 3 | ARCH ?= 4 | CROSS_COMPILE ?= 5 | export 6 | 7 | ### general build targets 8 | 9 | all: 10 | $(MAKE) all -e -C libloragw 11 | $(MAKE) all -e -C util_pkt_logger 12 | $(MAKE) all -e -C util_spi_stress 13 | $(MAKE) all -e -C util_tx_test 14 | $(MAKE) all -e -C util_lbt_test 15 | $(MAKE) all -e -C util_tx_continuous 16 | $(MAKE) all -e -C util_spectral_scan 17 | 18 | clean: 19 | $(MAKE) clean -e -C libloragw 20 | $(MAKE) clean -e -C util_pkt_logger 21 | $(MAKE) clean -e -C util_spi_stress 22 | $(MAKE) clean -e -C util_tx_test 23 | $(MAKE) clean -e -C util_lbt_test 24 | $(MAKE) clean -e -C util_tx_continuous 25 | $(MAKE) clean -e -C util_spectral_scan 26 | 27 | ### EOF 28 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 5.0.1 2 | -------------------------------------------------------------------------------- /fpga/readme.md: -------------------------------------------------------------------------------- 1 | / _____) _ | | 2 | ( (____ _____ ____ _| |_ _____ ____| |__ 3 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 4 | _____) ) ____| | | || |_| ____( (___| | | | 5 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 6 | (C)2013 Semtech-Cycleo 7 | 8 | FPGA images for LoRa Gateway SX1301AP2-PCB_E336 9 | =============================================== 10 | 11 | 1. Content 12 | ---------- 13 | 14 | This directory contains the FPGA images to be programmed in the Semtech's 15 | Reference Design board (SX1301AP2-PCB_E336) flash memory. 16 | 17 | The different images contain the following features: 18 | 19 | * SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_915_v33.hex: 20 | - 200KHz Notch filter for TX (not programmable) 21 | - Listen-Before-Talk for 915+MHz frequency range (Japan) 22 | - Background Spectral Scan (limited) 23 | 24 | * SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_863_v33.hex: 25 | - 200KHz Notch filter for TX (not programmable) 26 | - Listen-Before-Talk for 863+MHz frequency range 27 | - Background Spectral Scan (limited) 28 | Note: This image is the same as the 915+MHz version. It is just meant for 29 | testing "Japan-like" LBT feature on a EU868 board. It does not provide certified 30 | LBT for European band. 31 | 32 | * SX1301_FPGA_NOTCH_PROG_SPECTRAL_SCAN_v31.hex: 33 | - Programmable notch filter for TX 34 | - Background Spectral Scan (full) 35 | 36 | 2. Usage 37 | -------- 38 | 39 | The following parameters have to be set when using the Lattice Diamond 40 | Programmer software: 41 | 42 | Device Family -> iCE40 43 | Device -> iCE40LP1K 44 | Operation -> SPI Flash Programming 45 | -> Programming file: select one of the provided bin image 46 | -> SPI Vendor: Micron 47 | -> SPI Device: SPI-M25P10-A 48 | 49 | 3. Legal notice 50 | ---------------- 51 | 52 | The information presented in this project documentation does not form part of 53 | any quotation or contract, is believed to be accurate and reliable and may be 54 | changed without notice. No liability will be accepted by the publisher for any 55 | consequence of its use. Publication thereof does not convey nor imply any 56 | license under patent or other industrial or intellectual property rights. 57 | Semtech assumes no responsibility or liability whatsoever for any failure or 58 | unexpected operation resulting from misuse, neglect improper installation, 59 | repair or improper handling or unusual physical or electrical stress 60 | including, but not limited to, exposure to parameters beyond the specified 61 | maximum ratings or operation outside the specified range. 62 | 63 | SEMTECH PRODUCTS ARE NOT DESIGNED, INTENDED, AUTHORIZED OR WARRANTED TO BE 64 | SUITABLE FOR USE IN LIFE-SUPPORT APPLICATIONS, DEVICES OR SYSTEMS OR OTHER 65 | CRITICAL APPLICATIONS. INCLUSION OF SEMTECH PRODUCTS IN SUCH APPLICATIONS IS 66 | UNDERSTOOD TO BE UNDERTAKEN SOLELY AT THE CUSTOMER'S OWN RISK. Should a 67 | customer purchase or use Semtech products for any such unauthorized 68 | application, the customer shall indemnify and hold Semtech and its officers, 69 | employees, subsidiaries, affiliates, and distributors harmless against all 70 | claims, costs damages and attorney fees which could arise. 71 | 72 | *EOF* 73 | -------------------------------------------------------------------------------- /libloragw/99-libftdi.rules: -------------------------------------------------------------------------------- 1 | # FTDI Devices: FT232BM/L/Q, FT245BM/L/Q, FT232RL/Q, FT245RL/Q, VNC1L with VDPS Firmware 2 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0664", GROUP="plugdev" 3 | 4 | # FTDI Devices: FT2232C/D/L, FT2232HL/Q 5 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0664", GROUP="plugdev" 6 | 7 | # FTDI Devices: FT4232HL/Q 8 | SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", MODE="0664", GROUP="plugdev" 9 | -------------------------------------------------------------------------------- /libloragw/Makefile: -------------------------------------------------------------------------------- 1 | ### get external defined data 2 | 3 | LIBLORAGW_VERSION := `cat ../VERSION` 4 | include library.cfg 5 | 6 | ### constant symbols 7 | 8 | ARCH ?= 9 | CROSS_COMPILE ?= 10 | CC := $(CROSS_COMPILE)gcc 11 | AR := $(CROSS_COMPILE)ar 12 | 13 | CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I. 14 | 15 | OBJDIR = obj 16 | INCLUDES = $(wildcard inc/*.h) 17 | 18 | ### linking options 19 | 20 | LIBS := -lloragw -lrt -lm 21 | 22 | ### general build targets 23 | 24 | all: libloragw.a test_loragw_spi test_loragw_reg test_loragw_hal test_loragw_gps test_loragw_cal 25 | 26 | clean: 27 | rm -f libloragw.a 28 | rm -f test_loragw_* 29 | rm -f $(OBJDIR)/*.o 30 | rm -f inc/config.h 31 | 32 | ### transpose library.cfg into a C header file : config.h 33 | 34 | inc/config.h: ../VERSION library.cfg 35 | @echo "*** Checking libloragw library configuration ***" 36 | @rm -f $@ 37 | #File initialization 38 | @echo "#ifndef _LORAGW_CONFIGURATION_H" >> $@ 39 | @echo "#define _LORAGW_CONFIGURATION_H" >> $@ 40 | # Release version 41 | @echo "Release version : $(LIBLORAGW_VERSION)" 42 | @echo " #define LIBLORAGW_VERSION "\"$(LIBLORAGW_VERSION)\""" >> $@ 43 | # Debug options 44 | @echo " #define DEBUG_AUX $(DEBUG_AUX)" >> $@ 45 | @echo " #define DEBUG_SPI $(DEBUG_SPI)" >> $@ 46 | @echo " #define DEBUG_REG $(DEBUG_REG)" >> $@ 47 | @echo " #define DEBUG_HAL $(DEBUG_HAL)" >> $@ 48 | @echo " #define DEBUG_GPS $(DEBUG_GPS)" >> $@ 49 | @echo " #define DEBUG_GPIO $(DEBUG_GPIO)" >> $@ 50 | @echo " #define DEBUG_LBT $(DEBUG_LBT)" >> $@ 51 | # end of file 52 | @echo "#endif" >> $@ 53 | @echo "*** Configuration seems ok ***" 54 | 55 | ### library module target 56 | 57 | $(OBJDIR): 58 | mkdir -p $(OBJDIR) 59 | 60 | $(OBJDIR)/%.o: src/%.c $(INCLUDES) inc/config.h | $(OBJDIR) 61 | $(CC) -c $(CFLAGS) $< -o $@ 62 | 63 | $(OBJDIR)/loragw_spi.o: src/loragw_spi.native.c $(INCLUDES) inc/config.h | $(OBJDIR) 64 | $(CC) -c $(CFLAGS) $< -o $@ 65 | 66 | $(OBJDIR)/loragw_hal.o: src/loragw_hal.c $(INCLUDES) src/arb_fw.var src/agc_fw.var src/cal_fw.var inc/config.h | $(OBJDIR) 67 | $(CC) -c $(CFLAGS) $< -o $@ 68 | 69 | ### static library 70 | 71 | libloragw.a: $(OBJDIR)/loragw_hal.o $(OBJDIR)/loragw_gps.o $(OBJDIR)/loragw_reg.o $(OBJDIR)/loragw_spi.o $(OBJDIR)/loragw_aux.o $(OBJDIR)/loragw_radio.o $(OBJDIR)/loragw_fpga.o $(OBJDIR)/loragw_lbt.o 72 | $(AR) rcs $@ $^ 73 | 74 | ### test programs 75 | 76 | test_loragw_spi: tst/test_loragw_spi.c libloragw.a 77 | $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) 78 | 79 | test_loragw_reg: tst/test_loragw_reg.c libloragw.a 80 | $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) 81 | 82 | test_loragw_hal: tst/test_loragw_hal.c libloragw.a 83 | $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) 84 | 85 | test_loragw_gps: tst/test_loragw_gps.c libloragw.a 86 | $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) 87 | 88 | test_loragw_cal: tst/test_loragw_cal.c libloragw.a src/cal_fw.var 89 | $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) 90 | 91 | ### EOF 92 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_aux.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | LoRa concentrator HAL common auxiliary functions 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Sylvain Miermont 14 | */ 15 | 16 | 17 | #ifndef _LORAGW_AUX_H 18 | #define _LORAGW_AUX_H 19 | 20 | /* -------------------------------------------------------------------------- */ 21 | /* --- DEPENDANCIES --------------------------------------------------------- */ 22 | 23 | #include "config.h" /* library configuration options (dynamically generated) */ 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | /* --- PUBLIC MACROS -------------------------------------------------------- */ 27 | 28 | /** 29 | @brief Get a particular bit value from a byte 30 | @param b [in] Any byte from which we want a bit value 31 | @param p [in] Position of the bit in the byte [0..7] 32 | @param n [in] Number of bits we want to get 33 | @return The value corresponding the requested bits 34 | */ 35 | #define TAKE_N_BITS_FROM(b, p, n) (((b) >> (p)) & ((1 << (n)) - 1)) 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 39 | 40 | /** 41 | @brief Wait for a certain time (millisecond accuracy) 42 | @param t number of milliseconds to wait. 43 | */ 44 | void wait_ms(unsigned long t); 45 | 46 | #endif 47 | 48 | /* --- EOF ------------------------------------------------------------------ */ 49 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_fpga.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Functions used to handle FPGA register access for LoRa concentrator. 11 | Registers are addressed by name. 12 | Multi-bytes registers are handled automatically. 13 | Read-modify-write is handled automatically. 14 | 15 | License: Revised BSD License, see LICENSE.TXT file include in the project 16 | Maintainer: Michael Coracin 17 | */ 18 | 19 | #ifndef _LORAGW_FPGA_REG_H 20 | #define _LORAGW_FPGA_REG_H 21 | 22 | /* -------------------------------------------------------------------------- */ 23 | /* --- DEPENDANCIES --------------------------------------------------------- */ 24 | 25 | #include /* C99 types */ 26 | #include /* bool type */ 27 | 28 | /* -------------------------------------------------------------------------- */ 29 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 30 | 31 | #define LGW_REG_SUCCESS 0 32 | #define LGW_REG_ERROR -1 33 | 34 | #define LGW_MIN_NOTCH_FREQ 126000U /* 126 KHz */ 35 | #define LGW_MAX_NOTCH_FREQ 250000U /* 250 KHz */ 36 | #define LGW_DEFAULT_NOTCH_FREQ 129000U /* 129 KHz */ 37 | 38 | /* 39 | auto generated register mapping for C code 40 | this file contains autogenerated C struct used to access the FPGA registers 41 | this file is autogenerated from registers description 42 | */ 43 | 44 | #define LGW_FPGA_SOFT_RESET 0 45 | #define LGW_FPGA_FEATURE 1 46 | #define LGW_FPGA_LBT_INITIAL_FREQ 2 47 | #define LGW_FPGA_VERSION 3 48 | #define LGW_FPGA_STATUS 4 49 | #define LGW_FPGA_CTRL_FEATURE_START 5 50 | #define LGW_FPGA_CTRL_RADIO_RESET 6 51 | #define LGW_FPGA_CTRL_INPUT_SYNC_I 7 52 | #define LGW_FPGA_CTRL_INPUT_SYNC_Q 8 53 | #define LGW_FPGA_CTRL_OUTPUT_SYNC 9 54 | #define LGW_FPGA_CTRL_INVERT_IQ 10 55 | #define LGW_FPGA_CTRL_ACCESS_HISTO_MEM 11 56 | #define LGW_FPGA_CTRL_CLEAR_HISTO_MEM 12 57 | #define LGW_FPGA_HISTO_RAM_ADDR 13 58 | #define LGW_FPGA_HISTO_RAM_DATA 14 59 | #define LGW_FPGA_HISTO_NB_READ 15 60 | #define LGW_FPGA_LBT_TIMESTAMP_CH 16 61 | #define LGW_FPGA_LBT_TIMESTAMP_SELECT_CH 17 62 | #define LGW_FPGA_LBT_CH0_FREQ_OFFSET 18 63 | #define LGW_FPGA_LBT_CH1_FREQ_OFFSET 19 64 | #define LGW_FPGA_LBT_CH2_FREQ_OFFSET 20 65 | #define LGW_FPGA_LBT_CH3_FREQ_OFFSET 21 66 | #define LGW_FPGA_LBT_CH4_FREQ_OFFSET 22 67 | #define LGW_FPGA_LBT_CH5_FREQ_OFFSET 23 68 | #define LGW_FPGA_LBT_CH6_FREQ_OFFSET 24 69 | #define LGW_FPGA_LBT_CH7_FREQ_OFFSET 25 70 | #define LGW_FPGA_SCAN_FREQ_OFFSET 26 71 | #define LGW_FPGA_LBT_SCAN_TIME_CH0 27 72 | #define LGW_FPGA_LBT_SCAN_TIME_CH1 28 73 | #define LGW_FPGA_LBT_SCAN_TIME_CH2 29 74 | #define LGW_FPGA_LBT_SCAN_TIME_CH3 30 75 | #define LGW_FPGA_LBT_SCAN_TIME_CH4 31 76 | #define LGW_FPGA_LBT_SCAN_TIME_CH5 32 77 | #define LGW_FPGA_LBT_SCAN_TIME_CH6 33 78 | #define LGW_FPGA_LBT_SCAN_TIME_CH7 34 79 | #define LGW_FPGA_RSSI_TARGET 35 80 | #define LGW_FPGA_HISTO_SCAN_FREQ 36 81 | #define LGW_FPGA_NOTCH_FREQ_OFFSET 37 82 | #define LGW_FPGA_TOTALREGS 38 83 | 84 | /* -------------------------------------------------------------------------- */ 85 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 86 | 87 | /** 88 | @brief LoRa concentrator TX notch filter delay 89 | @return delay in microseconds introduced by TX notch filter 90 | */ 91 | float lgw_fpga_get_tx_notch_delay(void); 92 | 93 | /** 94 | @brief LoRa concentrator FPGA configuration 95 | @param tx_notch_freq TX notch filter frequency, in Hertz 96 | @return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) 97 | */ 98 | int lgw_fpga_configure(uint32_t tx_notch_freq); 99 | 100 | /** 101 | @brief LoRa concentrator FPGA register write 102 | @param register_id register number in the data structure describing registers 103 | @param reg_value signed value to write to the register (for u32, use cast) 104 | @return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) 105 | */ 106 | int lgw_fpga_reg_w(uint16_t register_id, int32_t reg_value); 107 | 108 | /** 109 | @brief LoRa concentrator FPGA register read 110 | @param register_id register number in the data structure describing registers 111 | @param reg_value pointer to a variable where to write register read value 112 | @return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) 113 | */ 114 | int lgw_fpga_reg_r(uint16_t register_id, int32_t *reg_value); 115 | 116 | /** 117 | @brief LoRa concentrator FPGA register burst write 118 | @param register_id register number in the data structure describing registers 119 | @param data pointer to byte array that will be sent to the LoRa concentrator 120 | @param size size of the transfer, in byte(s) 121 | @return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) 122 | */ 123 | int lgw_fpga_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size); 124 | 125 | /** 126 | @brief LoRa concentrator FPGA register burst read 127 | @param register_id register number in the data structure describing registers 128 | @param data pointer to byte array that will be written from the LoRa concentrator 129 | @param size size of the transfer, in byte(s) 130 | @return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) 131 | */ 132 | int lgw_fpga_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size); 133 | 134 | #endif 135 | /* --- EOF ------------------------------------------------------------------ */ 136 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_gps.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Library of functions to manage a GNSS module (typically GPS) for accurate 11 | timestamping of packets and synchronisation of gateways. 12 | A limited set of module brands/models are supported. 13 | 14 | License: Revised BSD License, see LICENSE.TXT file include in the project 15 | Maintainer: Michael Coracin 16 | */ 17 | 18 | 19 | #ifndef _LORAGW_GPS_H 20 | #define _LORAGW_GPS_H 21 | 22 | /* -------------------------------------------------------------------------- */ 23 | /* --- DEPENDANCIES --------------------------------------------------------- */ 24 | 25 | #define _GNU_SOURCE 26 | #include /* C99 types */ 27 | #include /* time library */ 28 | #include /* speed_t */ 29 | #include /* ssize_t */ 30 | 31 | #include "config.h" /* library configuration options (dynamically generated) */ 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | /* --- PUBLIC TYPES --------------------------------------------------------- */ 35 | 36 | /** 37 | @struct coord_s 38 | @brief Time solution required for timestamp to absolute time conversion 39 | */ 40 | struct tref { 41 | time_t systime; /*!> system time when solution was calculated */ 42 | uint32_t count_us; /*!> reference concentrator internal timestamp */ 43 | struct timespec utc; /*!> reference UTC time (from GPS/NMEA) */ 44 | struct timespec gps; /*!> reference GPS time (since 01.Jan.1980) */ 45 | double xtal_err; /*!> raw clock error (eg. <1 'slow' XTAL) */ 46 | }; 47 | 48 | /** 49 | @struct coord_s 50 | @brief Geodesic coordinates 51 | */ 52 | struct coord_s { 53 | double lat; /*!> latitude [-90,90] (North +, South -) */ 54 | double lon; /*!> longitude [-180,180] (East +, West -)*/ 55 | short alt; /*!> altitude in meters (WGS 84 geoid ref.) */ 56 | }; 57 | 58 | /** 59 | @enum gps_msg 60 | @brief Type of GPS (and other GNSS) sentences 61 | */ 62 | enum gps_msg { 63 | UNKNOWN, /*!> neutral value */ 64 | IGNORED, /*!> frame was not parsed by the system */ 65 | INVALID, /*!> system try to parse frame but failed */ 66 | INCOMPLETE, /*!> frame parsed was missing bytes */ 67 | /* NMEA messages of interest */ 68 | NMEA_RMC, /*!> Recommended Minimum data (time + date) */ 69 | NMEA_GGA, /*!> Global positioning system fix data (pos + alt) */ 70 | NMEA_GNS, /*!> GNSS fix data (pos + alt, sat number) */ 71 | NMEA_ZDA, /*!> Time and Date */ 72 | /* NMEA message useful for time reference quality assessment */ 73 | NMEA_GBS, /*!> GNSS Satellite Fault Detection */ 74 | NMEA_GST, /*!> GNSS Pseudo Range Error Statistics */ 75 | NMEA_GSA, /*!> GNSS DOP and Active Satellites (sat number) */ 76 | NMEA_GSV, /*!> GNSS Satellites in View (sat SNR) */ 77 | /* Misc. NMEA messages */ 78 | NMEA_GLL, /*!> Latitude and longitude, with time fix and status */ 79 | NMEA_TXT, /*!> Text Transmission */ 80 | NMEA_VTG, /*!> Course over ground and Ground speed */ 81 | /* uBlox proprietary NMEA messages of interest */ 82 | UBX_NAV_TIMEGPS, /*!> GPS Time Solution */ 83 | UBX_NAV_TIMEUTC /*!> UTC Time Solution */ 84 | }; 85 | 86 | /* -------------------------------------------------------------------------- */ 87 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 88 | 89 | #define LGW_GPS_SUCCESS 0 90 | #define LGW_GPS_ERROR -1 91 | 92 | #define LGW_GPS_MIN_MSG_SIZE (8) 93 | #define LGW_GPS_UBX_SYNC_CHAR (0xB5) 94 | #define LGW_GPS_NMEA_SYNC_CHAR (0x24) 95 | 96 | /* -------------------------------------------------------------------------- */ 97 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 98 | 99 | /** 100 | @brief Configure a GPS module 101 | 102 | @param tty_path path to the TTY connected to the GPS 103 | @param gps_familly parameter (eg. ubx6 for uBlox gen.6) 104 | @param target_brate target baudrate for communication (0 keeps default target baudrate) 105 | @param fd_ptr pointer to a variable to receive file descriptor on GPS tty 106 | @return success if the function was able to connect and configure a GPS module 107 | */ 108 | int lgw_gps_enable(char* tty_path, char* gps_familly, speed_t target_brate, int* fd_ptr); 109 | 110 | /** 111 | @brief Restore GPS serial configuration and close serial device 112 | 113 | @param fd file descriptor on GPS tty 114 | @return success if the function was able to complete 115 | */ 116 | int lgw_gps_disable(int fd); 117 | 118 | /** 119 | @brief Parse messages coming from the GPS system (or other GNSS) 120 | 121 | @param serial_buff pointer to the string to be parsed 122 | @param buff_size maximum string lengths for NMEA parsing (incl. null char) 123 | @return type of frame parsed 124 | 125 | The RAW NMEA sentences are parsed to a global set of variables shared with the 126 | lgw_gps_get function. 127 | If the lgw_parse_nmea and lgw_gps_get are used in different threads, a mutex 128 | lock must be acquired before calling either function. 129 | */ 130 | enum gps_msg lgw_parse_nmea(const char* serial_buff, int buff_size); 131 | 132 | /** 133 | @brief Parse Ublox proprietary messages coming from the GPS system 134 | 135 | @param serial_buff pointer to the string to be parsed 136 | @param buff_size maximum string lengths for UBX parsing (incl. null char) 137 | @param msg_size number of bytes parsed as UBX message if found 138 | @return type of frame parsed 139 | 140 | The RAW UBX sentences are parsed to a global set of variables shared with the 141 | lgw_gps_get function. 142 | If the lgw_parse_ubx and lgw_gps_get are used in different threads, a mutex 143 | lock must be acquired before calling either function. 144 | */ 145 | enum gps_msg lgw_parse_ubx(const char* serial_buff, size_t buff_size, size_t *msg_size); 146 | 147 | /** 148 | @brief Get the GPS solution (space & time) for the concentrator 149 | 150 | @param utc pointer to store UTC time, with ns precision (NULL to ignore) 151 | @param gps_time pointer to store GPS time, with ns precision (NULL to ignore) 152 | @param loc pointer to store coordinates (NULL to ignore) 153 | @param err pointer to store coordinates standard deviation (NULL to ignore) 154 | @return success if the chosen elements could be returned 155 | 156 | This function read the global variables generated by the NMEA/UBX parsing 157 | functions lgw_parse_nmea/lgw_parse_ubx. It returns time and location data in a 158 | format that is exploitable by other functions in that library sub-module. 159 | If the lgw_parse_nmea/lgw_parse_ubx and lgw_gps_get are used in different 160 | threads, a mutex lock must be acquired before calling either function. 161 | */ 162 | int lgw_gps_get(struct timespec *utc, struct timespec *gps_time, struct coord_s *loc, struct coord_s *err); 163 | 164 | /** 165 | @brief Get time and position information from the serial GPS last message received 166 | @param utc UTC time, with ns precision (leap seconds are ignored) 167 | @param gps_time timestamp of last time pulse from the GPS module converted to the UNIX epoch 168 | (leap seconds are ignored) 169 | @param loc location information 170 | @param err location error estimate if supported 171 | @return success if timestamp was read and time reference could be refreshed 172 | 173 | Set systime to 0 in ref to trigger initial synchronization. 174 | */ 175 | int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec gps_time); 176 | 177 | /** 178 | @brief Convert concentrator timestamp counter value to UTC time 179 | 180 | @param ref time reference structure required for time conversion 181 | @param count_us internal timestamp counter of the LoRa concentrator 182 | @param utc pointer to store UTC time, with ns precision (leap seconds ignored) 183 | @return success if the function was able to convert timestamp to UTC 184 | 185 | This function is typically used when a packet is received to transform the 186 | internal counter-based timestamp in an absolute timestamp with an accuracy in 187 | the order of a couple microseconds (ns resolution). 188 | */ 189 | int lgw_cnt2utc(struct tref ref, uint32_t count_us, struct timespec* utc); 190 | 191 | /** 192 | @brief Convert UTC time to concentrator timestamp counter value 193 | 194 | @param ref time reference structure required for time conversion 195 | @param utc UTC time, with ns precision (leap seconds are ignored) 196 | @param count_us pointer to store internal timestamp counter of LoRa concentrator 197 | @return success if the function was able to convert UTC to timestamp 198 | 199 | This function is typically used when a packet must be sent at an accurate time 200 | (eg. to send a piggy-back response after receiving a packet from a node) to 201 | transform an absolute UTC time into a matching internal concentrator timestamp. 202 | */ 203 | int lgw_utc2cnt(struct tref ref,struct timespec utc, uint32_t* count_us); 204 | 205 | /** 206 | @brief Convert concentrator timestamp counter value to GPS time 207 | 208 | @param ref time reference structure required for time conversion 209 | @param count_us internal timestamp counter of the LoRa concentrator 210 | @param gps_time pointer to store GPS time, with ns precision (leap seconds ignored) 211 | @return success if the function was able to convert timestamp to GPS time 212 | 213 | This function is typically used when a packet is received to transform the 214 | internal counter-based timestamp in an absolute timestamp with an accuracy in 215 | the order of a millisecond. 216 | */ 217 | int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec* gps_time); 218 | 219 | /** 220 | @brief Convert GPS time to concentrator timestamp counter value 221 | 222 | @param ref time reference structure required for time conversion 223 | @param gps_time GPS time, with ns precision (leap seconds are ignored) 224 | @param count_us pointer to store internal timestamp counter of LoRa concentrator 225 | @return success if the function was able to convert GPS time to timestamp 226 | 227 | This function is typically used when a packet must be sent at an accurate time 228 | (eg. to send a piggy-back response after receiving a packet from a node) to 229 | transform an absolute GPS time into a matching internal concentrator timestamp. 230 | */ 231 | int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t* count_us); 232 | 233 | #endif 234 | 235 | /* --- EOF ------------------------------------------------------------------ */ 236 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_lbt.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Functions used to handle the Listen Before Talk feature 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Michael Coracin 14 | */ 15 | 16 | #ifndef _LORAGW_LBT_H 17 | #define _LORAGW_LBT_H 18 | 19 | /* -------------------------------------------------------------------------- */ 20 | /* --- DEPENDANCIES --------------------------------------------------------- */ 21 | 22 | #include /* C99 types */ 23 | #include /* bool type */ 24 | 25 | #include "loragw_hal.h" 26 | 27 | /* -------------------------------------------------------------------------- */ 28 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 29 | 30 | #define LGW_LBT_SUCCESS 0 31 | #define LGW_LBT_ERROR -1 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 35 | 36 | /** 37 | @brief Set the configuration parameters for LBT feature 38 | @param conf structure containing the configuration parameters 39 | @return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else 40 | */ 41 | int lbt_setconf(struct lgw_conf_lbt_s * conf); 42 | 43 | /** 44 | @brief Configure the concentrator for LBT feature 45 | @return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else 46 | */ 47 | int lbt_setup(void); 48 | 49 | /** 50 | @brief Start the LBT FSM 51 | @return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else 52 | */ 53 | int lbt_start(void); 54 | 55 | /** 56 | @brief Configure the concentrator for LBT feature 57 | @param pkt_data pointer to downlink packet to be trabsmitted 58 | @param tx_allowed pointer to receive permission for transmission 59 | @return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else 60 | */ 61 | int lbt_is_channel_free(struct lgw_pkt_tx_s * pkt_data, uint16_t tx_start_delay, bool * tx_allowed); 62 | 63 | /** 64 | @brief Check if LBT is enabled 65 | @return true if enabled, false otherwise 66 | */ 67 | bool lbt_is_enabled(void); 68 | 69 | #endif 70 | /* --- EOF ------------------------------------------------------------------ */ 71 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_radio.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Functions used to handle LoRa concentrator radios. 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Michael Coracin 14 | */ 15 | 16 | #ifndef _LORAGW_RADIO_H 17 | #define _LORAGW_RADIO_H 18 | 19 | /* -------------------------------------------------------------------------- */ 20 | /* --- DEPENDANCIES --------------------------------------------------------- */ 21 | 22 | #include /* C99 types */ 23 | #include /* bool type */ 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 27 | 28 | #define LGW_REG_SUCCESS 0 29 | #define LGW_REG_ERROR -1 30 | 31 | #define SX125x_32MHz_FRAC 15625 /* irreductible fraction for PLL register caculation */ 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 35 | 36 | enum lgw_sx127x_rxbw_e { 37 | LGW_SX127X_RXBW_2K6_HZ, 38 | LGW_SX127X_RXBW_3K1_HZ, 39 | LGW_SX127X_RXBW_3K9_HZ, 40 | LGW_SX127X_RXBW_5K2_HZ, 41 | LGW_SX127X_RXBW_6K3_HZ, 42 | LGW_SX127X_RXBW_7K8_HZ, 43 | LGW_SX127X_RXBW_10K4_HZ, 44 | LGW_SX127X_RXBW_12K5_HZ, 45 | LGW_SX127X_RXBW_15K6_HZ, 46 | LGW_SX127X_RXBW_20K8_HZ, 47 | LGW_SX127X_RXBW_25K_HZ, 48 | LGW_SX127X_RXBW_31K3_HZ, 49 | LGW_SX127X_RXBW_41K7_HZ, 50 | LGW_SX127X_RXBW_50K_HZ, 51 | LGW_SX127X_RXBW_62K5_HZ, 52 | LGW_SX127X_RXBW_83K3_HZ, 53 | LGW_SX127X_RXBW_100K_HZ, 54 | LGW_SX127X_RXBW_125K_HZ, 55 | LGW_SX127X_RXBW_166K7_HZ, 56 | LGW_SX127X_RXBW_200K_HZ, 57 | LGW_SX127X_RXBW_250K_HZ 58 | }; 59 | 60 | /* -------------------------------------------------------------------------- */ 61 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 62 | 63 | int lgw_setup_sx125x(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz); 64 | 65 | int lgw_setup_sx127x(uint32_t frequency, uint8_t modulation, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset); 66 | 67 | int lgw_sx127x_reg_w(uint8_t address, uint8_t reg_value); 68 | 69 | int lgw_sx127x_reg_r(uint8_t address, uint8_t *reg_value); 70 | 71 | 72 | #endif 73 | /* --- EOF ------------------------------------------------------------------ */ 74 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Host specific functions to address the LoRa concentrator registers through a 11 | SPI interface. 12 | Single-byte read/write and burst read/write. 13 | Does not handle pagination. 14 | Could be used with multiple SPI ports in parallel (explicit file descriptor) 15 | 16 | License: Revised BSD License, see LICENSE.TXT file include in the project 17 | Maintainer: Sylvain Miermont 18 | */ 19 | 20 | 21 | #ifndef _LORAGW_SPI_H 22 | #define _LORAGW_SPI_H 23 | 24 | /* -------------------------------------------------------------------------- */ 25 | /* --- DEPENDANCIES --------------------------------------------------------- */ 26 | 27 | #include /* C99 types*/ 28 | 29 | #include "config.h" /* library configuration options (dynamically generated) */ 30 | 31 | /* -------------------------------------------------------------------------- */ 32 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 33 | 34 | #define LGW_SPI_SUCCESS 0 35 | #define LGW_SPI_ERROR -1 36 | #define LGW_BURST_CHUNK 1024 37 | 38 | #define LGW_SPI_MUX_MODE0 0x0 /* No FPGA */ 39 | #define LGW_SPI_MUX_MODE1 0x1 /* FPGA, with spi mux header */ 40 | 41 | #define LGW_SPI_MUX_TARGET_SX1301 0x0 42 | #define LGW_SPI_MUX_TARGET_FPGA 0x1 43 | #define LGW_SPI_MUX_TARGET_EEPROM 0x2 44 | #define LGW_SPI_MUX_TARGET_SX127X 0x3 45 | 46 | /* -------------------------------------------------------------------------- */ 47 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 48 | 49 | /** 50 | @brief LoRa concentrator SPI setup (configure I/O and peripherals) 51 | @param spi_target_ptr pointer on a generic pointer to SPI target (implementation dependant) 52 | @return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) 53 | */ 54 | 55 | int lgw_spi_open(void **spi_target_ptr); 56 | 57 | /** 58 | @brief LoRa concentrator SPI close 59 | @param spi_target generic pointer to SPI target (implementation dependant) 60 | @return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) 61 | */ 62 | 63 | int lgw_spi_close(void *spi_target); 64 | 65 | /** 66 | @brief LoRa concentrator SPI single-byte write 67 | @param spi_target generic pointer to SPI target (implementation dependant) 68 | @param address 7-bit register address 69 | @param data data byte to write 70 | @return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) 71 | */ 72 | int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data); 73 | 74 | /** 75 | @brief LoRa concentrator SPI single-byte read 76 | @param spi_target generic pointer to SPI target (implementation dependant) 77 | @param address 7-bit register address 78 | @param data data byte to write 79 | @return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) 80 | */ 81 | int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data); 82 | 83 | /** 84 | @brief LoRa concentrator SPI burst (multiple-byte) write 85 | @param spi_target generic pointer to SPI target (implementation dependant) 86 | @param address 7-bit register address 87 | @param data pointer to byte array that will be sent to the LoRa concentrator 88 | @param size size of the transfer, in byte(s) 89 | @return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) 90 | */ 91 | int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size); 92 | 93 | /** 94 | @brief LoRa concentrator SPI burst (multiple-byte) read 95 | @param spi_target generic pointer to SPI target (implementation dependant) 96 | @param address 7-bit register address 97 | @param data pointer to byte array that will be written from the LoRa concentrator 98 | @param size size of the transfer, in byte(s) 99 | @return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) 100 | */ 101 | int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size); 102 | 103 | #endif 104 | 105 | /* --- EOF ------------------------------------------------------------------ */ 106 | -------------------------------------------------------------------------------- /libloragw/inc/loragw_sx125x.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech 8 | 9 | Description: SX125x radio registers and constant definitions 10 | 11 | License: Revised BSD License, see LICENSE.TXT file include in the project 12 | 13 | Maintainer: Michael Coracin 14 | */ 15 | #ifndef __SX125X_REGS_H__ 16 | #define __SX125X_REGS_H__ 17 | 18 | /* 19 | SX1257 frequency setting : 20 | F_register(24bit) = F_rf (Hz) / F_step(Hz) 21 | = F_rf (Hz) * 2^19 / F_xtal(Hz) 22 | = F_rf (Hz) * 2^19 / 32e6 23 | = F_rf (Hz) * 256/15625 24 | 25 | SX1255 frequency setting : 26 | F_register(24bit) = F_rf (Hz) / F_step(Hz) 27 | = F_rf (Hz) * 2^20 / F_xtal(Hz) 28 | = F_rf (Hz) * 2^20 / 32e6 29 | = F_rf (Hz) * 512/15625 30 | */ 31 | 32 | #define SX125x_TX_DAC_CLK_SEL 1 /* 0:int, 1:ext */ 33 | #define SX125x_TX_DAC_GAIN 2 /* 3:0, 2:-3, 1:-6, 0:-9 dBFS (default 2) */ 34 | #define SX125x_TX_MIX_GAIN 14 /* -38 + 2*TxMixGain dB (default 14) */ 35 | #define SX125x_TX_PLL_BW 1 /* 0:75, 1:150, 2:225, 3:300 kHz (default 3) */ 36 | #define SX125x_TX_ANA_BW 0 /* 17.5 / 2*(41-TxAnaBw) MHz (default 0) */ 37 | #define SX125x_TX_DAC_BW 5 /* 24 + 8*TxDacBw Nb FIR taps (default 2) */ 38 | #define SX125x_RX_LNA_GAIN 1 /* 1 to 6, 1 highest gain */ 39 | #define SX125x_RX_BB_GAIN 12 /* 0 to 15 , 15 highest gain */ 40 | #define SX125x_LNA_ZIN 1 /* 0:50, 1:200 Ohms (default 1) */ 41 | #define SX125x_RX_ADC_BW 7 /* 0 to 7, 2:100= 199901L 22 | #define _XOPEN_SOURCE 600 23 | #else 24 | #define _XOPEN_SOURCE 500 25 | #endif 26 | 27 | #include /* printf fprintf */ 28 | #include /* clock_nanosleep */ 29 | 30 | /* -------------------------------------------------------------------------- */ 31 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 32 | 33 | #if DEBUG_AUX == 1 34 | #define DEBUG_MSG(str) fprintf(stderr, str) 35 | #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) 36 | #else 37 | #define DEBUG_MSG(str) 38 | #define DEBUG_PRINTF(fmt, args...) 39 | #endif 40 | 41 | /* -------------------------------------------------------------------------- */ 42 | /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ 43 | 44 | /* This implementation is POSIX-pecific and require a fix to be compatible with C99 */ 45 | void wait_ms(unsigned long a) { 46 | struct timespec dly; 47 | struct timespec rem; 48 | 49 | dly.tv_sec = a / 1000; 50 | dly.tv_nsec = ((long)a % 1000) * 1000000; 51 | 52 | DEBUG_PRINTF("NOTE dly: %ld sec %ld ns\n", dly.tv_sec, dly.tv_nsec); 53 | 54 | if((dly.tv_sec > 0) || ((dly.tv_sec == 0) && (dly.tv_nsec > 100000))) { 55 | clock_nanosleep(CLOCK_MONOTONIC, 0, &dly, &rem); 56 | DEBUG_PRINTF("NOTE remain: %ld sec %ld ns\n", rem.tv_sec, rem.tv_nsec); 57 | } 58 | return; 59 | } 60 | 61 | /* --- EOF ------------------------------------------------------------------ */ 62 | -------------------------------------------------------------------------------- /libloragw/src/loragw_fpga.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Functions used to handle FPGA register access for LoRa concentrator. 11 | Registers are addressed by name. 12 | Multi-bytes registers are handled automatically. 13 | Read-modify-write is handled automatically. 14 | 15 | License: Revised BSD License, see LICENSE.TXT file include in the project 16 | Maintainer: Michael Coracin 17 | */ 18 | 19 | /* -------------------------------------------------------------------------- */ 20 | /* --- DEPENDANCIES --------------------------------------------------------- */ 21 | 22 | #include /* C99 types */ 23 | #include /* bool type */ 24 | #include /* printf fprintf */ 25 | 26 | #include "loragw_spi.h" 27 | #include "loragw_aux.h" 28 | #include "loragw_hal.h" 29 | #include "loragw_reg.h" 30 | #include "loragw_fpga.h" 31 | 32 | /* -------------------------------------------------------------------------- */ 33 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 34 | 35 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 36 | #if DEBUG_REG == 1 37 | #define DEBUG_MSG(str) fprintf(stderr, str) 38 | #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) 39 | #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} 40 | #else 41 | #define DEBUG_MSG(str) 42 | #define DEBUG_PRINTF(fmt, args...) 43 | #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} 44 | #endif 45 | 46 | /* -------------------------------------------------------------------------- */ 47 | /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ 48 | 49 | /* 50 | auto generated register mapping for C code : 11-Jul-2013 13:20:40 51 | this file contains autogenerated C struct used to access the LoRa register from the Primer firmware 52 | this file is autogenerated from registers description 53 | 293 registers are defined 54 | */ 55 | const struct lgw_reg_s fpga_regs[LGW_FPGA_TOTALREGS] = { 56 | {-1,0,0,0,1,0,0}, /* SOFT_RESET */ 57 | {-1,0,1,0,4,1,0}, /* FPGA_FEATURE */ 58 | {-1,0,5,0,3,1,0}, /* LBT_INITIAL_FREQ */ 59 | {-1,1,0,0,8,1,0}, /* VERSION */ 60 | {-1,2,0,0,8,1,0}, /* FPGA_STATUS */ 61 | {-1,3,0,0,1,0,0}, /* FPGA_CTRL_FEATURE_START */ 62 | {-1,3,1,0,1,0,0}, /* FPGA_CTRL_RADIO_RESET */ 63 | {-1,3,2,0,1,0,0}, /* FPGA_CTRL_INPUT_SYNC_I */ 64 | {-1,3,3,0,1,0,0}, /* FPGA_CTRL_INPUT_SYNC_Q */ 65 | {-1,3,4,0,1,0,0}, /* FPGA_CTRL_OUTPUT_SYNC */ 66 | {-1,3,5,0,1,0,0}, /* FPGA_CTRL_INVERT_IQ */ 67 | {-1,3,6,0,1,0,0}, /* FPGA_CTRL_ACCESS_HISTO_MEM */ 68 | {-1,3,7,0,1,0,0}, /* FPGA_CTRL_CLEAR_HISTO_MEM */ 69 | {-1,4,0,0,8,0,0}, /* HISTO_RAM_ADDR */ 70 | {-1,5,0,0,8,1,0}, /* HISTO_RAM_DATA */ 71 | {-1,8,0,0,16,0,1000}, /* HISTO_NB_READ */ 72 | {-1,14,0,0,16,1,0}, /* LBT_TIMESTAMP_CH */ 73 | {-1,17,0,0,4,0,0}, /* LBT_TIMESTAMP_SELECT_CH */ 74 | {-1,18,0,0,8,0,0}, /* LBT_CH0_FREQ_OFFSET */ 75 | {-1,19,0,0,8,0,0}, /* LBT_CH1_FREQ_OFFSET */ 76 | {-1,20,0,0,8,0,0}, /* LBT_CH2_FREQ_OFFSET */ 77 | {-1,21,0,0,8,0,0}, /* LBT_CH3_FREQ_OFFSET */ 78 | {-1,22,0,0,8,0,0}, /* LBT_CH4_FREQ_OFFSET */ 79 | {-1,23,0,0,8,0,0}, /* LBT_CH5_FREQ_OFFSET */ 80 | {-1,24,0,0,8,0,0}, /* LBT_CH6_FREQ_OFFSET */ 81 | {-1,25,0,0,8,0,0}, /* LBT_CH7_FREQ_OFFSET */ 82 | {-1,26,0,0,8,0,0}, /* SCAN_FREQ_OFFSET */ 83 | {-1,28,0,0,1,0,0}, /* LBT_SCAN_TIME_CH0 */ 84 | {-1,28,1,0,1,0,0}, /* LBT_SCAN_TIME_CH1 */ 85 | {-1,28,2,0,1,0,0}, /* LBT_SCAN_TIME_CH2 */ 86 | {-1,28,3,0,1,0,0}, /* LBT_SCAN_TIME_CH3 */ 87 | {-1,28,4,0,1,0,0}, /* LBT_SCAN_TIME_CH4 */ 88 | {-1,28,5,0,1,0,0}, /* LBT_SCAN_TIME_CH5 */ 89 | {-1,28,6,0,1,0,0}, /* LBT_SCAN_TIME_CH6 */ 90 | {-1,28,7,0,1,0,0}, /* LBT_SCAN_TIME_CH7 */ 91 | {-1,30,0,0,8,0,160}, /* RSSI_TARGET */ 92 | {-1,31,0,0,24,0,0}, /* HISTO_SCAN_FREQ */ 93 | {-1,34,0,0,6,0,0} /* NOTCH_FREQ_OFFSET */ 94 | }; 95 | 96 | /* -------------------------------------------------------------------------- */ 97 | /* --- INTERNAL SHARED VARIABLES -------------------------------------------- */ 98 | 99 | extern void *lgw_spi_target; /*! generic pointer to the SPI device */ 100 | extern uint8_t lgw_spi_mux_mode; /*! current SPI mux mode used */ 101 | 102 | /* -------------------------------------------------------------------------- */ 103 | /* --- PRIVATE VARIABLES ---------------------------------------------------- */ 104 | static bool tx_notch_support = false; 105 | static uint8_t tx_notch_offset; 106 | 107 | /* -------------------------------------------------------------------------- */ 108 | /* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ 109 | 110 | /* -------------------------------------------------------------------------- */ 111 | /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ 112 | 113 | /* -------------------------------------------------------------------------- */ 114 | /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ 115 | 116 | float lgw_fpga_get_tx_notch_delay(void) { 117 | float tx_notch_delay; 118 | 119 | if (tx_notch_support == false) { 120 | return 0; 121 | } 122 | 123 | /* Notch filtering performed by FPGA adds a constant delay (group delay) that we need to compensate */ 124 | tx_notch_delay = (31.25 * ((64 + tx_notch_offset) / 2)) / 1E3; /* 32MHz => 31.25ns */ 125 | 126 | return tx_notch_delay; 127 | } 128 | 129 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 130 | 131 | int lgw_fpga_configure(uint32_t tx_notch_freq) { 132 | int x; 133 | int32_t val; 134 | bool spectral_scan_support, lbt_support; 135 | 136 | /* Check input parameters */ 137 | if ((tx_notch_freq < LGW_MIN_NOTCH_FREQ) || (tx_notch_freq > LGW_MAX_NOTCH_FREQ)) { 138 | DEBUG_PRINTF("WARNING: FPGA TX notch frequency is out of range (%u - [%u..%u]), setting it to default (%u)\n", tx_notch_freq, LGW_MIN_NOTCH_FREQ, LGW_MAX_NOTCH_FREQ, LGW_DEFAULT_NOTCH_FREQ); 139 | tx_notch_freq = LGW_DEFAULT_NOTCH_FREQ; 140 | } 141 | 142 | /* Get supported FPGA features */ 143 | printf("INFO: FPGA supported features:"); 144 | lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); 145 | tx_notch_support = TAKE_N_BITS_FROM((uint8_t)val, 0, 1); 146 | if (tx_notch_support == true) { 147 | printf(" [TX filter] "); 148 | } 149 | spectral_scan_support = TAKE_N_BITS_FROM((uint8_t)val, 1, 1); 150 | if (spectral_scan_support == true) { 151 | printf(" [Spectral Scan] "); 152 | } 153 | lbt_support = TAKE_N_BITS_FROM((uint8_t)val, 2, 1); 154 | if (lbt_support == true) { 155 | printf(" [LBT] "); 156 | } 157 | printf("\n"); 158 | 159 | x = lgw_fpga_reg_w(LGW_FPGA_CTRL_INPUT_SYNC_I, 1); 160 | x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_INPUT_SYNC_Q, 1); 161 | x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_OUTPUT_SYNC, 0); 162 | if (x != LGW_REG_SUCCESS) { 163 | DEBUG_MSG("ERROR: Failed to configure FPGA TX synchro\n"); 164 | return LGW_REG_ERROR; 165 | } 166 | 167 | /* Required for Semtech AP2 reference design */ 168 | x = lgw_fpga_reg_w(LGW_FPGA_CTRL_INVERT_IQ, 1); 169 | if (x != LGW_REG_SUCCESS) { 170 | DEBUG_MSG("ERROR: Failed to configure FPGA polarity\n"); 171 | return LGW_REG_ERROR; 172 | } 173 | 174 | /* Configure TX notch filter */ 175 | if (tx_notch_support == true) { 176 | tx_notch_offset = (32E6 / (2*tx_notch_freq)) - 64; 177 | x = lgw_fpga_reg_w(LGW_FPGA_NOTCH_FREQ_OFFSET, (int32_t)tx_notch_offset); 178 | if (x != LGW_REG_SUCCESS) { 179 | DEBUG_MSG("ERROR: Failed to configure FPGA TX notch filter\n"); 180 | return LGW_REG_ERROR; 181 | } 182 | 183 | /* Readback to check that notch frequency is programmable */ 184 | x = lgw_fpga_reg_r(LGW_FPGA_NOTCH_FREQ_OFFSET, &val); 185 | if (x != LGW_REG_SUCCESS) { 186 | DEBUG_MSG("ERROR: Failed to read FPGA TX notch frequency\n"); 187 | return LGW_REG_ERROR; 188 | } 189 | if (val != tx_notch_offset) { 190 | DEBUG_MSG("WARNING: TX notch filter frequency is not programmable (check your FPGA image)\n"); 191 | } else { 192 | DEBUG_PRINTF("INFO: TX notch filter frequency set to %u (%i)\n", tx_notch_freq, tx_notch_offset); 193 | } 194 | } 195 | 196 | return LGW_REG_SUCCESS; 197 | } 198 | 199 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 200 | 201 | /* Write to a register addressed by name */ 202 | int lgw_fpga_reg_w(uint16_t register_id, int32_t reg_value) { 203 | int spi_stat = LGW_SPI_SUCCESS; 204 | struct lgw_reg_s r; 205 | 206 | /* check input parameters */ 207 | if (register_id >= LGW_FPGA_TOTALREGS) { 208 | DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); 209 | return LGW_REG_ERROR; 210 | } 211 | 212 | /* check if SPI is initialised */ 213 | if (lgw_spi_target == NULL) { 214 | DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); 215 | return LGW_REG_ERROR; 216 | } 217 | 218 | /* get register struct from the struct array */ 219 | r = fpga_regs[register_id]; 220 | 221 | /* reject write to read-only registers */ 222 | if (r.rdon == 1){ 223 | DEBUG_MSG("ERROR: TRYING TO WRITE A READ-ONLY REGISTER\n"); 224 | return LGW_REG_ERROR; 225 | } 226 | 227 | spi_stat += reg_w_align32(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r, reg_value); 228 | 229 | if (spi_stat != LGW_SPI_SUCCESS) { 230 | DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); 231 | return LGW_REG_ERROR; 232 | } else { 233 | return LGW_REG_SUCCESS; 234 | } 235 | } 236 | 237 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 238 | 239 | /* Read to a register addressed by name */ 240 | int lgw_fpga_reg_r(uint16_t register_id, int32_t *reg_value) { 241 | int spi_stat = LGW_SPI_SUCCESS; 242 | struct lgw_reg_s r; 243 | 244 | /* check input parameters */ 245 | CHECK_NULL(reg_value); 246 | if (register_id >= LGW_FPGA_TOTALREGS) { 247 | DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); 248 | return LGW_REG_ERROR; 249 | } 250 | 251 | /* check if SPI is initialised */ 252 | if (lgw_spi_target == NULL) { 253 | DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); 254 | return LGW_REG_ERROR; 255 | } 256 | 257 | /* get register struct from the struct array */ 258 | r = fpga_regs[register_id]; 259 | 260 | spi_stat += reg_r_align32(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r, reg_value); 261 | 262 | if (spi_stat != LGW_SPI_SUCCESS) { 263 | DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); 264 | return LGW_REG_ERROR; 265 | } else { 266 | return LGW_REG_SUCCESS; 267 | } 268 | } 269 | 270 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 271 | 272 | /* Point to a register by name and do a burst write */ 273 | int lgw_fpga_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size) { 274 | int spi_stat = LGW_SPI_SUCCESS; 275 | struct lgw_reg_s r; 276 | 277 | /* check input parameters */ 278 | CHECK_NULL(data); 279 | if (size == 0) { 280 | DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); 281 | return LGW_REG_ERROR; 282 | } 283 | if (register_id >= LGW_FPGA_TOTALREGS) { 284 | DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); 285 | return LGW_REG_ERROR; 286 | } 287 | 288 | /* check if SPI is initialised */ 289 | if (lgw_spi_target == NULL) { 290 | DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); 291 | return LGW_REG_ERROR; 292 | } 293 | 294 | /* get register struct from the struct array */ 295 | r = fpga_regs[register_id]; 296 | 297 | /* reject write to read-only registers */ 298 | if (r.rdon == 1){ 299 | DEBUG_MSG("ERROR: TRYING TO BURST WRITE A READ-ONLY REGISTER\n"); 300 | return LGW_REG_ERROR; 301 | } 302 | 303 | /* do the burst write */ 304 | spi_stat += lgw_spi_wb(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r.addr, data, size); 305 | 306 | if (spi_stat != LGW_SPI_SUCCESS) { 307 | DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST WRITE\n"); 308 | return LGW_REG_ERROR; 309 | } else { 310 | return LGW_REG_SUCCESS; 311 | } 312 | } 313 | 314 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 315 | 316 | /* Point to a register by name and do a burst read */ 317 | int lgw_fpga_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size) { 318 | int spi_stat = LGW_SPI_SUCCESS; 319 | struct lgw_reg_s r; 320 | 321 | /* check input parameters */ 322 | CHECK_NULL(data); 323 | if (size == 0) { 324 | DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); 325 | return LGW_REG_ERROR; 326 | } 327 | if (register_id >= LGW_FPGA_TOTALREGS) { 328 | DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); 329 | return LGW_REG_ERROR; 330 | } 331 | 332 | /* check if SPI is initialised */ 333 | if (lgw_spi_target == NULL) { 334 | DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); 335 | return LGW_REG_ERROR; 336 | } 337 | 338 | /* get register struct from the struct array */ 339 | r = fpga_regs[register_id]; 340 | 341 | /* do the burst read */ 342 | spi_stat += lgw_spi_rb(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r.addr, data, size); 343 | 344 | if (spi_stat != LGW_SPI_SUCCESS) { 345 | DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST READ\n"); 346 | return LGW_REG_ERROR; 347 | } else { 348 | return LGW_REG_SUCCESS; 349 | } 350 | } 351 | 352 | /* --- EOF ------------------------------------------------------------------ */ 353 | -------------------------------------------------------------------------------- /libloragw/src/loragw_lbt.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Functions used to handle the Listen Before Talk feature 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Michael Coracin 14 | */ 15 | 16 | /* -------------------------------------------------------------------------- */ 17 | /* --- DEPENDANCIES --------------------------------------------------------- */ 18 | 19 | #include /* C99 types */ 20 | #include /* bool type */ 21 | #include /* printf fprintf */ 22 | #include /* abs, labs, llabs */ 23 | #include /* memset */ 24 | 25 | #include "loragw_radio.h" 26 | #include "loragw_aux.h" 27 | #include "loragw_lbt.h" 28 | #include "loragw_fpga.h" 29 | 30 | /* -------------------------------------------------------------------------- */ 31 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 32 | 33 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 34 | #if DEBUG_LBT == 1 35 | #define DEBUG_MSG(str) fprintf(stderr, str) 36 | #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) 37 | #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} 38 | #else 39 | #define DEBUG_MSG(str) 40 | #define DEBUG_PRINTF(fmt, args...) 41 | #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} 42 | #endif 43 | 44 | #define LBT_TIMESTAMP_MASK 0x007FF000 /* 11-bits timestamp */ 45 | 46 | /* -------------------------------------------------------------------------- */ 47 | /* --- PRIVATE TYPES -------------------------------------------------------- */ 48 | 49 | /* -------------------------------------------------------------------------- */ 50 | /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ 51 | 52 | /* -------------------------------------------------------------------------- */ 53 | /* --- INTERNAL SHARED VARIABLES -------------------------------------------- */ 54 | 55 | extern void *lgw_spi_target; /*! generic pointer to the SPI device */ 56 | extern uint8_t lgw_spi_mux_mode; /*! current SPI mux mode used */ 57 | extern uint16_t lgw_i_tx_start_delay_us; 58 | 59 | /* -------------------------------------------------------------------------- */ 60 | /* --- PRIVATE VARIABLES ---------------------------------------------------- */ 61 | 62 | static bool lbt_enable; 63 | static uint8_t lbt_nb_active_channel; 64 | static int8_t lbt_rssi_target_dBm; 65 | static int8_t lbt_rssi_offset_dB; 66 | static uint32_t lbt_start_freq; 67 | static struct lgw_conf_lbt_chan_s lbt_channel_cfg[LBT_CHANNEL_FREQ_NB]; 68 | 69 | /* -------------------------------------------------------------------------- */ 70 | /* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ 71 | 72 | bool is_equal_freq(uint32_t a, uint32_t b); 73 | 74 | /* -------------------------------------------------------------------------- */ 75 | /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ 76 | 77 | int lbt_setconf(struct lgw_conf_lbt_s * conf) { 78 | int i; 79 | 80 | /* Check input parameters */ 81 | if (conf == NULL) { 82 | return LGW_LBT_ERROR; 83 | } 84 | if ((conf->nb_channel < 1) || (conf->nb_channel > LBT_CHANNEL_FREQ_NB)) { 85 | DEBUG_PRINTF("ERROR: Number of defined LBT channels is out of range (%u)\n", conf->nb_channel); 86 | return LGW_LBT_ERROR; 87 | } 88 | 89 | /* Initialize LBT channels configuration */ 90 | memset(lbt_channel_cfg, 0, sizeof lbt_channel_cfg); 91 | 92 | /* Set internal LBT config according to parameters */ 93 | lbt_enable = conf->enable; 94 | lbt_nb_active_channel = conf->nb_channel; 95 | lbt_rssi_target_dBm = conf->rssi_target; 96 | lbt_rssi_offset_dB = conf->rssi_offset; 97 | 98 | for (i=0; ichannels[i].freq_hz; 100 | lbt_channel_cfg[i].scan_time_us = conf->channels[i].scan_time_us; 101 | } 102 | 103 | return LGW_LBT_SUCCESS; 104 | } 105 | 106 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 107 | 108 | int lbt_setup(void) { 109 | int x, i; 110 | int32_t val; 111 | uint32_t freq_offset; 112 | 113 | /* Check if LBT feature is supported by FPGA */ 114 | x = lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); 115 | if (x != LGW_REG_SUCCESS) { 116 | DEBUG_MSG("ERROR: Failed to read FPGA Features register\n"); 117 | return LGW_LBT_ERROR; 118 | } 119 | if (TAKE_N_BITS_FROM((uint8_t)val, 2, 1) != 1) { 120 | DEBUG_MSG("ERROR: No support for LBT in FPGA\n"); 121 | return LGW_LBT_ERROR; 122 | } 123 | 124 | /* Get FPGA lowest frequency for LBT channels */ 125 | x = lgw_fpga_reg_r(LGW_FPGA_LBT_INITIAL_FREQ, &val); 126 | if (x != LGW_REG_SUCCESS) { 127 | DEBUG_MSG("ERROR: Failed to read LBT initial frequency from FPGA\n"); 128 | return LGW_LBT_ERROR; 129 | } 130 | switch(val) { 131 | case 0: 132 | lbt_start_freq = 915000000; 133 | break; 134 | case 1: 135 | lbt_start_freq = 863000000; 136 | break; 137 | default: 138 | DEBUG_PRINTF("ERROR: LBT start frequency %d is not supported\n", val); 139 | return LGW_LBT_ERROR; 140 | } 141 | 142 | /* Configure SX127x for FSK */ 143 | x = lgw_setup_sx127x(lbt_start_freq, MOD_FSK, LGW_SX127X_RXBW_100K_HZ, lbt_rssi_offset_dB); /* 200KHz LBT channels */ 144 | if (x != LGW_REG_SUCCESS) { 145 | DEBUG_MSG("ERROR: Failed to configure SX127x for LBT\n"); 146 | return LGW_LBT_ERROR; 147 | } 148 | 149 | /* Configure FPGA for LBT */ 150 | val = -2*lbt_rssi_target_dBm; /* Convert RSSI target in dBm to FPGA register format */ 151 | x = lgw_fpga_reg_w(LGW_FPGA_RSSI_TARGET, val); 152 | if (x != LGW_REG_SUCCESS) { 153 | DEBUG_MSG("ERROR: Failed to configure FPGA for LBT\n"); 154 | return LGW_LBT_ERROR; 155 | } 156 | /* Set default values for non-active LBT channels */ 157 | for (i=lbt_nb_active_channel; imodulation != MOD_LORA) { 242 | *tx_allowed = false; 243 | DEBUG_PRINTF("INFO: TX is not allowed for this modulation (%x)\n", pkt_data->modulation); 244 | return LGW_LBT_SUCCESS; 245 | } 246 | 247 | /* Get SX1301 time at last PPS */ 248 | lgw_get_trigcnt(&sx1301_time); 249 | 250 | DEBUG_MSG("################################\n"); 251 | switch(pkt_data->tx_mode) { 252 | case TIMESTAMPED: 253 | DEBUG_MSG("tx_mode = TIMESTAMPED\n"); 254 | tx_start_time = pkt_data->count_us & LBT_TIMESTAMP_MASK; 255 | break; 256 | case ON_GPS: 257 | DEBUG_MSG("tx_mode = ON_GPS\n"); 258 | tx_start_time = (sx1301_time + (uint32_t)tx_start_delay + 1000000) & LBT_TIMESTAMP_MASK; 259 | break; 260 | case IMMEDIATE: 261 | DEBUG_MSG("ERROR: tx_mode IMMEDIATE is not supported when LBT is enabled\n"); 262 | /* FALLTHROUGH */ 263 | default: 264 | return LGW_LBT_ERROR; 265 | } 266 | 267 | /* Select LBT Channel corresponding to required TX frequency */ 268 | lbt_channel_decod_1 = -1; 269 | lbt_channel_decod_2 = -1; 270 | if (pkt_data->bandwidth == BW_125KHZ) { 271 | for (i=0; ifreq_hz, lbt_channel_cfg[i].freq_hz) == true) { 273 | DEBUG_PRINTF("LBT: select channel %d (%u Hz)\n", i, lbt_channel_cfg[i].freq_hz); 274 | lbt_channel_decod_1 = i; 275 | lbt_channel_decod_2 = i; 276 | if (lbt_channel_cfg[i].scan_time_us == 5000) { 277 | tx_max_time = 4000000; /* 4 seconds */ 278 | } else { /* scan_time_us = 128 */ 279 | tx_max_time = 400000; /* 400 milliseconds */ 280 | } 281 | break; 282 | } 283 | } 284 | } else if (pkt_data->bandwidth == BW_250KHZ) { 285 | /* In case of 250KHz, the TX freq has to be in between 2 consecutive channels of 200KHz BW. 286 | The TX can only be over 2 channels, not more */ 287 | for (i=0; i<(lbt_nb_active_channel-1); i++) { 288 | if ((is_equal_freq(pkt_data->freq_hz, (lbt_channel_cfg[i].freq_hz+lbt_channel_cfg[i+1].freq_hz)/2) == true) && ((lbt_channel_cfg[i+1].freq_hz-lbt_channel_cfg[i].freq_hz)==200E3)) { 289 | DEBUG_PRINTF("LBT: select channels %d,%d (%u Hz)\n", i, i+1, (lbt_channel_cfg[i].freq_hz+lbt_channel_cfg[i+1].freq_hz)/2); 290 | lbt_channel_decod_1 = i; 291 | lbt_channel_decod_2 = i+1; 292 | if (lbt_channel_cfg[i].scan_time_us == 5000) { 293 | tx_max_time = 4000000; /* 4 seconds */ 294 | } else { /* scan_time_us = 128 */ 295 | tx_max_time = 200000; /* 200 milliseconds */ 296 | } 297 | break; 298 | } 299 | } 300 | } else { 301 | /* Nothing to do for now */ 302 | } 303 | 304 | /* Get last time when selected channel was free */ 305 | if ((lbt_channel_decod_1 >= 0) && (lbt_channel_decod_2 >= 0)) { 306 | lgw_fpga_reg_w(LGW_FPGA_LBT_TIMESTAMP_SELECT_CH, (int32_t)lbt_channel_decod_1); 307 | lgw_fpga_reg_r(LGW_FPGA_LBT_TIMESTAMP_CH, &val); 308 | lbt_time = lbt_time1 = (uint32_t)(val & 0x0000FFFF) * 256; /* 16bits (1LSB = 256µs) */ 309 | 310 | if (lbt_channel_decod_1 != lbt_channel_decod_2 ) { 311 | lgw_fpga_reg_w(LGW_FPGA_LBT_TIMESTAMP_SELECT_CH, (int32_t)lbt_channel_decod_2); 312 | lgw_fpga_reg_r(LGW_FPGA_LBT_TIMESTAMP_CH, &val); 313 | lbt_time2 = (uint32_t)(val & 0x0000FFFF) * 256; /* 16bits (1LSB = 256µs) */ 314 | 315 | if (lbt_time2 < lbt_time1) { 316 | lbt_time = lbt_time2; 317 | } 318 | } 319 | } else { 320 | lbt_time = 0; 321 | } 322 | 323 | packet_duration = lgw_time_on_air(pkt_data) * 1000UL; 324 | tx_end_time = (tx_start_time + packet_duration) & LBT_TIMESTAMP_MASK; 325 | if (lbt_time < tx_end_time) { 326 | delta_time = tx_end_time - lbt_time; 327 | } else { 328 | /* It means LBT counter has wrapped */ 329 | printf("LBT: lbt counter has wrapped\n"); 330 | delta_time = (LBT_TIMESTAMP_MASK - lbt_time) + tx_end_time; 331 | } 332 | 333 | DEBUG_PRINTF("sx1301_time = %u\n", sx1301_time & LBT_TIMESTAMP_MASK); 334 | DEBUG_PRINTF("tx_freq = %u\n", pkt_data->freq_hz); 335 | DEBUG_MSG("------------------------------------------------\n"); 336 | DEBUG_PRINTF("packet_duration = %u\n", packet_duration); 337 | DEBUG_PRINTF("tx_start_time = %u\n", tx_start_time); 338 | DEBUG_PRINTF("lbt_time1 = %u\n", lbt_time1); 339 | DEBUG_PRINTF("lbt_time2 = %u\n", lbt_time2); 340 | DEBUG_PRINTF("lbt_time = %u\n", lbt_time); 341 | DEBUG_PRINTF("delta_time = %u\n", delta_time); 342 | DEBUG_MSG("------------------------------------------------\n"); 343 | 344 | /* send data if allowed */ 345 | /* lbt_time: last time when channel was free */ 346 | /* tx_max_time: maximum time allowed to send packet since last free time */ 347 | /* 2048: some margin */ 348 | if ((delta_time < (tx_max_time - 2048)) && (lbt_time != 0)) { 349 | *tx_allowed = true; 350 | } else { 351 | DEBUG_MSG("ERROR: TX request rejected (LBT)\n"); 352 | *tx_allowed = false; 353 | } 354 | } else { 355 | /* Always allow if LBT is disabled */ 356 | *tx_allowed = true; 357 | } 358 | 359 | return LGW_LBT_SUCCESS; 360 | } 361 | 362 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 363 | 364 | bool lbt_is_enabled(void) { 365 | return lbt_enable; 366 | } 367 | 368 | /* -------------------------------------------------------------------------- */ 369 | /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ 370 | 371 | /* As given frequencies have been converted from float to integer, some aliasing 372 | issues can appear, so we can't simply check for equality, but have to take some 373 | margin */ 374 | bool is_equal_freq(uint32_t a, uint32_t b) { 375 | int64_t diff; 376 | int64_t a64 = (int64_t)a; 377 | int64_t b64 = (int64_t)b; 378 | 379 | /* Calculate the difference */ 380 | diff = llabs(a64 - b64); 381 | 382 | /* Check for acceptable diff range */ 383 | if( diff <= 10000 ) 384 | { 385 | return true; 386 | } 387 | 388 | return false; 389 | } 390 | 391 | /* --- EOF ------------------------------------------------------------------ */ 392 | -------------------------------------------------------------------------------- /libloragw/src/loragw_spi.native.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Host specific functions to address the LoRa concentrator registers through 11 | a SPI interface. 12 | Single-byte read/write and burst read/write. 13 | Does not handle pagination. 14 | Could be used with multiple SPI ports in parallel (explicit file descriptor) 15 | 16 | License: Revised BSD License, see LICENSE.TXT file include in the project 17 | Maintainer: Sylvain Miermont 18 | */ 19 | 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | /* --- DEPENDANCIES --------------------------------------------------------- */ 23 | 24 | #include /* C99 types */ 25 | #include /* printf fprintf */ 26 | #include /* malloc free */ 27 | #include /* lseek, close */ 28 | #include /* open */ 29 | #include /* memset */ 30 | 31 | #include 32 | #include 33 | 34 | #include "loragw_spi.h" 35 | #include "loragw_hal.h" 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 39 | 40 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 41 | #if DEBUG_SPI == 1 42 | #define DEBUG_MSG(str) fprintf(stderr, str) 43 | #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) 44 | #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;} 45 | #else 46 | #define DEBUG_MSG(str) 47 | #define DEBUG_PRINTF(fmt, args...) 48 | #define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;} 49 | #endif 50 | 51 | /* -------------------------------------------------------------------------- */ 52 | /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ 53 | 54 | #define READ_ACCESS 0x00 55 | #define WRITE_ACCESS 0x80 56 | #define SPI_SPEED 1000000 57 | #define SPI_DEV_PATH "/dev/spidev0.0" 58 | //#define SPI_DEV_PATH "/dev/spidev32766.0" 59 | 60 | /* -------------------------------------------------------------------------- */ 61 | /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ 62 | 63 | /* SPI initialization and configuration */ 64 | int lgw_spi_open(void **spi_target_ptr) { 65 | int *spi_device = NULL; 66 | int dev; 67 | int a=0, b=0; 68 | int i; 69 | 70 | /* check input variables */ 71 | CHECK_NULL(spi_target_ptr); /* cannot be null, must point on a void pointer (*spi_target_ptr can be null) */ 72 | 73 | /* allocate memory for the device descriptor */ 74 | spi_device = malloc(sizeof(int)); 75 | if (spi_device == NULL) { 76 | DEBUG_MSG("ERROR: MALLOC FAIL\n"); 77 | return LGW_SPI_ERROR; 78 | } 79 | 80 | /* open SPI device */ 81 | dev = open(SPI_DEV_PATH, O_RDWR); 82 | if (dev < 0) { 83 | DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", SPI_DEV_PATH); 84 | return LGW_SPI_ERROR; 85 | } 86 | 87 | /* setting SPI mode to 'mode 0' */ 88 | i = SPI_MODE_0; 89 | a = ioctl(dev, SPI_IOC_WR_MODE, &i); 90 | b = ioctl(dev, SPI_IOC_RD_MODE, &i); 91 | if ((a < 0) || (b < 0)) { 92 | DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n"); 93 | close(dev); 94 | free(spi_device); 95 | return LGW_SPI_ERROR; 96 | } 97 | 98 | /* setting SPI max clk (in Hz) */ 99 | i = SPI_SPEED; 100 | a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i); 101 | b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i); 102 | if ((a < 0) || (b < 0)) { 103 | DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n"); 104 | close(dev); 105 | free(spi_device); 106 | return LGW_SPI_ERROR; 107 | } 108 | 109 | /* setting SPI to MSB first */ 110 | i = 0; 111 | a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i); 112 | b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i); 113 | if ((a < 0) || (b < 0)) { 114 | DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n"); 115 | close(dev); 116 | free(spi_device); 117 | return LGW_SPI_ERROR; 118 | } 119 | 120 | /* setting SPI to 8 bits per word */ 121 | i = 0; 122 | a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i); 123 | b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i); 124 | if ((a < 0) || (b < 0)) { 125 | DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n"); 126 | close(dev); 127 | return LGW_SPI_ERROR; 128 | } 129 | 130 | *spi_device = dev; 131 | *spi_target_ptr = (void *)spi_device; 132 | DEBUG_MSG("Note: SPI port opened and configured ok\n"); 133 | return LGW_SPI_SUCCESS; 134 | } 135 | 136 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 137 | 138 | /* SPI release */ 139 | int lgw_spi_close(void *spi_target) { 140 | int spi_device; 141 | int a; 142 | 143 | /* check input variables */ 144 | CHECK_NULL(spi_target); 145 | 146 | /* close file & deallocate file descriptor */ 147 | spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ 148 | a = close(spi_device); 149 | free(spi_target); 150 | 151 | /* determine return code */ 152 | if (a < 0) { 153 | DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n"); 154 | return LGW_SPI_ERROR; 155 | } else { 156 | DEBUG_MSG("Note: SPI port closed\n"); 157 | return LGW_SPI_SUCCESS; 158 | } 159 | } 160 | 161 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 162 | 163 | /* Simple write */ 164 | int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data) { 165 | int spi_device; 166 | uint8_t out_buf[3]; 167 | uint8_t command_size; 168 | struct spi_ioc_transfer k; 169 | int a; 170 | 171 | /* check input variables */ 172 | CHECK_NULL(spi_target); 173 | if ((address & 0x80) != 0) { 174 | DEBUG_MSG("WARNING: SPI address > 127\n"); 175 | } 176 | 177 | spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ 178 | 179 | /* prepare frame to be sent */ 180 | if (spi_mux_mode == LGW_SPI_MUX_MODE1) { 181 | out_buf[0] = spi_mux_target; 182 | out_buf[1] = WRITE_ACCESS | (address & 0x7F); 183 | out_buf[2] = data; 184 | command_size = 3; 185 | } else { 186 | out_buf[0] = WRITE_ACCESS | (address & 0x7F); 187 | out_buf[1] = data; 188 | command_size = 2; 189 | } 190 | 191 | /* I/O transaction */ 192 | memset(&k, 0, sizeof(k)); /* clear k */ 193 | k.tx_buf = (unsigned long) out_buf; 194 | k.len = command_size; 195 | k.speed_hz = SPI_SPEED; 196 | k.cs_change = 0; 197 | k.bits_per_word = 8; 198 | a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); 199 | 200 | /* determine return code */ 201 | if (a != (int)k.len) { 202 | DEBUG_MSG("ERROR: SPI WRITE FAILURE\n"); 203 | return LGW_SPI_ERROR; 204 | } else { 205 | DEBUG_MSG("Note: SPI write success\n"); 206 | return LGW_SPI_SUCCESS; 207 | } 208 | } 209 | 210 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 211 | 212 | /* Simple read */ 213 | int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data) { 214 | int spi_device; 215 | uint8_t out_buf[3]; 216 | uint8_t command_size; 217 | uint8_t in_buf[ARRAY_SIZE(out_buf)]; 218 | struct spi_ioc_transfer k; 219 | int a; 220 | 221 | /* check input variables */ 222 | CHECK_NULL(spi_target); 223 | if ((address & 0x80) != 0) { 224 | DEBUG_MSG("WARNING: SPI address > 127\n"); 225 | } 226 | CHECK_NULL(data); 227 | 228 | spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ 229 | 230 | /* prepare frame to be sent */ 231 | if (spi_mux_mode == LGW_SPI_MUX_MODE1) { 232 | out_buf[0] = spi_mux_target; 233 | out_buf[1] = READ_ACCESS | (address & 0x7F); 234 | out_buf[2] = 0x00; 235 | command_size = 3; 236 | } else { 237 | out_buf[0] = READ_ACCESS | (address & 0x7F); 238 | out_buf[1] = 0x00; 239 | command_size = 2; 240 | } 241 | 242 | /* I/O transaction */ 243 | memset(&k, 0, sizeof(k)); /* clear k */ 244 | k.tx_buf = (unsigned long) out_buf; 245 | k.rx_buf = (unsigned long) in_buf; 246 | k.len = command_size; 247 | k.cs_change = 0; 248 | a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); 249 | 250 | /* determine return code */ 251 | if (a != (int)k.len) { 252 | DEBUG_MSG("ERROR: SPI READ FAILURE\n"); 253 | return LGW_SPI_ERROR; 254 | } else { 255 | DEBUG_MSG("Note: SPI read success\n"); 256 | *data = in_buf[command_size - 1]; 257 | return LGW_SPI_SUCCESS; 258 | } 259 | } 260 | 261 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 262 | 263 | /* Burst (multiple-byte) write */ 264 | int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size) { 265 | int spi_device; 266 | uint8_t command[2]; 267 | uint8_t command_size; 268 | struct spi_ioc_transfer k[2]; 269 | int size_to_do, chunk_size, offset; 270 | int byte_transfered = 0; 271 | int i; 272 | 273 | /* check input parameters */ 274 | CHECK_NULL(spi_target); 275 | if ((address & 0x80) != 0) { 276 | DEBUG_MSG("WARNING: SPI address > 127\n"); 277 | } 278 | CHECK_NULL(data); 279 | if (size == 0) { 280 | DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); 281 | return LGW_SPI_ERROR; 282 | } 283 | 284 | spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ 285 | 286 | /* prepare command byte */ 287 | if (spi_mux_mode == LGW_SPI_MUX_MODE1) { 288 | command[0] = spi_mux_target; 289 | command[1] = WRITE_ACCESS | (address & 0x7F); 290 | command_size = 2; 291 | } else { 292 | command[0] = WRITE_ACCESS | (address & 0x7F); 293 | command_size = 1; 294 | } 295 | size_to_do = size; 296 | 297 | /* I/O transaction */ 298 | memset(&k, 0, sizeof(k)); /* clear k */ 299 | k[0].tx_buf = (unsigned long) &command[0]; 300 | k[0].len = command_size; 301 | k[0].cs_change = 0; 302 | k[1].cs_change = 0; 303 | for (i=0; size_to_do > 0; ++i) { 304 | chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK; 305 | offset = i * LGW_BURST_CHUNK; 306 | k[1].tx_buf = (unsigned long)(data + offset); 307 | k[1].len = chunk_size; 308 | byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len ); 309 | DEBUG_PRINTF("BURST WRITE: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered); 310 | size_to_do -= chunk_size; /* subtract the quantity of data already transferred */ 311 | } 312 | 313 | /* determine return code */ 314 | if (byte_transfered != size) { 315 | DEBUG_MSG("ERROR: SPI BURST WRITE FAILURE\n"); 316 | return LGW_SPI_ERROR; 317 | } else { 318 | DEBUG_MSG("Note: SPI burst write success\n"); 319 | return LGW_SPI_SUCCESS; 320 | } 321 | } 322 | 323 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 324 | 325 | /* Burst (multiple-byte) read */ 326 | int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size) { 327 | int spi_device; 328 | uint8_t command[2]; 329 | uint8_t command_size; 330 | struct spi_ioc_transfer k[2]; 331 | int size_to_do, chunk_size, offset; 332 | int byte_transfered = 0; 333 | int i; 334 | 335 | /* check input parameters */ 336 | CHECK_NULL(spi_target); 337 | if ((address & 0x80) != 0) { 338 | DEBUG_MSG("WARNING: SPI address > 127\n"); 339 | } 340 | CHECK_NULL(data); 341 | if (size == 0) { 342 | DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); 343 | return LGW_SPI_ERROR; 344 | } 345 | 346 | spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ 347 | 348 | /* prepare command byte */ 349 | if (spi_mux_mode == LGW_SPI_MUX_MODE1) { 350 | command[0] = spi_mux_target; 351 | command[1] = READ_ACCESS | (address & 0x7F); 352 | command_size = 2; 353 | } else { 354 | command[0] = READ_ACCESS | (address & 0x7F); 355 | command_size = 1; 356 | } 357 | size_to_do = size; 358 | 359 | /* I/O transaction */ 360 | memset(&k, 0, sizeof(k)); /* clear k */ 361 | k[0].tx_buf = (unsigned long) &command[0]; 362 | k[0].len = command_size; 363 | k[0].cs_change = 0; 364 | k[1].cs_change = 0; 365 | for (i=0; size_to_do > 0; ++i) { 366 | chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK; 367 | offset = i * LGW_BURST_CHUNK; 368 | k[1].rx_buf = (unsigned long)(data + offset); 369 | k[1].len = chunk_size; 370 | byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len ); 371 | DEBUG_PRINTF("BURST READ: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered); 372 | size_to_do -= chunk_size; /* subtract the quantity of data already transferred */ 373 | } 374 | 375 | /* determine return code */ 376 | if (byte_transfered != size) { 377 | DEBUG_MSG("ERROR: SPI BURST READ FAILURE\n"); 378 | return LGW_SPI_ERROR; 379 | } else { 380 | DEBUG_MSG("Note: SPI burst read success\n"); 381 | return LGW_SPI_SUCCESS; 382 | } 383 | } 384 | 385 | /* --- EOF ------------------------------------------------------------------ */ 386 | -------------------------------------------------------------------------------- /libloragw/tst/test_loragw_gps.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Minimum test program for the loragw_gps 'library' 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Michael Coracin 14 | */ 15 | 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | /* --- DEPENDANCIES --------------------------------------------------------- */ 19 | 20 | /* fix an issue between POSIX and C99 */ 21 | #if __STDC_VERSION__ >= 199901L 22 | #define _XOPEN_SOURCE 600 23 | #else 24 | #define _XOPEN_SOURCE 500 25 | #endif 26 | 27 | #include /* C99 types */ 28 | #include /* bool type */ 29 | #include /* printf */ 30 | #include /* memset */ 31 | #include /* sigaction */ 32 | #include /* exit */ 33 | #include /* read */ 34 | 35 | #include "loragw_hal.h" 36 | #include "loragw_gps.h" 37 | #include "loragw_aux.h" 38 | 39 | /* -------------------------------------------------------------------------- */ 40 | /* --- PRIVATE VARIABLES ---------------------------------------------------- */ 41 | 42 | static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ 43 | static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ 44 | 45 | struct tref ppm_ref; 46 | 47 | /* -------------------------------------------------------------------------- */ 48 | /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ 49 | 50 | static void sig_handler(int sigio); 51 | static void gps_process_sync(void); 52 | static void gps_process_coords(void); 53 | 54 | /* -------------------------------------------------------------------------- */ 55 | /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ 56 | 57 | static void sig_handler(int sigio) { 58 | if (sigio == SIGQUIT) { 59 | quit_sig = 1;; 60 | } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { 61 | exit_sig = 1; 62 | } 63 | } 64 | 65 | static void gps_process_sync(void) { 66 | /* variables for PPM pulse GPS synchronization */ 67 | uint32_t ppm_tstamp; 68 | struct timespec ppm_gps; 69 | struct timespec ppm_utc; 70 | 71 | /* variables for timestamp <-> GPS time conversions */ 72 | uint32_t x, z; 73 | struct timespec y; 74 | 75 | /* get GPS time for synchronization */ 76 | int i = lgw_gps_get(&ppm_utc, &ppm_gps, NULL, NULL); 77 | if (i != LGW_GPS_SUCCESS) { 78 | printf(" No valid reference GPS time available, synchronization impossible.\n"); 79 | return; 80 | } 81 | 82 | /* get timestamp for synchronization */ 83 | i = lgw_get_trigcnt(&ppm_tstamp); 84 | if (i != LGW_HAL_SUCCESS) { 85 | printf(" Failed to read timestamp, synchronization impossible.\n"); 86 | return; 87 | } 88 | 89 | /* try to update synchronize time reference with the new GPS & timestamp */ 90 | i = lgw_gps_sync(&ppm_ref, ppm_tstamp, ppm_utc, ppm_gps); 91 | if (i != LGW_GPS_SUCCESS) { 92 | printf(" Synchronization error.\n"); 93 | return; 94 | } 95 | 96 | /* display result */ 97 | printf(" * Synchronization successful *\n"); 98 | printf(" UTC reference time: %lld.%09ld\n", (long long)ppm_ref.utc.tv_sec, ppm_ref.utc.tv_nsec); 99 | printf(" GPS reference time: %lld.%09ld\n", (long long)ppm_ref.gps.tv_sec, ppm_ref.gps.tv_nsec); 100 | printf(" Internal counter reference value: %u\n", ppm_ref.count_us); 101 | printf(" Clock error: %.9f\n", ppm_ref.xtal_err); 102 | 103 | x = ppm_tstamp + 500000; 104 | printf(" * Test of timestamp counter <-> GPS value conversion *\n"); 105 | printf(" Test value: %u\n", x); 106 | lgw_cnt2gps(ppm_ref, x, &y); 107 | printf(" Conversion to GPS: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); 108 | lgw_gps2cnt(ppm_ref, y, &z); 109 | printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); 110 | printf(" * Test of timestamp counter <-> UTC value conversion *\n"); 111 | printf(" Test value: %u\n", x); 112 | lgw_cnt2utc(ppm_ref, x, &y); 113 | printf(" Conversion to UTC: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); 114 | lgw_utc2cnt(ppm_ref, y, &z); 115 | printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); 116 | } 117 | 118 | static void gps_process_coords(void) { 119 | /* position variable */ 120 | struct coord_s coord; 121 | struct coord_s gpserr; 122 | int i = lgw_gps_get(NULL, NULL, &coord, &gpserr); 123 | 124 | /* update gateway coordinates */ 125 | if (i == LGW_GPS_SUCCESS) { 126 | printf("# GPS coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", coord.lat, coord.lon, coord.alt); 127 | printf("# GPS err: latitude %.5f, longitude %.5f, altitude %i m\n", gpserr.lat, gpserr.lon, gpserr.alt); 128 | } 129 | } 130 | 131 | /* -------------------------------------------------------------------------- */ 132 | /* --- MAIN FUNCTION -------------------------------------------------------- */ 133 | 134 | int main() 135 | { 136 | struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ 137 | 138 | int i; 139 | 140 | /* concentrator variables */ 141 | struct lgw_conf_board_s boardconf; 142 | struct lgw_conf_rxrf_s rfconf; 143 | 144 | /* serial variables */ 145 | char serial_buff[128]; /* buffer to receive GPS data */ 146 | size_t wr_idx = 0; /* pointer to end of chars in buffer */ 147 | int gps_tty_dev; /* file descriptor to the serial port of the GNSS module */ 148 | 149 | /* NMEA/UBX variables */ 150 | enum gps_msg latest_msg; /* keep track of latest NMEA/UBX message parsed */ 151 | 152 | /* configure signal handling */ 153 | sigemptyset(&sigact.sa_mask); 154 | sigact.sa_flags = 0; 155 | sigact.sa_handler = sig_handler; 156 | sigaction(SIGQUIT, &sigact, NULL); 157 | sigaction(SIGINT, &sigact, NULL); 158 | sigaction(SIGTERM, &sigact, NULL); 159 | 160 | /* Intro message and library information */ 161 | printf("Beginning of test for loragw_gps.c\n"); 162 | printf("*** Library version information ***\n%s\n***\n", lgw_version_info()); 163 | 164 | /* Open and configure GPS */ 165 | i = lgw_gps_enable("/dev/ttyAMA0", "ubx7", 0, &gps_tty_dev); 166 | if (i != LGW_GPS_SUCCESS) { 167 | printf("ERROR: IMPOSSIBLE TO ENABLE GPS\n"); 168 | exit(EXIT_FAILURE); 169 | } 170 | 171 | /* start concentrator (default conf for IoT SK) */ 172 | /* board config */ 173 | memset(&boardconf, 0, sizeof(boardconf)); 174 | boardconf.lorawan_public = true; 175 | boardconf.clksrc = 1; 176 | lgw_board_setconf(boardconf); 177 | 178 | /* RF config */ 179 | memset(&rfconf, 0, sizeof(rfconf)); 180 | rfconf.enable = true; 181 | rfconf.freq_hz = 868000000; 182 | rfconf.rssi_offset = 0.0; 183 | rfconf.type = LGW_RADIO_TYPE_SX1257; 184 | rfconf.tx_enable = true; 185 | lgw_rxrf_setconf(0, rfconf); 186 | 187 | lgw_start(); 188 | 189 | /* initialize some variables before loop */ 190 | memset(serial_buff, 0, sizeof serial_buff); 191 | memset(&ppm_ref, 0, sizeof ppm_ref); 192 | 193 | /* loop until user action */ 194 | while ((quit_sig != 1) && (exit_sig != 1)) { 195 | size_t rd_idx = 0; 196 | size_t frame_end_idx = 0; 197 | 198 | /* blocking non-canonical read on serial port */ 199 | ssize_t nb_char = read(gps_tty_dev, serial_buff + wr_idx, LGW_GPS_MIN_MSG_SIZE); 200 | if (nb_char <= 0) { 201 | printf("WARNING: [gps] read() returned value %d\n", nb_char); 202 | continue; 203 | } 204 | wr_idx += (size_t)nb_char; 205 | 206 | /******************************************* 207 | * Scan buffer for UBX/NMEA sync chars and * 208 | * attempt to decode frame if one is found * 209 | *******************************************/ 210 | while (rd_idx < wr_idx) { 211 | size_t frame_size = 0; 212 | 213 | /* Scan buffer for UBX sync char */ 214 | if (serial_buff[rd_idx] == LGW_GPS_UBX_SYNC_CHAR) { 215 | 216 | /*********************** 217 | * Found UBX sync char * 218 | ***********************/ 219 | latest_msg = lgw_parse_ubx(&serial_buff[rd_idx], (wr_idx - rd_idx), &frame_size); 220 | 221 | if (frame_size > 0) { 222 | if (latest_msg == INCOMPLETE) { 223 | /* UBX header found but frame appears to be missing bytes */ 224 | frame_size = 0; 225 | } else if (latest_msg == INVALID) { 226 | /* message header received but message appears to be corrupted */ 227 | printf("WARNING: [gps] could not get a valid message from GPS (no time)\n"); 228 | frame_size = 0; 229 | } else if (latest_msg == UBX_NAV_TIMEGPS) { 230 | printf("\n~~ UBX NAV-TIMEGPS sentence, triggering synchronization attempt ~~\n"); 231 | gps_process_sync(); 232 | } 233 | } 234 | } else if(serial_buff[rd_idx] == LGW_GPS_NMEA_SYNC_CHAR) { 235 | /************************ 236 | * Found NMEA sync char * 237 | ************************/ 238 | /* scan for NMEA end marker (LF = 0x0a) */ 239 | char* nmea_end_ptr = memchr(&serial_buff[rd_idx],(int)0x0a, (wr_idx - rd_idx)); 240 | 241 | if (nmea_end_ptr) { 242 | /* found end marker */ 243 | frame_size = nmea_end_ptr - &serial_buff[rd_idx] + 1; 244 | latest_msg = lgw_parse_nmea(&serial_buff[rd_idx], frame_size); 245 | 246 | if(latest_msg == INVALID || latest_msg == UNKNOWN) { 247 | /* checksum failed */ 248 | frame_size = 0; 249 | } else if (latest_msg == NMEA_RMC) { /* Get location from RMC frames */ 250 | gps_process_coords(); 251 | } 252 | } 253 | } 254 | 255 | if (frame_size > 0) { 256 | /* At this point message is a checksum verified frame 257 | we're processed or ignored. Remove frame from buffer */ 258 | rd_idx += frame_size; 259 | frame_end_idx = rd_idx; 260 | } else { 261 | rd_idx++; 262 | } 263 | } /* ...for(rd_idx = 0... */ 264 | 265 | if (frame_end_idx) { 266 | /* Frames have been processed. Remove bytes to end of last processed frame */ 267 | memcpy(serial_buff,&serial_buff[frame_end_idx],wr_idx - frame_end_idx); 268 | wr_idx -= frame_end_idx; 269 | } /* ...for(rd_idx = 0... */ 270 | 271 | /* Prevent buffer overflow */ 272 | if ((sizeof(serial_buff) - wr_idx) < LGW_GPS_MIN_MSG_SIZE) { 273 | memcpy(serial_buff,&serial_buff[LGW_GPS_MIN_MSG_SIZE],wr_idx - LGW_GPS_MIN_MSG_SIZE); 274 | wr_idx -= LGW_GPS_MIN_MSG_SIZE; 275 | } 276 | } 277 | 278 | /* clean up before leaving */ 279 | if (exit_sig == 1) { 280 | lgw_gps_disable(gps_tty_dev); 281 | lgw_stop(); 282 | } 283 | 284 | printf("\nEnd of test for loragw_gps.c\n"); 285 | exit(EXIT_SUCCESS); 286 | } 287 | 288 | /* --- EOF ------------------------------------------------------------------ */ 289 | -------------------------------------------------------------------------------- /libloragw/tst/test_loragw_hal.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Minimum test program for the loragw_hal 'library' 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Sylvain Miermont 14 | */ 15 | 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | /* --- DEPENDANCIES --------------------------------------------------------- */ 19 | 20 | /* fix an issue between POSIX and C99 */ 21 | #if __STDC_VERSION__ >= 199901L 22 | #define _XOPEN_SOURCE 600 23 | #else 24 | #define _XOPEN_SOURCE 500 25 | #endif 26 | 27 | #include /* C99 types */ 28 | #include /* bool type */ 29 | #include /* printf */ 30 | #include /* memset */ 31 | #include /* sigaction */ 32 | #include /* getopt access */ 33 | 34 | #include "loragw_hal.h" 35 | #include "loragw_reg.h" 36 | #include "loragw_aux.h" 37 | 38 | /* -------------------------------------------------------------------------- */ 39 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 40 | 41 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 42 | 43 | /* -------------------------------------------------------------------------- */ 44 | /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ 45 | 46 | #define DEFAULT_RSSI_OFFSET 0.0 47 | #define DEFAULT_NOTCH_FREQ 129000U 48 | 49 | /* -------------------------------------------------------------------------- */ 50 | /* --- PRIVATE VARIABLES ---------------------------------------------------- */ 51 | 52 | static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ 53 | static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ 54 | 55 | /* -------------------------------------------------------------------------- */ 56 | /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ 57 | 58 | static void sig_handler(int sigio); 59 | 60 | /* -------------------------------------------------------------------------- */ 61 | /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ 62 | 63 | static void sig_handler(int sigio) { 64 | if (sigio == SIGQUIT) { 65 | quit_sig = 1;; 66 | } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { 67 | exit_sig = 1; 68 | } 69 | } 70 | 71 | /* describe command line options */ 72 | void usage(void) { 73 | printf("Library version information: %s\n", lgw_version_info()); 74 | printf( "Available options:\n"); 75 | printf( " -h print this help\n"); 76 | printf( " -a Radio A RX frequency in MHz\n"); 77 | printf( " -b Radio B RX frequency in MHz\n"); 78 | printf( " -t Radio TX frequency in MHz\n"); 79 | printf( " -r Radio type (SX1255:1255, SX1257:1257)\n"); 80 | printf( " -k Concentrator clock source (0: radio_A, 1: radio_B(default))\n"); 81 | } 82 | 83 | /* -------------------------------------------------------------------------- */ 84 | /* --- MAIN FUNCTION -------------------------------------------------------- */ 85 | 86 | int main(int argc, char **argv) 87 | { 88 | struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ 89 | 90 | struct lgw_conf_board_s boardconf; 91 | struct lgw_conf_rxrf_s rfconf; 92 | struct lgw_conf_rxif_s ifconf; 93 | 94 | struct lgw_pkt_rx_s rxpkt[4]; /* array containing up to 4 inbound packets metadata */ 95 | struct lgw_pkt_tx_s txpkt; /* configuration and metadata for an outbound packet */ 96 | struct lgw_pkt_rx_s *p; /* pointer on a RX packet */ 97 | 98 | int i, j; 99 | int nb_pkt; 100 | uint32_t fa = 0, fb = 0, ft = 0; 101 | enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; 102 | uint8_t clocksource = 1; /* Radio B is source by default */ 103 | 104 | uint32_t tx_cnt = 0; 105 | unsigned long loop_cnt = 0; 106 | uint8_t status_var = 0; 107 | double xd = 0.0; 108 | int xi = 0; 109 | 110 | /* parse command line options */ 111 | while ((i = getopt (argc, argv, "ha:b:t:r:k:")) != -1) { 112 | switch (i) { 113 | case 'h': 114 | usage(); 115 | return -1; 116 | break; 117 | case 'a': /* Radio A RX frequency in MHz */ 118 | sscanf(optarg, "%lf", &xd); 119 | fa = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ 120 | break; 121 | case 'b': /* Radio B RX frequency in MHz */ 122 | sscanf(optarg, "%lf", &xd); 123 | fb = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ 124 | break; 125 | case 't': /* Radio TX frequency in MHz */ 126 | sscanf(optarg, "%lf", &xd); 127 | ft = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ 128 | break; 129 | case 'r': /* Radio type (1255, 1257) */ 130 | sscanf(optarg, "%i", &xi); 131 | switch (xi) { 132 | case 1255: 133 | radio_type = LGW_RADIO_TYPE_SX1255; 134 | break; 135 | case 1257: 136 | radio_type = LGW_RADIO_TYPE_SX1257; 137 | break; 138 | default: 139 | printf("ERROR: invalid radio type\n"); 140 | usage(); 141 | return -1; 142 | } 143 | break; 144 | case 'k': /* Concentrator clock source (Radio A or Radio B) */ 145 | sscanf(optarg, "%i", &xi); 146 | clocksource = (uint8_t)xi; 147 | break; 148 | default: 149 | printf("ERROR: argument parsing\n"); 150 | usage(); 151 | return -1; 152 | } 153 | } 154 | 155 | /* check input parameters */ 156 | if ((fa == 0) || (fb == 0) || (ft == 0)) { 157 | printf("ERROR: missing frequency input parameter:\n"); 158 | printf(" Radio A RX: %u\n", fa); 159 | printf(" Radio B RX: %u\n", fb); 160 | printf(" Radio TX: %u\n", ft); 161 | usage(); 162 | return -1; 163 | } 164 | 165 | if (radio_type == LGW_RADIO_TYPE_NONE) { 166 | printf("ERROR: missing radio type parameter:\n"); 167 | usage(); 168 | return -1; 169 | } 170 | 171 | /* configure signal handling */ 172 | sigemptyset(&sigact.sa_mask); 173 | sigact.sa_flags = 0; 174 | sigact.sa_handler = sig_handler; 175 | sigaction(SIGQUIT, &sigact, NULL); 176 | sigaction(SIGINT, &sigact, NULL); 177 | sigaction(SIGTERM, &sigact, NULL); 178 | 179 | /* beginning of LoRa concentrator-specific code */ 180 | printf("Beginning of test for loragw_hal.c\n"); 181 | 182 | printf("*** Library version information ***\n%s\n\n", lgw_version_info()); 183 | 184 | /* set configuration for board */ 185 | memset(&boardconf, 0, sizeof(boardconf)); 186 | 187 | boardconf.lorawan_public = true; 188 | boardconf.clksrc = clocksource; 189 | lgw_board_setconf(boardconf); 190 | 191 | /* set configuration for RF chains */ 192 | memset(&rfconf, 0, sizeof(rfconf)); 193 | 194 | rfconf.enable = true; 195 | rfconf.freq_hz = fa; 196 | rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; 197 | rfconf.type = radio_type; 198 | rfconf.tx_enable = true; 199 | rfconf.tx_notch_freq = DEFAULT_NOTCH_FREQ; 200 | lgw_rxrf_setconf(0, rfconf); /* radio A, f0 */ 201 | 202 | rfconf.enable = true; 203 | rfconf.freq_hz = fb; 204 | rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; 205 | rfconf.type = radio_type; 206 | rfconf.tx_enable = false; 207 | lgw_rxrf_setconf(1, rfconf); /* radio B, f1 */ 208 | 209 | /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */ 210 | memset(&ifconf, 0, sizeof(ifconf)); 211 | 212 | ifconf.enable = true; 213 | ifconf.rf_chain = 1; 214 | ifconf.freq_hz = -400000; 215 | ifconf.datarate = DR_LORA_MULTI; 216 | lgw_rxif_setconf(0, ifconf); /* chain 0: LoRa 125kHz, all SF, on f1 - 0.4 MHz */ 217 | 218 | ifconf.enable = true; 219 | ifconf.rf_chain = 1; 220 | ifconf.freq_hz = -200000; 221 | ifconf.datarate = DR_LORA_MULTI; 222 | lgw_rxif_setconf(1, ifconf); /* chain 1: LoRa 125kHz, all SF, on f1 - 0.2 MHz */ 223 | 224 | ifconf.enable = true; 225 | ifconf.rf_chain = 1; 226 | ifconf.freq_hz = 0; 227 | ifconf.datarate = DR_LORA_MULTI; 228 | lgw_rxif_setconf(2, ifconf); /* chain 2: LoRa 125kHz, all SF, on f1 - 0.0 MHz */ 229 | 230 | ifconf.enable = true; 231 | ifconf.rf_chain = 0; 232 | ifconf.freq_hz = -400000; 233 | ifconf.datarate = DR_LORA_MULTI; 234 | lgw_rxif_setconf(3, ifconf); /* chain 3: LoRa 125kHz, all SF, on f0 - 0.4 MHz */ 235 | 236 | ifconf.enable = true; 237 | ifconf.rf_chain = 0; 238 | ifconf.freq_hz = -200000; 239 | ifconf.datarate = DR_LORA_MULTI; 240 | lgw_rxif_setconf(4, ifconf); /* chain 4: LoRa 125kHz, all SF, on f0 - 0.2 MHz */ 241 | 242 | ifconf.enable = true; 243 | ifconf.rf_chain = 0; 244 | ifconf.freq_hz = 0; 245 | ifconf.datarate = DR_LORA_MULTI; 246 | lgw_rxif_setconf(5, ifconf); /* chain 5: LoRa 125kHz, all SF, on f0 + 0.0 MHz */ 247 | 248 | ifconf.enable = true; 249 | ifconf.rf_chain = 0; 250 | ifconf.freq_hz = 200000; 251 | ifconf.datarate = DR_LORA_MULTI; 252 | lgw_rxif_setconf(6, ifconf); /* chain 6: LoRa 125kHz, all SF, on f0 + 0.2 MHz */ 253 | 254 | ifconf.enable = true; 255 | ifconf.rf_chain = 0; 256 | ifconf.freq_hz = 400000; 257 | ifconf.datarate = DR_LORA_MULTI; 258 | lgw_rxif_setconf(7, ifconf); /* chain 7: LoRa 125kHz, all SF, on f0 + 0.4 MHz */ 259 | 260 | /* set configuration for LoRa 'stand alone' channel */ 261 | memset(&ifconf, 0, sizeof(ifconf)); 262 | ifconf.enable = true; 263 | ifconf.rf_chain = 0; 264 | ifconf.freq_hz = 0; 265 | ifconf.bandwidth = BW_250KHZ; 266 | ifconf.datarate = DR_LORA_SF10; 267 | lgw_rxif_setconf(8, ifconf); /* chain 8: LoRa 250kHz, SF10, on f0 MHz */ 268 | 269 | /* set configuration for FSK channel */ 270 | memset(&ifconf, 0, sizeof(ifconf)); 271 | ifconf.enable = true; 272 | ifconf.rf_chain = 1; 273 | ifconf.freq_hz = 0; 274 | ifconf.bandwidth = BW_250KHZ; 275 | ifconf.datarate = 64000; 276 | lgw_rxif_setconf(9, ifconf); /* chain 9: FSK 64kbps, on f1 MHz */ 277 | 278 | /* set configuration for TX packet */ 279 | memset(&txpkt, 0, sizeof(txpkt)); 280 | txpkt.freq_hz = ft; 281 | txpkt.tx_mode = IMMEDIATE; 282 | txpkt.rf_power = 10; 283 | txpkt.modulation = MOD_LORA; 284 | txpkt.bandwidth = BW_125KHZ; 285 | txpkt.datarate = DR_LORA_SF9; 286 | txpkt.coderate = CR_LORA_4_5; 287 | strcpy((char *)txpkt.payload, "TX.TEST.LORA.GW.????" ); 288 | txpkt.size = 20; 289 | txpkt.preamble = 6; 290 | txpkt.rf_chain = 0; 291 | /* 292 | memset(&txpkt, 0, sizeof(txpkt)); 293 | txpkt.freq_hz = F_TX; 294 | txpkt.tx_mode = IMMEDIATE; 295 | txpkt.rf_power = 10; 296 | txpkt.modulation = MOD_FSK; 297 | txpkt.f_dev = 50; 298 | txpkt.datarate = 64000; 299 | strcpy((char *)txpkt.payload, "TX.TEST.LORA.GW.????" ); 300 | txpkt.size = 20; 301 | txpkt.preamble = 4; 302 | txpkt.rf_chain = 0; 303 | */ 304 | 305 | /* connect, configure and start the LoRa concentrator */ 306 | i = lgw_start(); 307 | if (i == LGW_HAL_SUCCESS) { 308 | printf("*** Concentrator started ***\n"); 309 | } else { 310 | printf("*** Impossible to start concentrator ***\n"); 311 | return -1; 312 | } 313 | 314 | /* once configured, dump content of registers to a file, for reference */ 315 | // FILE * reg_dump = NULL; 316 | // reg_dump = fopen("reg_dump.log", "w"); 317 | // if (reg_dump != NULL) { 318 | // lgw_reg_check(reg_dump); 319 | // fclose(reg_dump); 320 | // } 321 | 322 | while ((quit_sig != 1) && (exit_sig != 1)) { 323 | loop_cnt++; 324 | 325 | /* fetch N packets */ 326 | nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt); 327 | 328 | if (nb_pkt == 0) { 329 | wait_ms(300); 330 | } else { 331 | /* display received packets */ 332 | for(i=0; i < nb_pkt; ++i) { 333 | p = &rxpkt[i]; 334 | printf("---\nRcv pkt #%d >>", i+1); 335 | if (p->status == STAT_CRC_OK) { 336 | printf(" if_chain:%2d", p->if_chain); 337 | printf(" tstamp:%010u", p->count_us); 338 | printf(" size:%3u", p->size); 339 | switch (p-> modulation) { 340 | case MOD_LORA: printf(" LoRa"); break; 341 | case MOD_FSK: printf(" FSK"); break; 342 | default: printf(" modulation?"); 343 | } 344 | switch (p->datarate) { 345 | case DR_LORA_SF7: printf(" SF7"); break; 346 | case DR_LORA_SF8: printf(" SF8"); break; 347 | case DR_LORA_SF9: printf(" SF9"); break; 348 | case DR_LORA_SF10: printf(" SF10"); break; 349 | case DR_LORA_SF11: printf(" SF11"); break; 350 | case DR_LORA_SF12: printf(" SF12"); break; 351 | default: printf(" datarate?"); 352 | } 353 | switch (p->coderate) { 354 | case CR_LORA_4_5: printf(" CR1(4/5)"); break; 355 | case CR_LORA_4_6: printf(" CR2(2/3)"); break; 356 | case CR_LORA_4_7: printf(" CR3(4/7)"); break; 357 | case CR_LORA_4_8: printf(" CR4(1/2)"); break; 358 | default: printf(" coderate?"); 359 | } 360 | printf("\n"); 361 | printf(" RSSI:%+6.1f SNR:%+5.1f (min:%+5.1f, max:%+5.1f) payload:\n", p->rssi, p->snr, p->snr_min, p->snr_max); 362 | 363 | for (j = 0; j < p->size; ++j) { 364 | printf(" %02X", p->payload[j]); 365 | } 366 | printf(" #\n"); 367 | } else if (p->status == STAT_CRC_BAD) { 368 | printf(" if_chain:%2d", p->if_chain); 369 | printf(" tstamp:%010u", p->count_us); 370 | printf(" size:%3u\n", p->size); 371 | printf(" CRC error, damaged packet\n\n"); 372 | } else if (p->status == STAT_NO_CRC){ 373 | printf(" if_chain:%2d", p->if_chain); 374 | printf(" tstamp:%010u", p->count_us); 375 | printf(" size:%3u\n", p->size); 376 | printf(" no CRC\n\n"); 377 | } else { 378 | printf(" if_chain:%2d", p->if_chain); 379 | printf(" tstamp:%010u", p->count_us); 380 | printf(" size:%3u\n", p->size); 381 | printf(" invalid status ?!?\n\n"); 382 | } 383 | } 384 | } 385 | 386 | /* send a packet every X loop */ 387 | if (loop_cnt%16 == 0) { 388 | /* 32b counter in the payload, big endian */ 389 | txpkt.payload[16] = 0xff & (tx_cnt >> 24); 390 | txpkt.payload[17] = 0xff & (tx_cnt >> 16); 391 | txpkt.payload[18] = 0xff & (tx_cnt >> 8); 392 | txpkt.payload[19] = 0xff & tx_cnt; 393 | i = lgw_send(txpkt); /* non-blocking scheduling of TX packet */ 394 | j = 0; 395 | printf("+++\nSending packet #%d, rf path %d, return %d\nstatus -> ", tx_cnt, txpkt.rf_chain, i); 396 | do { 397 | ++j; 398 | wait_ms(100); 399 | lgw_status(TX_STATUS, &status_var); /* get TX status */ 400 | printf("%d:", status_var); 401 | } while ((status_var != TX_FREE) && (j < 100)); 402 | ++tx_cnt; 403 | printf("\nTX finished\n"); 404 | } 405 | } 406 | 407 | if (exit_sig == 1) { 408 | /* clean up before leaving */ 409 | lgw_stop(); 410 | } 411 | 412 | printf("\nEnd of test for loragw_hal.c\n"); 413 | return 0; 414 | } 415 | 416 | /* --- EOF ------------------------------------------------------------------ */ 417 | -------------------------------------------------------------------------------- /libloragw/tst/test_loragw_reg.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Minimum test program for the loragw_spi 'library' 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Sylvain Miermont 14 | */ 15 | 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | /* --- DEPENDANCIES --------------------------------------------------------- */ 19 | 20 | #include 21 | #include 22 | 23 | #include "loragw_reg.h" 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | /* --- MAIN FUNCTION -------------------------------------------------------- */ 27 | 28 | #define BURST_TEST_LENGTH 8192 29 | 30 | int main() 31 | { 32 | int32_t read_value, test_value; 33 | uint16_t lfsr; 34 | uint8_t burst_buffout[BURST_TEST_LENGTH]; 35 | uint8_t burst_buffin[BURST_TEST_LENGTH]; 36 | int i; 37 | 38 | printf("Beginning of test for loragw_reg.c\n"); 39 | 40 | lgw_connect(false, 129E3); 41 | /* 2 SPI transactions: 42 | -> 0x80 0x00 <- 0x00 0x00 forcing page 0 43 | -> 0x01 0x00 <- 0x00 0x64 checking version 44 | */ 45 | 46 | /* --- READ TEST --- */ 47 | 48 | lgw_reg_w(LGW_SOFT_RESET, 1); 49 | lgw_reg_check(stdout); 50 | 51 | /* --- READ/WRITE COHERENCY TEST --- */ 52 | 53 | /* 8b unsigned */ 54 | test_value = 197; /* 11000101b */ 55 | lgw_reg_w(LGW_IMPLICIT_PAYLOAD_LENGHT, test_value); 56 | lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); 57 | printf("IMPLICIT_PAYLOAD_LENGHT = %d (should be %d)\n", read_value, test_value); 58 | 59 | /* 8b signed */ 60 | /* NO SUCH REG AVAILABLE */ 61 | // /* RADIO_SELECT is normally unsigned, modify it manually in loragw_reg.c */ 62 | // test_value = -59; /* 11000101b */ 63 | // lgw_reg_w(LGW_RADIO_SELECT, test_value); 64 | // lgw_reg_r(LGW_RADIO_SELECT, &read_value); 65 | // printf("RADIO_SELECT = %d (should be %d)\n", read_value, test_value); 66 | 67 | /* less than 8b, with offset, unsigned */ 68 | test_value = 11; /* 1011b */ 69 | lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS, test_value); 70 | lgw_reg_r(LGW_FRAME_SYNCH_PEAK2_POS, &read_value); 71 | printf("FRAME_SYNCH_PEAK2_POS = %d (should be %d)\n", read_value, test_value); 72 | 73 | /* less than 8b, with offset, signed */ 74 | /* NO SUCH REG AVAILABLE */ 75 | // /* MBWSSF_FRAME_SYNCH_PEAK2_POS is normally unsigned, modify it manually in loragw_reg.c */ 76 | // test_value = -5; /* 1011b */ 77 | // lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, test_value); 78 | // lgw_reg_r(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, &read_value); 79 | // printf("MBWSSF_FRAME_SYNCH_PEAK2_POS = %d (should be %d)\n", read_value, test_value); 80 | 81 | /* 16b unsigned */ 82 | test_value = 49253; /* 11000000 01100101b */ 83 | lgw_reg_w(LGW_PREAMBLE_SYMB1_NB, test_value); 84 | lgw_reg_r(LGW_PREAMBLE_SYMB1_NB, &read_value); 85 | printf("PREAMBLE_SYMB1_NB = %d (should be %d)\n", read_value, test_value); 86 | 87 | /* 16b signed */ 88 | /* NO SUCH REG AVAILABLE */ 89 | // /* CAPTURE_PERIOD is normally unsigned, modify it manually in loragw_reg.c */ 90 | // test_value = -16283; /* 11000000 01100101b */ 91 | // lgw_reg_w(LGW_CAPTURE_PERIOD, test_value); 92 | // lgw_reg_r(LGW_CAPTURE_PERIOD, &read_value); 93 | // printf("CAPTURE_PERIOD = %d (should be %d)\n", read_value, test_value); 94 | 95 | /* between 8b and 16b, unsigned */ 96 | test_value = 3173; /* 1100 01100101b */ 97 | lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, test_value); 98 | lgw_reg_r(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, &read_value); 99 | printf("ADJUST_MODEM_START_OFFSET_SF12_RDX4 = %d (should be %d)\n", read_value, test_value); 100 | 101 | /* between 8b and 16b, signed */ 102 | test_value = -1947; /* 11000 01100101b */ 103 | lgw_reg_w(LGW_IF_FREQ_1, test_value); 104 | lgw_reg_r(LGW_IF_FREQ_1, &read_value); 105 | printf("IF_FREQ_1 = %d (should be %d)\n", read_value, test_value); 106 | 107 | /* --- BURST WRITE AND READ TEST --- */ 108 | 109 | /* initialize data for SPI test */ 110 | lfsr = 0xFFFF; 111 | for(i=0; i> 4)); 113 | /* printf("%05d # 0x%04x 0x%02x\n", i, lfsr, burst_buffout[i]); */ 114 | lfsr = (lfsr & 1) ? ((lfsr >> 1) ^ 0x8679) : (lfsr >> 1); 115 | } 116 | 117 | lgw_reg_wb(LGW_TX_DATA_BUF_DATA, burst_buffout, 256); 118 | lgw_reg_rb(LGW_RX_DATA_BUF_DATA, burst_buffin, 256); 119 | 120 | /* impossible to check in software, 121 | RX_DATA_BUF_DATA is read-only, 122 | TX_DATA_BUF_DATA is write only, 123 | use a logic analyser */ 124 | 125 | /* --- END OF TEST --- */ 126 | 127 | lgw_disconnect(); 128 | /* no SPI transaction */ 129 | 130 | printf("End of test for loragw_reg.c\n"); 131 | return 0; 132 | } 133 | 134 | /* --- EOF ------------------------------------------------------------------ */ 135 | -------------------------------------------------------------------------------- /libloragw/tst/test_loragw_spi.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Minimum test program for the loragw_spi 'library' 11 | Use logic analyser to check the results. 12 | 13 | License: Revised BSD License, see LICENSE.TXT file include in the project 14 | Maintainer: Sylvain Miermont 15 | */ 16 | 17 | 18 | /* -------------------------------------------------------------------------- */ 19 | /* --- DEPENDANCIES --------------------------------------------------------- */ 20 | 21 | #include 22 | #include 23 | 24 | #include "loragw_spi.h" 25 | 26 | /* -------------------------------------------------------------------------- */ 27 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 28 | 29 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 30 | 31 | /* -------------------------------------------------------------------------- */ 32 | /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ 33 | 34 | #define BURST_TEST_SIZE 2500 /* >> LGW_BURST_CHUNK */ 35 | #define TIMING_REPEAT 1 /* repeat transactions multiple times for timing characterisation */ 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | /* --- MAIN FUNCTION -------------------------------------------------------- */ 39 | 40 | int main() 41 | { 42 | int i; 43 | void *spi_target = NULL; 44 | uint8_t data = 0; 45 | uint8_t dataout[BURST_TEST_SIZE]; 46 | uint8_t datain[BURST_TEST_SIZE]; 47 | uint8_t spi_mux_mode = LGW_SPI_MUX_MODE0; 48 | 49 | for (i = 0; i < BURST_TEST_SIZE; ++i) { 50 | dataout[i] = 0x30 + (i % 10); /* ASCCI code for 0 -> 9 */ 51 | datain[i] = 0x23; /* garbage data, to be overwritten by received data */ 52 | } 53 | 54 | printf("Beginning of test for loragw_spi.c\n"); 55 | lgw_spi_open(&spi_target); 56 | 57 | /* normal R/W test */ 58 | for (i = 0; i < TIMING_REPEAT; ++i) 59 | lgw_spi_w(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0xAA, 0x96); 60 | for (i = 0; i < TIMING_REPEAT; ++i) 61 | lgw_spi_r(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, &data); 62 | 63 | /* burst R/W test, small bursts << LGW_BURST_CHUNK */ 64 | for (i = 0; i < TIMING_REPEAT; ++i) 65 | lgw_spi_wb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, dataout, 16); 66 | for (i = 0; i < TIMING_REPEAT; ++i) 67 | lgw_spi_rb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, datain, 16); 68 | 69 | /* burst R/W test, large bursts >> LGW_BURST_CHUNK */ 70 | for (i = 0; i < TIMING_REPEAT; ++i) 71 | lgw_spi_wb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x5A, dataout, ARRAY_SIZE(dataout)); 72 | for (i = 0; i < TIMING_REPEAT; ++i) 73 | lgw_spi_rb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x5A, datain, ARRAY_SIZE(datain)); 74 | 75 | /* last read (blocking), just to be sure no to quit before the FTDI buffer is flushed */ 76 | lgw_spi_r(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, &data); 77 | printf("data received (simple read): %d\n",data); 78 | 79 | lgw_spi_close(spi_target); 80 | printf("End of test for loragw_spi.c\n"); 81 | 82 | return 0; 83 | } 84 | 85 | /* --- EOF ------------------------------------------------------------------ */ 86 | -------------------------------------------------------------------------------- /reset_lgw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script is intended to be used on IoT Starter Kit platform, it performs 4 | # the following actions: 5 | # - export/unpexort GPIO7 used to reset the SX1301 chip 6 | # 7 | # Usage examples: 8 | # ./reset_lgw.sh stop 9 | # ./reset_lgw.sh start 10 | 11 | # The reset pin of SX1301 is wired with RPi GPIO7 12 | # If used on another platform, the GPIO number can be given as parameter. 13 | if [ -z "$2" ]; then 14 | IOT_SK_SX1301_RESET_PIN=25 15 | else 16 | IOT_SK_SX1301_RESET_PIN=$2 17 | fi 18 | 19 | echo "Accessing concentrator reset pin through GPIO$IOT_SK_SX1301_RESET_PIN..." 20 | 21 | WAIT_GPIO() { 22 | sleep 0.1 23 | } 24 | 25 | iot_sk_init() { 26 | # setup GPIO 7 27 | echo "$IOT_SK_SX1301_RESET_PIN" > /sys/class/gpio/export; WAIT_GPIO 28 | 29 | # set GPIO 7 as output 30 | echo "out" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/direction; WAIT_GPIO 31 | 32 | # write output for SX1301 reset 33 | echo "1" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/value; WAIT_GPIO 34 | echo "0" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/value; WAIT_GPIO 35 | 36 | # set GPIO 7 as input 37 | echo "in" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/direction; WAIT_GPIO 38 | } 39 | 40 | iot_sk_term() { 41 | # cleanup GPIO 7 42 | if [ -d /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN ] 43 | then 44 | echo "$IOT_SK_SX1301_RESET_PIN" > /sys/class/gpio/unexport; WAIT_GPIO 45 | fi 46 | } 47 | 48 | case "$1" in 49 | start) 50 | iot_sk_term 51 | iot_sk_init 52 | ;; 53 | stop) 54 | iot_sk_term 55 | ;; 56 | *) 57 | echo "Usage: $0 {start|stop} []" 58 | exit 1 59 | ;; 60 | esac 61 | 62 | exit 0 63 | -------------------------------------------------------------------------------- /util_lbt_test/Makefile: -------------------------------------------------------------------------------- 1 | ### Application-specific constants 2 | 3 | APP_NAME := util_lbt_test 4 | 5 | ### Environment constants 6 | 7 | LGW_PATH ?= ../libloragw 8 | ARCH ?= 9 | CROSS_COMPILE ?= 10 | 11 | ### External constant definitions 12 | 13 | include $(LGW_PATH)/library.cfg 14 | 15 | ### Constant symbols 16 | 17 | CC := $(CROSS_COMPILE)gcc 18 | AR := $(CROSS_COMPILE)ar 19 | 20 | CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. 21 | 22 | OBJDIR = obj 23 | 24 | ### Constants for LoRa concentrator HAL library 25 | # List the library sub-modules that are used by the application 26 | 27 | LGW_INC = $(LGW_PATH)/inc/config.h 28 | LGW_INC += $(LGW_PATH)/inc/loragw_reg.h 29 | LGW_INC += $(LGW_PATH)/inc/loragw_fpga.h 30 | 31 | ### Linking options 32 | 33 | LIBS := -lloragw -lrt -lm 34 | 35 | ### General build targets 36 | 37 | all: $(APP_NAME) 38 | 39 | clean: 40 | rm -f $(OBJDIR)/*.o 41 | rm -f $(APP_NAME) 42 | 43 | ### HAL library (do no force multiple library rebuild even with 'make -B') 44 | 45 | $(LGW_PATH)/inc/config.h: 46 | @if test ! -f $@; then \ 47 | $(MAKE) all -C $(LGW_PATH); \ 48 | fi 49 | 50 | $(LGW_PATH)/libloragw.a: $(LGW_INC) 51 | @if test ! -f $@; then \ 52 | $(MAKE) all -C $(LGW_PATH); \ 53 | fi 54 | 55 | ### Main program compilation and assembly 56 | $(OBJDIR): 57 | mkdir -p $(OBJDIR) 58 | 59 | $(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) | $(OBJDIR) 60 | $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ 61 | 62 | $(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a 63 | $(CC) -L$(LGW_PATH) $< -o $@ $(LIBS) 64 | 65 | ### EOF 66 | -------------------------------------------------------------------------------- /util_lbt_test/readme.md: -------------------------------------------------------------------------------- 1 | / _____) _ | | 2 | ( (____ _____ ____ _| |_ _____ ____| |__ 3 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 4 | _____) ) ____| | | || |_| ____( (___| | | | 5 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 6 | ©2013 Semtech-Cycleo 7 | 8 | Lora Gateway LBT basic test application 9 | ======================================= 10 | 11 | 1. Introduction 12 | ---------------- 13 | 14 | This software configures the FPGA for "Liste-Before-Talk" feature and 15 | continuously reads the LBT channels timestamps which indicate when was the last 16 | instant when the channel was free. 17 | 18 | 2. Dependencies 19 | ---------------- 20 | 21 | A SX1301AP2 Ref Design board with its FPGA programmed with LBT feature 22 | 23 | 3. Usage 24 | --------- 25 | 26 | Before running the util_lbt_test application, the concentrator MUST be first 27 | started with the HAL, using for example util_pkt_logger or the packet forwarder 28 | with LBT feature disabled, as the util_lbt_test will configure it. 29 | 30 | For a description of the command line options available: 31 | ./util_lbt_test -h 32 | 33 | ex: 34 | ./util_lbt_test -f 867.1 -r -80 -s 5000 35 | 36 | This will set 8 LBT channels, starting from 867.1 MHz, then each subsequent 37 | channel being set to the frequency of the previous channel +200 KHz (867.3, 38 | 867.5, ...). 39 | 40 | The above test will run for ever, with a CHANNEL_SCAN_TIME of 5000µs 41 | and a target RSSI of -80dBm. 42 | 43 | Please refer to the lora_gateway library readme.md to get more details on the 44 | LBT feature implementation and configuration. 45 | 46 | 4. Changelog 47 | ------------- 48 | 49 | 2016-03-03 v1.0 Initial version 50 | 2016-08-31 v1.1 LBT feature rework 51 | -------------------------------------------------------------------------------- /util_lbt_test/src/util_lbt_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | Listen Before Talk basic test application 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Michael Coracin 14 | */ 15 | 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | /* --- DEPENDANCIES --------------------------------------------------------- */ 19 | 20 | /* fix an issue between POSIX and C99 */ 21 | #if __STDC_VERSION__ >= 199901L 22 | #define _XOPEN_SOURCE 600 23 | #else 24 | #define _XOPEN_SOURCE 500 25 | #endif 26 | 27 | #include /* C99 types */ 28 | #include /* bool type */ 29 | #include /* printf fprintf sprintf fopen fputs */ 30 | 31 | #include /* sigaction */ 32 | #include /* getopt access */ 33 | #include /* rand */ 34 | 35 | #include "loragw_aux.h" 36 | #include "loragw_reg.h" 37 | #include "loragw_hal.h" 38 | #include "loragw_radio.h" 39 | #include "loragw_fpga.h" 40 | 41 | /* -------------------------------------------------------------------------- */ 42 | /* --- PRIVATE MACROS & CONSTANTS ------------------------------------------- */ 43 | 44 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 45 | #define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */ 46 | 47 | #define DEFAULT_SX127X_RSSI_OFFSET -1 48 | 49 | /* -------------------------------------------------------------------------- */ 50 | /* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ 51 | 52 | /* signal handling variables */ 53 | struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ 54 | static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ 55 | static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ 56 | 57 | /* -------------------------------------------------------------------------- */ 58 | /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ 59 | 60 | static void sig_handler(int sigio); 61 | 62 | void usage (void); 63 | 64 | /* -------------------------------------------------------------------------- */ 65 | /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ 66 | 67 | static void sig_handler(int sigio) { 68 | if (sigio == SIGQUIT) { 69 | quit_sig = 1;; 70 | } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { 71 | exit_sig = 1; 72 | } 73 | } 74 | 75 | /* describe command line options */ 76 | void usage(void) { 77 | printf("Available options:\n"); 78 | printf(" -h print this help\n"); 79 | printf(" -f frequency in MHz of the first LBT channel\n"); 80 | printf(" -o offset in dB to be applied to the SX127x RSSI [-128..127]\n"); 81 | printf(" -r target RSSI: signal strength target used to detect if the channel is clear or not [-128..0]\n"); 82 | printf(" -s scan time in µs for all 8 LBT channels [128,5000]\n"); 83 | } 84 | 85 | /* -------------------------------------------------------------------------- */ 86 | /* --- MAIN FUNCTION -------------------------------------------------------- */ 87 | 88 | int main(int argc, char **argv) 89 | { 90 | int i; 91 | int xi = 0; 92 | 93 | /* in/out variables */ 94 | double f1 = 0.0; 95 | uint32_t f_init = 0; /* in Hz */ 96 | uint32_t f_start = 0; /* in Hz */ 97 | uint16_t loop_cnt = 0; 98 | int8_t rssi_target_dBm = -80; 99 | uint16_t scan_time_us = 128; 100 | uint32_t timestamp; 101 | uint8_t rssi_value; 102 | int8_t rssi_offset = DEFAULT_SX127X_RSSI_OFFSET; 103 | int32_t val, val2; 104 | int channel; 105 | uint32_t freq_offset; 106 | 107 | /* parse command line options */ 108 | while ((i = getopt (argc, argv, "h:f:s:r:o:")) != -1) { 109 | switch (i) { 110 | case 'h': 111 | usage(); 112 | return EXIT_FAILURE; 113 | break; 114 | 115 | case 'f': 116 | i = sscanf(optarg, "%lf", &f1); 117 | if ((i != 1) || (f1 < 30.0) || (f1 > 3000.0)) { 118 | MSG("ERROR: Invalid LBT start frequency\n"); 119 | usage(); 120 | return EXIT_FAILURE; 121 | } else { 122 | f_start = (uint32_t)((f1*1e6) + 0.5);/* .5 Hz offset to get rounding instead of truncating */ 123 | } 124 | break; 125 | case 's': 126 | i = sscanf(optarg, "%i", &xi); 127 | if ((i != 1) || ((xi != 128) && (xi != 5000))) { 128 | MSG("ERROR: scan_time_us must be 128 or 5000 \n"); 129 | usage(); 130 | return EXIT_FAILURE; 131 | } else { 132 | scan_time_us = xi; 133 | } 134 | break; 135 | case 'r': 136 | i = sscanf(optarg, "%i", &xi); 137 | if ((i != 1) || ((xi < -128) && (xi > 0))) { 138 | MSG("ERROR: rssi_target must be b/w -128 & 0 \n"); 139 | usage(); 140 | return EXIT_FAILURE; 141 | } else { 142 | rssi_target_dBm = xi; 143 | } 144 | break; 145 | case 'o': /* -o SX127x RSSI offset [-128..127] */ 146 | i = sscanf(optarg, "%i", &xi); 147 | if((i != 1) || (xi < -128) || (xi > 127)) { 148 | MSG("ERROR: rssi_offset must be b/w -128 & 127\n"); 149 | usage(); 150 | return EXIT_FAILURE; 151 | } else { 152 | rssi_offset = (int8_t)xi; 153 | } 154 | break; 155 | default: 156 | MSG("ERROR: argument parsing use -h option for help\n"); 157 | usage(); 158 | return EXIT_FAILURE; 159 | } 160 | } 161 | 162 | MSG("INFO: Starting LoRa Gateway v1.5 LBT test\n"); 163 | 164 | /* configure signal handling */ 165 | sigemptyset(&sigact.sa_mask); 166 | sigact.sa_flags = 0; 167 | sigact.sa_handler = sig_handler; 168 | sigaction(SIGQUIT, &sigact, NULL); 169 | sigaction(SIGINT, &sigact, NULL); 170 | sigaction(SIGTERM, &sigact, NULL); 171 | 172 | /* Connect to concentrator */ 173 | i = lgw_connect(false, LGW_DEFAULT_NOTCH_FREQ); 174 | if (i != LGW_REG_SUCCESS) { 175 | MSG("ERROR: lgw_connect() did not return SUCCESS\n"); 176 | return EXIT_FAILURE; 177 | } 178 | 179 | /* Check if FPGA supports LBT */ 180 | lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); 181 | if (TAKE_N_BITS_FROM((uint8_t)val, 2, 1) != true) { 182 | MSG("ERROR: LBT is not supported (0x%x)\n", (uint8_t)val); 183 | return EXIT_FAILURE; 184 | } 185 | 186 | /* Get FPGA lowest frequency for LBT channels */ 187 | lgw_fpga_reg_r(LGW_FPGA_LBT_INITIAL_FREQ, &val); 188 | switch (val) { 189 | case 0: 190 | f_init = 915000000; 191 | break; 192 | case 1: 193 | f_init = 863000000; 194 | break; 195 | default: 196 | MSG("ERROR: LBT start frequency %d is not supported\n", val); 197 | return EXIT_FAILURE; 198 | } 199 | 200 | /* Initialize 1st LBT channel freq if not given by user */ 201 | if (f_start == 0) { 202 | f_start = f_init; 203 | } else if (f_start < f_init) { 204 | MSG("ERROR: LBT start frequency %u is not supported (f_init=%u)\n", f_start, f_init); 205 | return EXIT_FAILURE; 206 | } 207 | MSG("FREQ: %u\n", f_start); 208 | 209 | /* Configure SX127x and read few RSSI points */ 210 | lgw_setup_sx127x(f_init, MOD_FSK, LGW_SX127X_RXBW_100K_HZ, rssi_offset); /* 200KHz LBT channels */ 211 | for (i = 0; i < 100; i++) { 212 | lgw_sx127x_reg_r(0x11, &rssi_value); /* 0x11: RegRssiValue */ 213 | MSG("SX127x RSSI:%i dBm\n", -(rssi_value/2)); 214 | wait_ms(10); 215 | } 216 | 217 | /* Configure LBT */ 218 | val = -2*(rssi_target_dBm); 219 | lgw_fpga_reg_w(LGW_FPGA_RSSI_TARGET, val); 220 | for (i = 0; i < LBT_CHANNEL_FREQ_NB; i++) { 221 | freq_offset = (f_start - f_init)/100E3 + i*2; /* 200KHz between each channel */ 222 | lgw_fpga_reg_w(LGW_FPGA_LBT_CH0_FREQ_OFFSET+i, (int32_t)freq_offset); 223 | if (scan_time_us == 5000) { /* configured to 128 by default */ 224 | lgw_fpga_reg_w(LGW_FPGA_LBT_SCAN_TIME_CH0+i, 1); 225 | } 226 | } 227 | 228 | lgw_fpga_reg_r(LGW_FPGA_RSSI_TARGET, &val); 229 | MSG("RSSI_TARGET = %d\n", val); 230 | if (val != (-2*rssi_target_dBm)) { 231 | MSG("ERROR: failed to read back RSSI target register value\n"); 232 | return EXIT_FAILURE; 233 | } 234 | for (i = 0; i < LBT_CHANNEL_FREQ_NB; i++) { 235 | lgw_fpga_reg_r(LGW_FPGA_LBT_CH0_FREQ_OFFSET+i, &val); 236 | lgw_fpga_reg_r(LGW_FPGA_LBT_SCAN_TIME_CH0+i, &val2); 237 | MSG("CH_%i: freq=%u (offset=%i), scan_time=%u (%i)\n", i, (uint32_t)((val*100E3)+f_init), val, (val2==1)?5000:128, val2); 238 | } 239 | lgw_fpga_reg_r(LGW_FPGA_VERSION, &val); 240 | MSG("FPGA VERSION = %d\n", val); 241 | 242 | /* Enable LBT FSM */ 243 | lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); 244 | 245 | /* Start test */ 246 | while ((quit_sig != 1) && (exit_sig != 1)) { 247 | MSG("~~~~\n"); 248 | for (channel = 0; channel < LBT_CHANNEL_FREQ_NB; channel++) { 249 | /* Select LBT channel */ 250 | lgw_fpga_reg_w(LGW_FPGA_LBT_TIMESTAMP_SELECT_CH, channel); 251 | 252 | /* Get last instant when the selected channel was free */ 253 | lgw_fpga_reg_r(LGW_FPGA_LBT_TIMESTAMP_CH, &val); 254 | timestamp = (uint32_t)(val & 0x0000FFFF) * 256; /* 16bits (1LSB = 256µs) */ 255 | MSG(" TIMESTAMP_CH%u = %u\n", channel, timestamp); 256 | } 257 | 258 | loop_cnt += 1; 259 | wait_ms(400); 260 | } 261 | 262 | /* close SPI link */ 263 | i = lgw_disconnect(); 264 | if (i != LGW_REG_SUCCESS) { 265 | MSG("ERROR: lgw_disconnect() did not return SUCCESS\n"); 266 | return EXIT_FAILURE; 267 | } 268 | 269 | MSG("INFO: Exiting LoRa Gateway v1.5 LBT test successfully\n"); 270 | return EXIT_SUCCESS; 271 | } 272 | 273 | /* --- EOF ------------------------------------------------------------------ */ 274 | 275 | -------------------------------------------------------------------------------- /util_pkt_logger/Makefile: -------------------------------------------------------------------------------- 1 | ### Application-specific constants 2 | 3 | APP_NAME := util_pkt_logger 4 | 5 | ### Environment constants 6 | 7 | LGW_PATH ?= ../libloragw 8 | ARCH ?= 9 | CROSS_COMPILE ?= 10 | 11 | ### External constant definitions 12 | 13 | include $(LGW_PATH)/library.cfg 14 | 15 | ### Constant symbols 16 | 17 | CC := $(CROSS_COMPILE)gcc 18 | AR := $(CROSS_COMPILE)ar 19 | 20 | CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. 21 | 22 | OBJDIR = obj 23 | 24 | ### Constants for LoRa concentrator HAL library 25 | # List the library sub-modules that are used by the application 26 | 27 | LGW_INC = $(LGW_PATH)/inc/config.h 28 | LGW_INC += $(LGW_PATH)/inc/loragw_hal.h 29 | 30 | ### Linking options 31 | 32 | LIBS := -lloragw -lrt -lm 33 | 34 | ### General build targets 35 | 36 | all: $(APP_NAME) 37 | 38 | clean: 39 | rm -f $(OBJDIR)/*.o 40 | rm -f $(APP_NAME) 41 | 42 | ### HAL library (do no force multiple library rebuild even with 'make -B') 43 | 44 | $(LGW_PATH)/inc/config.h: 45 | @if test ! -f $@; then \ 46 | $(MAKE) all -C $(LGW_PATH); \ 47 | fi 48 | 49 | $(LGW_PATH)/libloragw.a: $(LGW_INC) 50 | @if test ! -f $@; then \ 51 | $(MAKE) all -C $(LGW_PATH); \ 52 | fi 53 | 54 | ### Sub-modules compilation 55 | 56 | $(OBJDIR): 57 | mkdir -p $(OBJDIR) 58 | 59 | $(OBJDIR)/parson.o: src/parson.c inc/parson.h | $(OBJDIR) 60 | $(CC) -c $(CFLAGS) $< -o $@ 61 | 62 | ### Main program compilation and assembly 63 | 64 | $(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) inc/parson.h | $(OBJDIR) 65 | $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ 66 | 67 | $(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a $(OBJDIR)/parson.o 68 | $(CC) -L$(LGW_PATH) $< $(OBJDIR)/parson.o -o $@ $(LIBS) 69 | 70 | ### EOF 71 | -------------------------------------------------------------------------------- /util_pkt_logger/global_conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "SX1301_conf": { 3 | "lorawan_public": true, 4 | "clksrc": 1, /* radio_1 provides clock to concentrator */ 5 | "radio_0": { 6 | "enable": true, 7 | "type": "SX1257", 8 | "freq": 915000000, 9 | "rssi_offset": -166.0, 10 | "tx_enable": false 11 | }, 12 | "radio_1": { 13 | "enable": true, 14 | "type": "SX1257", 15 | "freq": 916500000, 16 | "rssi_offset": -166.0, 17 | "tx_enable": false 18 | }, 19 | "chan_multiSF_0": { 20 | /* Lora MAC channel, 125kHz, all SF, 868.1 MHz */ 21 | "enable": true, 22 | "radio": 1, 23 | "if": -400000 24 | }, 25 | "chan_multiSF_1": { 26 | /* Lora MAC channel, 125kHz, all SF, 868.3 MHz */ 27 | "enable": true, 28 | "radio": 1, 29 | "if": -200000 30 | }, 31 | "chan_multiSF_2": { 32 | /* Lora MAC channel, 125kHz, all SF, 868.5 MHz */ 33 | "enable": true, 34 | "radio": 1, 35 | "if": 0 36 | }, 37 | "chan_multiSF_3": { 38 | /* Lora MAC channel, 125kHz, all SF, 867.1 MHz */ 39 | "enable": true, 40 | "radio": 0, 41 | "if": -400000 42 | }, 43 | "chan_multiSF_4": { 44 | /* Lora MAC channel, 125kHz, all SF, 867.3 MHz */ 45 | "enable": true, 46 | "radio": 0, 47 | "if": -200000 48 | }, 49 | "chan_multiSF_5": { 50 | /* Lora MAC channel, 125kHz, all SF, 867.5 MHz */ 51 | "enable": true, 52 | "radio": 0, 53 | "if": 0 54 | }, 55 | "chan_multiSF_6": { 56 | /* Lora MAC channel, 125kHz, all SF, 867.7 MHz */ 57 | "enable": true, 58 | "radio": 0, 59 | "if": 200000 60 | }, 61 | "chan_multiSF_7": { 62 | /* Lora MAC channel, 125kHz, all SF, 867.9 MHz */ 63 | "enable": true, 64 | "radio": 0, 65 | "if": 400000 66 | }, 67 | "chan_Lora_std": { 68 | /* Lora MAC channel, 250kHz, SF7, 868.3 MHz */ 69 | "enable": true, 70 | "radio": 1, 71 | "if": -200000, 72 | "bandwidth": 250000, 73 | "spread_factor": 7 74 | }, 75 | "chan_FSK": { 76 | /* FSK 50kbps channel, 868.8 MHz */ 77 | "enable": true, 78 | "radio": 1, 79 | "if": 300000, 80 | "bandwidth": 125000, 81 | "datarate": 50000 82 | } 83 | }, 84 | "gateway_conf": { 85 | "gateway_ID": "AA555A0000000000" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /util_pkt_logger/inc/parson.h: -------------------------------------------------------------------------------- 1 | /* 2 | Parson ( http://kgabis.github.com/parson/ ) 3 | Copyright (c) 2012 - 2016 Krzysztof Gabis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #ifndef parson_parson_h 25 | #define parson_parson_h 26 | 27 | #ifdef __cplusplus 28 | extern "C" 29 | { 30 | #endif 31 | 32 | #include /* size_t */ 33 | 34 | /* Types and enums */ 35 | typedef struct json_object_t JSON_Object; 36 | typedef struct json_array_t JSON_Array; 37 | typedef struct json_value_t JSON_Value; 38 | 39 | enum json_value_type { 40 | JSONError = -1, 41 | JSONNull = 1, 42 | JSONString = 2, 43 | JSONNumber = 3, 44 | JSONObject = 4, 45 | JSONArray = 5, 46 | JSONBoolean = 6 47 | }; 48 | typedef int JSON_Value_Type; 49 | 50 | enum json_result_t { 51 | JSONSuccess = 0, 52 | JSONFailure = -1 53 | }; 54 | typedef int JSON_Status; 55 | 56 | typedef void * (*JSON_Malloc_Function)(size_t); 57 | typedef void (*JSON_Free_Function)(void *); 58 | 59 | /* Call only once, before calling any other function from parson API. If not called, malloc and free 60 | from stdlib will be used for all allocations */ 61 | void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); 62 | 63 | /* Parses first JSON value in a file, returns NULL in case of error */ 64 | JSON_Value * json_parse_file(const char *filename); 65 | 66 | /* Parses first JSON value in a file and ignores comments (/ * * / and //), 67 | returns NULL in case of error */ 68 | JSON_Value * json_parse_file_with_comments(const char *filename); 69 | 70 | /* Parses first JSON value in a string, returns NULL in case of error */ 71 | JSON_Value * json_parse_string(const char *string); 72 | 73 | /* Parses first JSON value in a string and ignores comments (/ * * / and //), 74 | returns NULL in case of error */ 75 | JSON_Value * json_parse_string_with_comments(const char *string); 76 | 77 | /* Serialization */ 78 | size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ 79 | JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); 80 | JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); 81 | char * json_serialize_to_string(const JSON_Value *value); 82 | 83 | /* Pretty serialization */ 84 | size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ 85 | JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); 86 | JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); 87 | char * json_serialize_to_string_pretty(const JSON_Value *value); 88 | 89 | void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ 90 | 91 | /* Comparing */ 92 | int json_value_equals(const JSON_Value *a, const JSON_Value *b); 93 | 94 | /* Validation 95 | This is *NOT* JSON Schema. It validates json by checking if object have identically 96 | named fields with matching types. 97 | For example schema {"name":"", "age":0} will validate 98 | {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, 99 | but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. 100 | In case of arrays, only first value in schema is checked against all values in tested array. 101 | Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, 102 | null validates values of every type. 103 | */ 104 | JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); 105 | 106 | /* 107 | * JSON Object 108 | */ 109 | JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); 110 | const char * json_object_get_string (const JSON_Object *object, const char *name); 111 | JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); 112 | JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); 113 | double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ 114 | int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ 115 | 116 | /* dotget functions enable addressing values with dot notation in nested objects, 117 | just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). 118 | Because valid names in JSON can contain dots, some values may be inaccessible 119 | this way. */ 120 | JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); 121 | const char * json_object_dotget_string (const JSON_Object *object, const char *name); 122 | JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); 123 | JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); 124 | double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ 125 | int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ 126 | 127 | /* Functions to get available names */ 128 | size_t json_object_get_count(const JSON_Object *object); 129 | const char * json_object_get_name (const JSON_Object *object, size_t index); 130 | 131 | /* Creates new name-value pair or frees and replaces old value with a new one. 132 | * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ 133 | JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); 134 | JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); 135 | JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); 136 | JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); 137 | JSON_Status json_object_set_null(JSON_Object *object, const char *name); 138 | 139 | /* Works like dotget functions, but creates whole hierarchy if necessary. 140 | * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ 141 | JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); 142 | JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); 143 | JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); 144 | JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); 145 | JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); 146 | 147 | /* Frees and removes name-value pair */ 148 | JSON_Status json_object_remove(JSON_Object *object, const char *name); 149 | 150 | /* Works like dotget function, but removes name-value pair only on exact match. */ 151 | JSON_Status json_object_dotremove(JSON_Object *object, const char *key); 152 | 153 | /* Removes all name-value pairs in object */ 154 | JSON_Status json_object_clear(JSON_Object *object); 155 | 156 | /* 157 | *JSON Array 158 | */ 159 | JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); 160 | const char * json_array_get_string (const JSON_Array *array, size_t index); 161 | JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); 162 | JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); 163 | double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */ 164 | int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ 165 | size_t json_array_get_count (const JSON_Array *array); 166 | 167 | /* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. 168 | * Order of values in array may change during execution. */ 169 | JSON_Status json_array_remove(JSON_Array *array, size_t i); 170 | 171 | /* Frees and removes from array value at given index and replaces it with given one. 172 | * Does nothing and returns JSONFailure if index doesn't exist. 173 | * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ 174 | JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); 175 | JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); 176 | JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); 177 | JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); 178 | JSON_Status json_array_replace_null(JSON_Array *array, size_t i); 179 | 180 | /* Frees and removes all values from array */ 181 | JSON_Status json_array_clear(JSON_Array *array); 182 | 183 | /* Appends new value at the end of array. 184 | * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ 185 | JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); 186 | JSON_Status json_array_append_string(JSON_Array *array, const char *string); 187 | JSON_Status json_array_append_number(JSON_Array *array, double number); 188 | JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); 189 | JSON_Status json_array_append_null(JSON_Array *array); 190 | 191 | /* 192 | *JSON Value 193 | */ 194 | JSON_Value * json_value_init_object (void); 195 | JSON_Value * json_value_init_array (void); 196 | JSON_Value * json_value_init_string (const char *string); /* copies passed string */ 197 | JSON_Value * json_value_init_number (double number); 198 | JSON_Value * json_value_init_boolean(int boolean); 199 | JSON_Value * json_value_init_null (void); 200 | JSON_Value * json_value_deep_copy (const JSON_Value *value); 201 | void json_value_free (JSON_Value *value); 202 | 203 | JSON_Value_Type json_value_get_type (const JSON_Value *value); 204 | JSON_Object * json_value_get_object (const JSON_Value *value); 205 | JSON_Array * json_value_get_array (const JSON_Value *value); 206 | const char * json_value_get_string (const JSON_Value *value); 207 | double json_value_get_number (const JSON_Value *value); 208 | int json_value_get_boolean(const JSON_Value *value); 209 | 210 | /* Same as above, but shorter */ 211 | JSON_Value_Type json_type (const JSON_Value *value); 212 | JSON_Object * json_object (const JSON_Value *value); 213 | JSON_Array * json_array (const JSON_Value *value); 214 | const char * json_string (const JSON_Value *value); 215 | double json_number (const JSON_Value *value); 216 | int json_boolean(const JSON_Value *value); 217 | 218 | #ifdef __cplusplus 219 | } 220 | #endif 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /util_pkt_logger/local_conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "gateway_conf": { 3 | "gateway_ID": "AA555A0000000101" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /util_pkt_logger/readme.md: -------------------------------------------------------------------------------- 1 | / _____) _ | | 2 | ( (____ _____ ____ _| |_ _____ ____| |__ 3 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 4 | _____) ) ____| | | || |_| ____( (___| | | | 5 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 6 | (C)2013 Semtech-Cycleo 7 | 8 | LoRa packet logger 9 | =================== 10 | 11 | 1. Introduction 12 | ---------------- 13 | 14 | This software is used to set up a LoRa concentrator using a JSON configuration 15 | file and then record all the packets received in a log file, indefinitely, until 16 | the user stops the application. 17 | No filtering is done and all packets that are LoRa packets with the correct RF 18 | parameters (frequency, datarate, bandwidth) should appear in the log. 19 | 20 | 2. Dependencies 21 | ---------------- 22 | 23 | This program uses the Parson library (http://kgabis.github.com/parson/) by 24 | Krzysztof Gabis for JSON parsing. 25 | Many thanks to him for that very practical and well written library. 26 | 27 | This program is a typical example of LoRa concentrator HAL usage for receiving 28 | packets. 29 | 30 | Only high-level functions are used (the ones contained in loragw_hal) so there 31 | is no hardware dependencies assuming the HAL is matched with the proper version 32 | of the hardware. 33 | Data structures of the received packets are accessed by name (ie. not at a 34 | binary level) so new functionalities can be added to the API without affecting 35 | that program at all. 36 | 37 | It was tested with v1.3.0 of the libloragw library, and should be compatible 38 | with any later version of the library assuming the API is downward-compatible. 39 | 40 | 3. Usage 41 | --------- 42 | 43 | To stop the application, press Ctrl+C. 44 | 45 | The only optional parameter when launching the application is the log rotation 46 | time (in seconds). 47 | 48 | The way the program takes configuration files into account is the following: 49 | * if there is a debug_conf.json parse it, others are ignored 50 | * if there is a global_conf.json parse it and look for the next file 51 | * if there is a local_conf.json parse it 52 | If some parameters are defined in both global and local configuration files, the 53 | local definition overwrites the global definition. 54 | 55 | The global configuration file should be exactly the same throughout your 56 | network, contain all global parameters (parameters for "sensor" radio channels) 57 | and preferably default "safe" values for parameters that are specific for each 58 | gateway (eg. specify a default MAC address). 59 | 60 | If you have build the libloragw library for a specific radio band (eg. ETSI 61 | 868 MHz band) a ready-to-use global_conf.json file is generated by the Makefile 62 | with a set of channels typical for a 'LoRa MAC' network application. 63 | If you don't specify a radio band, an empty global_conf.json is generated and 64 | must be filled with the settings you need. 65 | 66 | The local configuration file should contain parameters that are specific to each 67 | gateway (eg. MAC address, frequency for backhaul radio channels). 68 | 69 | In each configuration file, the program looks for a JSON object named 70 | "SX1301_conf" that should contain the parameters for the LoRa concentrator board 71 | (RF channels definition, modem parameters, etc) and another JSON object called 72 | "gateway_conf" that should contain the gateway parameters (gateway MAC address, 73 | IP address of the LoRa MAC controller, network authentication parameters, etc). 74 | 75 | To learn more about the JSON configuration format, read the provided JSON files 76 | and the API documentation. A dedicated document will be available later on. 77 | 78 | The received packets are put in a CSV file whose name include the MAC address of 79 | the gateway in hexadecimal format and a UTC timestamp of log starting time in 80 | ISO 8601 recommended compact format: 81 | yyyymmddThhmmssZ (eg. 20131009T172345Z for October 9th, 2013 at 5:23:45PM UTC) 82 | 83 | To able continuous monitoring, the current log file is closed is closed and a 84 | new one is opened every hour (by default, rotation interval is settable by the 85 | user using -r command line option). 86 | No packet is lost during that rotation of log file. 87 | Every log file but the current one can then be modified, uploaded and/or deleted 88 | without any consequence for the program execution. 89 | 90 | 4. License 91 | ----------- 92 | 93 | Copyright (c) 2013, SEMTECH S.A. 94 | All rights reserved. 95 | 96 | Redistribution and use in source and binary forms, with or without 97 | modification, are permitted provided that the following conditions are met: 98 | 99 | * Redistributions of source code must retain the above copyright 100 | notice, this list of conditions and the following disclaimer. 101 | * Redistributions in binary form must reproduce the above copyright 102 | notice, this list of conditions and the following disclaimer in the 103 | documentation and/or other materials provided with the distribution. 104 | * Neither the name of the Semtech corporation nor the 105 | names of its contributors may be used to endorse or promote products 106 | derived from this software without specific prior written permission. 107 | 108 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 109 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 110 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 111 | DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY 112 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 113 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 114 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 115 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 116 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 117 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 118 | 119 | 5. License for Parson library 120 | ------------------------------ 121 | 122 | Parson ( http://kgabis.github.com/parson/ ) 123 | Copyright (c) 2012 Krzysztof Gabis 124 | 125 | Permission is hereby granted, free of charge, to any person obtaining a copy 126 | of this software and associated documentation files (the "Software"), to deal 127 | in the Software without restriction, including without limitation the rights 128 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 129 | copies of the Software, and to permit persons to whom the Software is 130 | furnished to do so, subject to the following conditions: 131 | 132 | The above copyright notice and this permission notice shall be included in 133 | all copies or substantial portions of the Software. 134 | 135 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 136 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 137 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 138 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 139 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 140 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 141 | THE SOFTWARE. 142 | 143 | *EOF* -------------------------------------------------------------------------------- /util_spectral_scan/Makefile: -------------------------------------------------------------------------------- 1 | ### Environment constants 2 | 3 | LGW_PATH ?= ../libloragw 4 | ARCH ?= 5 | CROSS_COMPILE ?= 6 | 7 | ### External constant definitions 8 | 9 | include $(LGW_PATH)/library.cfg 10 | 11 | ### Constant symbols 12 | 13 | CC = $(CROSS_COMPILE)gcc 14 | AR = $(CROSS_COMPILE)ar 15 | CFLAGS = -O2 -Wall -Wextra -std=c99 -I inc 16 | 17 | OBJDIR = obj 18 | INCLUDES = $(wildcard inc/*.h) 19 | 20 | ### Constants for LoRa concentrator HAL library 21 | # List the library sub-modules that are used by the application 22 | 23 | LGW_INC = $(LGW_PATH)/inc/config.h 24 | LGW_INC += $(LGW_PATH)/inc/loragw_hal.h 25 | 26 | ### Linking options 27 | 28 | LIBS := -lloragw -lrt 29 | 30 | ### General build targets 31 | 32 | all: util_spectral_scan 33 | 34 | clean: 35 | rm -f $(OBJDIR)/*.o 36 | rm -f util_spectral_scan 37 | 38 | ### HAL library (do no force multiple library rebuild even with 'make -B') 39 | 40 | $(LGW_PATH)/inc/config.h: 41 | @if test ! -f $@; then \ 42 | $(MAKE) all -C $(LGW_PATH); \ 43 | fi 44 | 45 | $(LGW_PATH)/libloragw.a: $(LGW_INC) 46 | @if test ! -f $@; then \ 47 | $(MAKE) all -C $(LGW_PATH); \ 48 | fi 49 | 50 | ### Sub-modules compilation 51 | 52 | $(OBJDIR): 53 | mkdir -p $(OBJDIR) 54 | 55 | $(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR) 56 | $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ 57 | 58 | ### Main program assembly 59 | 60 | util_spectral_scan: $(OBJDIR)/util_spectral_scan.o 61 | $(CC) -L$(LGW_PATH) $^ $(LIBS) -o $@ 62 | 63 | ### EOF 64 | -------------------------------------------------------------------------------- /util_spectral_scan/readme.md: -------------------------------------------------------------------------------- 1 | ______ _ 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2014 Semtech-Cycleo 8 | 9 | Background Spectral Scan for LoRa gateway 10 | ========================================= 11 | 12 | 13 | 1. Introduction 14 | ---------------- 15 | 16 | This software is used to scan the spectral band where the LoRa gateway operates. 17 | It simply computes a RSSI histogram on several frequencies, that will help to 18 | detect occupied bands and get interferer profiles. 19 | It logs the histogram in a .csv file. 20 | 21 | This utility program is meant to run on the LoRa gateway reference design 22 | SX1301AP2 (with FPGA and additionnal SX127x). 23 | 24 | The background RSSI scan is a diagnostic tool and must be run on top of the 25 | gateway activity. Moreover the two SX1257 radios have to be configured in RX 26 | mode to optimize the matching impedance with SX127x. The 32MHz clock provided 27 | to the SX127x is available once SX1301 has enabled the two SX1257 radios, so 28 | the background RSSI scan must be launched after the packet forwarder. 29 | 30 | Note: if the FPGA running the spectral scan also supports Listen-Before-Talk 31 | feature (LBT), the LBT feature has to be enabled and running before launching 32 | util_spectral_scan. For example, if the lora_pkt_fwd runs in background, it has 33 | to use a global_conf.json file with "lbt_cfg.enable" set to true. 34 | 35 | 2. Command line options 36 | ------------------------ 37 | 38 | `-h` 39 | will display a short help 40 | 41 | `-f start:step:stop` 42 | Frequency vector to scan in MHz: start:step:stop 43 | Valid range: start > 800, step > 0.005, stop < 1000 44 | 45 | `-b` 46 | Channel bandwidth to be used to configure the SX1272x radio for scanning in KHz 47 | Valid values: [25,50,100,125,200,250,500] 48 | 49 | `-n` 50 | Total number of RSSI points. 51 | Valid range: [1..65535] 52 | 53 | `-l` 54 | Log file name 55 | 56 | Note: For FPGA image that provides LBT support, the spectral scan gets less 57 | flexible. The following parameters have constraints: 58 | - Frequency step: has to be multiple of 100KHz 59 | - Channel bandwidth: hardcoded to 200KHz 60 | - Number of RSSI points: 16641 61 | 62 | 3. Usage 63 | --------- 64 | 65 | The format of the log file is the following: 66 | Freq_1, RSSI_1, histo_1, ...., RSSI_128, histo_128 67 | Freq_2, RSSI_1, histo_1, ...., RSSI_128, histo_128 68 | ... 69 | 70 | RSSI_n is the nth value of RSSI in dBm 71 | 72 | Default setup: 73 | - freq 863 : 0.2 : 870 74 | - 65535 RSSI points in total at 32kHz rate 75 | - 125KHz channel bandwidth 76 | 77 | Example with frequencies from 865 MHz to 870 MHz, by step of 100KHz, BW 200KHz 78 | 10000 RSSI points processed at 10kHz rate, saved in "log.csv": 79 | ./util_spectral_scan -f 865:0.1:870 -n 10000 -b 200 -l "log" 80 | -------------------------------------------------------------------------------- /util_spi_stress/Makefile: -------------------------------------------------------------------------------- 1 | ### Application-specific constants 2 | 3 | APP_NAME := util_spi_stress 4 | 5 | ### Environment constants 6 | 7 | LGW_PATH ?= ../libloragw 8 | ARCH ?= 9 | CROSS_COMPILE ?= 10 | 11 | ### External constant definitions 12 | # must get library build option to know if mpsse must be linked or not 13 | 14 | include $(LGW_PATH)/library.cfg 15 | 16 | ### Constant symbols 17 | 18 | CC := $(CROSS_COMPILE)gcc 19 | AR := $(CROSS_COMPILE)ar 20 | 21 | CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. 22 | 23 | OBJDIR = obj 24 | 25 | ### Constants for LoRa concentrator HAL library 26 | # List the library sub-modules that are used by the application 27 | 28 | LGW_INC = $(LGW_PATH)/inc/config.h 29 | LGW_INC += $(LGW_PATH)/inc/loragw_reg.h 30 | 31 | ### Linking options 32 | 33 | LIBS := -lloragw -lrt -lm 34 | 35 | ### General build targets 36 | 37 | all: $(APP_NAME) 38 | 39 | clean: 40 | rm -f $(OBJDIR)/*.o 41 | rm -f $(APP_NAME) 42 | 43 | ### HAL library (do no force multiple library rebuild even with 'make -B') 44 | 45 | $(LGW_PATH)/inc/config.h: 46 | @if test ! -f $@; then \ 47 | $(MAKE) all -C $(LGW_PATH); \ 48 | fi 49 | 50 | $(LGW_PATH)/libloragw.a: $(LGW_INC) 51 | @if test ! -f $@; then \ 52 | $(MAKE) all -C $(LGW_PATH); \ 53 | fi 54 | 55 | ### Main program compilation and assembly 56 | 57 | $(OBJDIR): 58 | mkdir -p $(OBJDIR) 59 | 60 | $(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) | $(OBJDIR) 61 | $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ 62 | 63 | $(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a 64 | $(CC) -L$(LGW_PATH) $< -o $@ $(LIBS) 65 | 66 | ### EOF 67 | -------------------------------------------------------------------------------- /util_spi_stress/readme.md: -------------------------------------------------------------------------------- 1 | / _____) _ | | 2 | ( (____ _____ ____ _| |_ _____ ____| |__ 3 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 4 | _____) ) ____| | | || |_| ____( (___| | | | 5 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 6 | (C)2013 Semtech-Cycleo 7 | 8 | LoRa concentrator SPI stress test 9 | ================================== 10 | 11 | 1. Introduction 12 | ---------------- 13 | 14 | This software is used to check the reliability of the link between the host 15 | platform (on which the program is run) and the LoRa concentrator register file 16 | that is the interface through which all interaction with the LoRa concentrator 17 | happens. 18 | 19 | 2. Dependencies 20 | ---------------- 21 | 22 | This program only access the LoRa concentrator HAL library through its 23 | loragw_reg "named registers" access sub-module. 24 | 25 | It was tested with v1.3.0 of the libloragw library, and should be compatible 26 | with any later version of the library and the hardware, assuming the registers 27 | used for the tests are still present. 28 | 29 | The registers used are: 30 | * LGW_VERSION 31 | * LGW_IMPLICIT_PAYLOAD_LENGHT 32 | * LGW_FSK_REF_PATTERN_LSB 33 | * LGW_RX_DATA_BUF_ADDR 34 | * LGW_RX_DATA_BUF_DATA 35 | 36 | A data buffer accessible through the 2 registers above must be implemented. 37 | 38 | 3. Usage 39 | --------- 40 | 41 | The tests run forever or until an error is detected. 42 | Press Ctrl+C to stop the application. 43 | 44 | When an error is detected, diagnosis information are displayed. Please refer to 45 | the source code for more details on what is displayed for diagnosis. 46 | 47 | All tests use pseudo-random data generated by the rand() function. The random 48 | generator is not seeded, and the same sequence of data will be use each time the 49 | program is launched. 50 | 51 | Basically, some random data is written, read back and then compared to the 52 | initial written data. Some "useless" read on others registers might be inserted 53 | to be sure that the data read back is coming from the hardware, and not from the 54 | internal buffer(s) of the software driver(s). 55 | 56 | Test 1 > R/W on a simple 8-bit register 57 | 58 | Test 2 > R/W on a simple 8-bit register with interstitial reads on VERSION 59 | 60 | Test 3 > R/W on a 32-bit register (short SPI bursts access) 61 | 62 | Test 4 > data buffer R/W (long SPI bursts access) 63 | 64 | 4. License 65 | ----------- 66 | 67 | Copyright (c) 2013, SEMTECH S.A. 68 | All rights reserved. 69 | 70 | Redistribution and use in source and binary forms, with or without 71 | modification, are permitted provided that the following conditions are met: 72 | 73 | * Redistributions of source code must retain the above copyright 74 | notice, this list of conditions and the following disclaimer. 75 | * Redistributions in binary form must reproduce the above copyright 76 | notice, this list of conditions and the following disclaimer in the 77 | documentation and/or other materials provided with the distribution. 78 | * Neither the name of the Semtech corporation nor the 79 | names of its contributors may be used to endorse or promote products 80 | derived from this software without specific prior written permission. 81 | 82 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 83 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 84 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 85 | DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY 86 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 87 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 88 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 89 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 90 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 91 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 92 | 93 | *EOF* -------------------------------------------------------------------------------- /util_spi_stress/src/util_spi_stress.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | SPI stress test 11 | 12 | License: Revised BSD License, see LICENSE.TXT file include in the project 13 | Maintainer: Sylvain Miermont 14 | */ 15 | 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | /* --- DEPENDANCIES --------------------------------------------------------- */ 19 | 20 | /* fix an issue between POSIX and C99 */ 21 | #if __STDC_VERSION__ >= 199901L 22 | #define _XOPEN_SOURCE 600 23 | #else 24 | #define _XOPEN_SOURCE 500 25 | #endif 26 | 27 | #include /* C99 types */ 28 | #include /* bool type */ 29 | #include /* printf fprintf sprintf fopen fputs */ 30 | 31 | #include /* sigaction */ 32 | #include /* getopt access */ 33 | #include /* rand */ 34 | 35 | #include "loragw_reg.h" 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | /* --- PRIVATE MACROS ------------------------------------------------------- */ 39 | 40 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 41 | #define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */ 42 | 43 | /* -------------------------------------------------------------------------- */ 44 | /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ 45 | 46 | #define VERS 103 47 | #define READS_WHEN_ERROR 16 /* number of times a read is repeated if there is a read error */ 48 | #define BUFF_SIZE 1024 /* maximum number of bytes that we can write in sx1301 RX data buffer */ 49 | #define DEFAULT_TX_NOTCH_FREQ 129E3 50 | 51 | /* -------------------------------------------------------------------------- */ 52 | /* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ 53 | 54 | /* signal handling variables */ 55 | struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ 56 | static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ 57 | static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ 58 | 59 | /* -------------------------------------------------------------------------- */ 60 | /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ 61 | 62 | static void sig_handler(int sigio); 63 | 64 | void usage (void); 65 | 66 | /* -------------------------------------------------------------------------- */ 67 | /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ 68 | 69 | static void sig_handler(int sigio) { 70 | if (sigio == SIGQUIT) { 71 | quit_sig = 1;; 72 | } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { 73 | exit_sig = 1; 74 | } 75 | } 76 | 77 | /* describe command line options */ 78 | void usage(void) { 79 | MSG( "Available options:\n"); 80 | MSG( " -h print this help\n"); 81 | MSG( " -t specify which test you want to run (1-4)\n"); 82 | } 83 | 84 | /* -------------------------------------------------------------------------- */ 85 | /* --- MAIN FUNCTION -------------------------------------------------------- */ 86 | 87 | int main(int argc, char **argv) 88 | { 89 | int i; 90 | int xi = 0; 91 | 92 | /* application option */ 93 | int test_number = 1; 94 | int cycle_number = 0; 95 | int repeats_per_cycle = 1000; 96 | bool error = false; 97 | 98 | /* in/out variables */ 99 | int32_t test_value; 100 | int32_t read_value; 101 | int32_t rb1, rb2, rb3; /* interstitial readbacks, to flush buffers if needed */ 102 | 103 | /* data buffer */ 104 | int32_t test_addr; 105 | uint8_t test_buff[BUFF_SIZE]; 106 | uint8_t read_buff[BUFF_SIZE]; 107 | 108 | /* parse command line options */ 109 | while ((i = getopt (argc, argv, "ht:")) != -1) { 110 | switch (i) { 111 | case 'h': 112 | usage(); 113 | return EXIT_FAILURE; 114 | break; 115 | 116 | case 't': 117 | i = sscanf(optarg, "%i", &xi); 118 | if ((i != 1) || (xi < 1) || (xi > 4)) { 119 | MSG("ERROR: invalid test number\n"); 120 | return EXIT_FAILURE; 121 | } else { 122 | test_number = xi; 123 | } 124 | break; 125 | 126 | default: 127 | MSG("ERROR: argument parsing use -h option for help\n"); 128 | usage(); 129 | return EXIT_FAILURE; 130 | } 131 | } 132 | MSG("INFO: Starting LoRa concentrator SPI stress-test number %i\n", test_number); 133 | 134 | /* configure signal handling */ 135 | sigemptyset(&sigact.sa_mask); 136 | sigact.sa_flags = 0; 137 | sigact.sa_handler = sig_handler; 138 | sigaction(SIGQUIT, &sigact, NULL); 139 | sigaction(SIGINT, &sigact, NULL); 140 | sigaction(SIGTERM, &sigact, NULL); 141 | 142 | /* start SPI link */ 143 | i = lgw_connect(false, DEFAULT_TX_NOTCH_FREQ); 144 | if (i != LGW_REG_SUCCESS) { 145 | MSG("ERROR: lgw_connect() did not return SUCCESS"); 146 | return EXIT_FAILURE; 147 | } 148 | 149 | if (test_number == 1) { 150 | /* single 8b register R/W stress test */ 151 | while ((quit_sig != 1) && (exit_sig != 1)) { 152 | printf("Cycle %i > ", cycle_number); 153 | for (i=0; i ", cycle_number); 180 | for (i=0; i ", cycle_number); 210 | for (i=0; i ", cycle_number); 241 | test_addr = rand() & 0xFFFF; 242 | lgw_reg_w(LGW_RX_DATA_BUF_ADDR, test_addr); /* write at random offset in memory */ 243 | lgw_reg_wb(LGW_RX_DATA_BUF_DATA, test_buff, BUFF_SIZE); 244 | lgw_reg_w(LGW_RX_DATA_BUF_ADDR, test_addr); /* go back to start of segment */ 245 | lgw_reg_rb(LGW_RX_DATA_BUF_DATA, read_buff, BUFF_SIZE); 246 | for (i=0; ((i