├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── 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 ├── CMakeLists.txt ├── config.h.in ├── inc │ ├── cursor │ │ ├── cursor.h │ │ └── packing.h │ └── loragw │ │ ├── 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 ├── readme.md ├── src │ ├── agc_fw.var │ ├── arb_fw.var │ ├── cal_fw.var │ ├── cursor.c │ ├── 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 │ └── packing.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 ├── CMakeLists.txt ├── readme.md └── src │ └── util_lbt_test.c ├── util_pkt_logger ├── CMakeLists.txt ├── global_conf.json ├── inc │ └── parson.h ├── local_conf.json ├── readme.md └── src │ ├── parson.c │ └── util_pkt_logger.c ├── util_spectral_scan ├── CMakeLists.txt ├── readme.md └── src │ └── util_spectral_scan.c ├── util_spi_stress ├── CMakeLists.txt ├── readme.md └── src │ └── util_spi_stress.c ├── util_tx_continuous ├── CMakeLists.txt ├── readme.md └── src │ └── util_tx_continuous.c └── util_tx_test ├── CMakeLists.txt ├── readme.md └── src └── util_tx_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | *.bak 4 | build 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Project Setup # 3 | ########################################################################## 4 | cmake_minimum_required(VERSION 3.0.2) 5 | project(LoRaGatewayHAL 6 | VERSION 5.0.1 7 | LANGUAGES C 8 | ) 9 | set(CMAKE_C_STANDARD 99) 10 | # Generate `compile_commands.json` in build directory 11 | # Can be used with static analyzers and code-completion tools 12 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 13 | include(CTest) 14 | 15 | ########################################################################## 16 | # Static Analysis # 17 | ########################################################################## 18 | find_program(CPPCHECK NAMES cppcheck) 19 | if(CPPCHECK) 20 | message(STATUS "Found cppcheck: ${CPPCHECK}") 21 | set(CMAKE_C_CPPCHECK ${CPPCHECK} 22 | --enable=warning 23 | --inline-suppr 24 | --quiet 25 | --std=c${CMAKE_C_STANDARD} 26 | --suppress=missingIncludeSystem 27 | --suppress=unusedFunction 28 | --template=gcc 29 | ) 30 | endif() 31 | 32 | ########################################################################## 33 | # Local # 34 | ########################################################################## 35 | add_subdirectory(libloragw) 36 | add_subdirectory(util_lbt_test) 37 | add_subdirectory(util_pkt_logger) 38 | add_subdirectory(util_spectral_scan) 39 | add_subdirectory(util_spi_stress) 40 | add_subdirectory(util_tx_continuous) 41 | add_subdirectory(util_tx_test) 42 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(loragw 2 | src/cursor.c 3 | src/loragw_aux.c 4 | src/loragw_fpga.c 5 | src/loragw_gps.c 6 | src/loragw_hal.c 7 | src/loragw_lbt.c 8 | src/loragw_radio.c 9 | src/loragw_reg.c 10 | src/loragw_spi.native.c 11 | src/packing.c 12 | ) 13 | 14 | target_include_directories(loragw 15 | PUBLIC 16 | $ 17 | $ 18 | $ 19 | PRIVATE 20 | $ 21 | ) 22 | 23 | target_link_libraries(loragw 24 | PRIVATE m 25 | ) 26 | 27 | add_library(LoraGW::loragw ALIAS loragw) 28 | 29 | ########################################################################## 30 | # Logging Options # 31 | ########################################################################## 32 | option(DEBUG_AUX "Enable AUX logging") 33 | option(DEBUG_SPI "Enable SPI logging") 34 | option(DEBUG_REG "Enable REG logging") 35 | option(DEBUG_HAL "Enable HAL logging") 36 | option(DEBUG_LBT "Enable LBT logging") 37 | option(DEBUG_GPS "Enable GPS logging") 38 | 39 | ########################################################################## 40 | # Tests # 41 | ########################################################################## 42 | if(${BUILD_TESTING}) 43 | # Tests are not run automatically (`make test`) due to the fact they 44 | # are still use hard-coded devices and paths that may not make sense. 45 | add_executable(test_loragw_cal tst/test_loragw_cal.c) 46 | target_link_libraries(test_loragw_cal PRIVATE LoraGW::loragw) 47 | 48 | add_executable(test_loragw_gps tst/test_loragw_gps.c) 49 | target_link_libraries(test_loragw_gps PRIVATE LoraGW::loragw) 50 | 51 | add_executable(test_loragw_hal tst/test_loragw_hal.c) 52 | target_link_libraries(test_loragw_hal PRIVATE LoraGW::loragw) 53 | 54 | add_executable(test_loragw_reg tst/test_loragw_reg.c) 55 | target_link_libraries(test_loragw_reg PRIVATE LoraGW::loragw) 56 | 57 | add_executable(test_loragw_spi tst/test_loragw_spi.c) 58 | target_link_libraries(test_loragw_spi PRIVATE LoraGW::loragw) 59 | endif(${BUILD_TESTING}) 60 | 61 | ########################################################################## 62 | # Generate `config.h` # 63 | ########################################################################## 64 | set(CONFIG_H_IN ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in) 65 | set(CONFIG_H_OUT ${CMAKE_CURRENT_BINARY_DIR}/config.h) 66 | configure_file(${CONFIG_H_IN} ${CONFIG_H_OUT} @ONLY) 67 | 68 | ########################################################################## 69 | # Install # 70 | ########################################################################## 71 | include(GNUInstallDirs) 72 | 73 | install(TARGETS loragw 74 | EXPORT LoraGWConfig 75 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 76 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 77 | ) 78 | 79 | export(TARGETS loragw 80 | NAMESPACE LoraGW:: 81 | FILE "${CMAKE_CURRENT_BINARY_DIR}/LoraGWConfig.cmake" 82 | ) 83 | 84 | install(EXPORT LoraGWConfig 85 | DESTINATION ${CMAKE_INSTALL_DATADIR}/LoraGW/cmake 86 | NAMESPACE LoraGW:: 87 | ) 88 | 89 | install(DIRECTORY inc/loragw DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 90 | install(FILES ${CONFIG_H_OUT} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/loragw/) 91 | -------------------------------------------------------------------------------- /libloragw/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_FF5C0CDF 2 | #define CONFIG_H_FF5C0CDF 3 | 4 | #cmakedefine PROJECT_VERSION_MAJOR "@PROJECT_VERSION_MAJOR@" 5 | #cmakedefine PROJECT_VERSION_MINOR "@PROJECT_VERSION_MINOR@" 6 | #cmakedefine PROJECT_VERSION_PATCH "@PROJECT_VERSION_PATCH@" 7 | 8 | #ifndef PROJECT_VERSION_MAJOR 9 | #define PROJECT_VERSION_MAJOR "0" 10 | #endif 11 | 12 | #ifndef PROJECT_VERSION_MINOR 13 | #define PROJECT_VERSION_MINOR "0" 14 | #endif 15 | 16 | #ifndef PROJECT_VERSION_PATCH 17 | #define PROJECT_VERSION_PATCH "0" 18 | #endif 19 | 20 | #define LIBLORAGW_VERSION PROJECT_VERSION_MAJOR "." PROJECT_VERSION_MINOR "." PROJECT_VERSION_PATCH 21 | #cmakedefine01 DEBUG_AUX 22 | #cmakedefine01 DEBUG_SPI 23 | #cmakedefine01 DEBUG_REG 24 | #cmakedefine01 DEBUG_HAL 25 | #cmakedefine01 DEBUG_LBT 26 | #cmakedefine01 DEBUG_GPS 27 | 28 | #endif /* CONFIG_H_FF5C0CDF */ 29 | -------------------------------------------------------------------------------- /libloragw/inc/cursor/cursor.h: -------------------------------------------------------------------------------- 1 | #ifndef CURSOR_H 2 | #define CURSOR_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* A context for [de]serializing data from a buffer */ 12 | struct cursor { 13 | uint8_t * buf; 14 | size_t len; 15 | size_t pos; 16 | }; 17 | 18 | enum cursor_res { 19 | /* The unpack/pack operation was successful */ 20 | cursor_res_ok, 21 | /* No more bytes left in the buffer */ 22 | cursor_res_err_buf_exhausted, 23 | }; 24 | 25 | /** 26 | * Creates and returns a new cursor object 27 | */ 28 | struct cursor 29 | cursor_new(void * buf, size_t buflen); 30 | 31 | size_t 32 | cursor_remaining(struct cursor const * csr); 33 | 34 | enum cursor_res 35 | cursor_take(struct cursor * csr, size_t n, uint8_t * dst); 36 | 37 | size_t 38 | cursor_take_remaining(struct cursor * csr, uint8_t * dst); 39 | 40 | enum cursor_res 41 | cursor_put(struct cursor * csr, void const * src, size_t src_len); 42 | 43 | enum cursor_res 44 | cursor_unpack_le_u8(struct cursor * csr, uint8_t * dst); 45 | 46 | enum cursor_res 47 | cursor_unpack_le_i8(struct cursor * csr, int8_t * dst); 48 | 49 | enum cursor_res 50 | cursor_unpack_le_u16(struct cursor * csr, uint16_t * dst); 51 | 52 | enum cursor_res 53 | cursor_unpack_le_i16(struct cursor * csr, int16_t * dst); 54 | 55 | enum cursor_res 56 | cursor_unpack_le_u32(struct cursor * csr, uint32_t * dst); 57 | 58 | enum cursor_res 59 | cursor_unpack_le_i32(struct cursor * csr, int32_t * dst); 60 | 61 | enum cursor_res 62 | cursor_unpack_le_u64(struct cursor * csr, uint64_t * dst); 63 | 64 | enum cursor_res 65 | cursor_unpack_le_i64(struct cursor * csr, int64_t * dst); 66 | 67 | enum cursor_res 68 | cursor_unpack_le_f(struct cursor * csr, float * dst); 69 | 70 | enum cursor_res 71 | cursor_unpack_le_d(struct cursor * csr, double * dst); 72 | 73 | #define cursor_unpack_le(_CSR, _DST) \ 74 | _Generic((_DST), \ 75 | uint8_t*: cursor_unpack_le_u8, \ 76 | uint16_t*: cursor_unpack_le_u16, \ 77 | uint32_t*: cursor_unpack_le_u32, \ 78 | uint64_t*: cursor_unpack_le_u64, \ 79 | int8_t*: cursor_unpack_le_i8, \ 80 | int16_t*: cursor_unpack_le_i16, \ 81 | int32_t*: cursor_unpack_le_i32, \ 82 | int64_t*: cursor_unpack_le_i64, \ 83 | float*: cursor_unpack_le_f, \ 84 | double*: cursor_unpack_le_d \ 85 | )(_CSR, _DST) 86 | 87 | enum cursor_res 88 | cursor_unpack_be_u8(struct cursor * csr, uint8_t * dst); 89 | 90 | enum cursor_res 91 | cursor_unpack_be_i8(struct cursor * csr, int8_t * dst); 92 | 93 | enum cursor_res 94 | cursor_unpack_be_u16(struct cursor * csr, uint16_t * dst); 95 | 96 | enum cursor_res 97 | cursor_unpack_be_i16(struct cursor * csr, int16_t * dst); 98 | 99 | enum cursor_res 100 | cursor_unpack_be_u32(struct cursor * csr, uint32_t * dst); 101 | 102 | enum cursor_res 103 | cursor_unpack_be_i32(struct cursor * csr, int32_t * dst); 104 | 105 | enum cursor_res 106 | cursor_unpack_be_u64(struct cursor * csr, uint64_t * dst); 107 | 108 | enum cursor_res 109 | cursor_unpack_be_i64(struct cursor * csr, int64_t * dst); 110 | 111 | enum cursor_res 112 | cursor_unpack_be_f(struct cursor * csr, float * dst); 113 | 114 | enum cursor_res 115 | cursor_unpack_be_d(struct cursor * csr, double * dst); 116 | 117 | #define cursor_unpack_be(_CSR, _DST) \ 118 | _Generic((_DST), \ 119 | uint8_t*: cursor_unpack_be_u8, \ 120 | uint16_t*: cursor_unpack_be_u16, \ 121 | uint32_t*: cursor_unpack_be_u32, \ 122 | uint64_t*: cursor_unpack_be_u64, \ 123 | int8_t*: cursor_unpack_be_i8, \ 124 | int16_t*: cursor_unpack_be_i16, \ 125 | int32_t*: cursor_unpack_be_i32, \ 126 | int64_t*: cursor_unpack_be_i64, \ 127 | float*: cursor_unpack_be_f, \ 128 | double*: cursor_unpack_be_d \ 129 | )(_CSR, _DST) 130 | 131 | enum cursor_res 132 | cursor_pack_le_u8(struct cursor * csr, uint8_t val); 133 | 134 | enum cursor_res 135 | cursor_pack_le_i8(struct cursor * csr, int8_t val); 136 | 137 | enum cursor_res 138 | cursor_pack_le_u16(struct cursor * csr, uint16_t val); 139 | 140 | enum cursor_res 141 | cursor_pack_le_i16(struct cursor * csr, int16_t val); 142 | 143 | enum cursor_res 144 | cursor_pack_le_u32(struct cursor * csr, uint32_t val); 145 | 146 | enum cursor_res 147 | cursor_pack_le_i32(struct cursor * csr, int32_t val); 148 | 149 | enum cursor_res 150 | cursor_pack_le_u64(struct cursor * csr, uint64_t val); 151 | 152 | enum cursor_res 153 | cursor_pack_le_i64(struct cursor * csr, int64_t val); 154 | 155 | enum cursor_res 156 | cursor_pack_le_f(struct cursor * csr, float val); 157 | 158 | enum cursor_res 159 | cursor_pack_le_d(struct cursor * csr, double val); 160 | 161 | #define cursor_pack_le(_CSR, _DST) \ 162 | _Generic((_DST), uint8_t \ 163 | : cursor_pack_le_u8, uint16_t \ 164 | : cursor_pack_le_u16, uint32_t \ 165 | : cursor_pack_le_u32, uint64_t \ 166 | : cursor_pack_le_u64, int8_t \ 167 | : cursor_pack_le_i8, int16_t \ 168 | : cursor_pack_le_i16, int32_t \ 169 | : cursor_pack_le_i32, int64_t \ 170 | : cursor_pack_le_i64, float \ 171 | : cursor_pack_le_f, double \ 172 | : cursor_pack_le_d)(_CSR, _DST) 173 | 174 | enum cursor_res 175 | cursor_pack_be_u8(struct cursor * csr, uint8_t val); 176 | 177 | enum cursor_res 178 | cursor_pack_be_i8(struct cursor * csr, int8_t val); 179 | 180 | enum cursor_res 181 | cursor_pack_be_u16(struct cursor * csr, uint16_t val); 182 | 183 | enum cursor_res 184 | cursor_pack_be_i16(struct cursor * csr, int16_t val); 185 | 186 | enum cursor_res 187 | cursor_pack_be_u32(struct cursor * csr, uint32_t val); 188 | 189 | enum cursor_res 190 | cursor_pack_be_i32(struct cursor * csr, int32_t val); 191 | 192 | enum cursor_res 193 | cursor_pack_be_u64(struct cursor * csr, uint64_t val); 194 | 195 | enum cursor_res 196 | cursor_pack_be_i64(struct cursor * csr, int64_t val); 197 | 198 | enum cursor_res 199 | cursor_pack_be_f(struct cursor * csr, float val); 200 | 201 | enum cursor_res 202 | cursor_pack_be_d(struct cursor * csr, double val); 203 | 204 | #define cursor_pack_be(_CSR, _DST) \ 205 | _Generic((_DST), uint8_t \ 206 | : cursor_pack_be_u8, uint16_t \ 207 | : cursor_pack_be_u16, uint32_t \ 208 | : cursor_pack_be_u32, uint64_t \ 209 | : cursor_pack_be_u64, int8_t \ 210 | : cursor_pack_be_i8, int16_t \ 211 | : cursor_pack_be_i16, int32_t \ 212 | : cursor_pack_be_i32, int64_t \ 213 | : cursor_pack_be_i64, float \ 214 | : cursor_pack_be_f, double \ 215 | : cursor_pack_be_d)(_CSR, _DST) 216 | 217 | #ifdef __cplusplus 218 | } 219 | #endif 220 | 221 | #endif /* CURSOR_H */ 222 | -------------------------------------------------------------------------------- /libloragw/inc/cursor/packing.h: -------------------------------------------------------------------------------- 1 | #ifndef PACKING_H 2 | #define PACKING_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void 11 | unpack_le_u8(uint8_t * dst, void const * src); 12 | 13 | void 14 | unpack_le_i8(int8_t * dst, void const * src); 15 | 16 | void 17 | unpack_le_u16(uint16_t * dst, void const * src); 18 | 19 | void 20 | unpack_le_i16(int16_t * dst, void const * src); 21 | 22 | void 23 | unpack_le_u32(uint32_t * dst, void const * src); 24 | 25 | void 26 | unpack_le_i32(int32_t * dst, void const * src); 27 | 28 | void 29 | unpack_le_u32(uint32_t * dst, void const * src); 30 | 31 | void 32 | unpack_le_i32(int32_t * dst, void const * src); 33 | 34 | void 35 | unpack_le_u64(uint64_t * dst, void const * src); 36 | 37 | void 38 | unpack_le_i64(int64_t * dst, void const * src); 39 | 40 | void 41 | unpack_le_f(float * dst, void const * src); 42 | 43 | void 44 | unpack_le_d(double * dst, void const * src); 45 | 46 | 47 | #define unpack_le(_DST, _SRC) \ 48 | _Generic((_DST), \ 49 | uint8_t*: unpack_le_u8, \ 50 | int8_t*: unpack_le_i8, \ 51 | uint16_t*: unpack_le_u16, \ 52 | int16_t*: unpack_le_i16, \ 53 | uint32_t*: unpack_le_u32, \ 54 | int32_t*: unpack_le_i32, \ 55 | uint64_t*: unpack_le_u64, \ 56 | int64_t*: unpack_le_i64, \ 57 | float*: unpack_le_f, \ 58 | double*: unpack_le_d \ 59 | )(_DST, _SRC) 60 | 61 | void 62 | unpack_be_u8(uint8_t * dst, void const * src); 63 | 64 | void 65 | unpack_be_i8(int8_t * dst, void const * src); 66 | 67 | void 68 | unpack_be_u16(uint16_t * dst, void const * src); 69 | 70 | void 71 | unpack_be_i16(int16_t * dst, void const * src); 72 | 73 | void 74 | unpack_be_u32(uint32_t * dst, void const * src); 75 | 76 | void 77 | unpack_be_i32(int32_t * dst, void const * src); 78 | 79 | void 80 | unpack_be_u32(uint32_t * dst, void const * src); 81 | 82 | void 83 | unpack_be_i32(int32_t * dst, void const * src); 84 | 85 | void 86 | unpack_be_u64(uint64_t * dst, void const * src); 87 | 88 | void 89 | unpack_be_i64(int64_t * dst, void const * src); 90 | 91 | void 92 | unpack_be_f(float * dst, void const * src); 93 | 94 | void 95 | unpack_be_d(double * dst, void const * src); 96 | 97 | #define unpack_be(_DST, _SRC) \ 98 | _Generic((_DST), \ 99 | uint8_t*: unpack_be_u8, \ 100 | int8_t*: unpack_be_i8, \ 101 | uint16_t*: unpack_be_u16, \ 102 | int16_t*: unpack_be_i16, \ 103 | uint32_t*: unpack_be_u32, \ 104 | int32_t*: unpack_be_i32, \ 105 | uint64_t*: unpack_be_u64, \ 106 | int64_t*: unpack_be_i64, \ 107 | float*: unpack_be_f, \ 108 | double*: unpack_be_d \ 109 | )(_DST, _SRC) 110 | 111 | void 112 | pack_le_u8(void * dst, uint8_t val); 113 | 114 | void 115 | pack_le_i8(void * dst, int8_t val); 116 | 117 | void 118 | pack_le_u16(void * dst, uint16_t val); 119 | 120 | void 121 | pack_le_i16(void * dst, int16_t val); 122 | 123 | void 124 | pack_le_u32(void * dst, uint32_t val); 125 | 126 | void 127 | pack_le_i32(void * dst, int32_t val); 128 | 129 | void 130 | pack_le_u64(void * dst, uint64_t val); 131 | 132 | void 133 | pack_le_i64(void * dst, int64_t val); 134 | 135 | void 136 | pack_le_f(void * dst, float val); 137 | 138 | void 139 | pack_le_d(void * dst, double val); 140 | 141 | #define pack_le(_DST, _VAL) \ 142 | _Generic((_VAL), uint8_t \ 143 | : pack_le_u8, int8_t \ 144 | : pack_le_i8, uint16_t \ 145 | : pack_le_u16, int16_t \ 146 | : pack_le_i16, uint32_t \ 147 | : pack_le_u32, int32_t \ 148 | : pack_le_i32, uint64_t \ 149 | : pack_le_u64, int64_t \ 150 | : pack_le_i64, float \ 151 | : pack_le_f, double \ 152 | : pack_le_d)(_DST, _VAL) 153 | 154 | void 155 | pack_be_u8(void * dst, uint8_t val); 156 | 157 | void 158 | pack_be_i8(void * dst, int8_t val); 159 | 160 | void 161 | pack_be_u16(void * dst, uint16_t val); 162 | 163 | void 164 | pack_be_i16(void * dst, int16_t val); 165 | 166 | void 167 | pack_be_u32(void * dst, uint32_t val); 168 | 169 | void 170 | pack_be_i32(void * dst, int32_t val); 171 | 172 | void 173 | pack_be_u64(void * dst, uint64_t val); 174 | 175 | void 176 | pack_be_i64(void * dst, int64_t val); 177 | 178 | void 179 | pack_be_f(void * dst, float val); 180 | 181 | void 182 | pack_be_d(void * dst, double val); 183 | 184 | #define pack_be(_DST, _VAL) \ 185 | _Generic((_VAL), uint8_t \ 186 | : pack_be_u8, int8_t \ 187 | : pack_be_i8, uint16_t \ 188 | : pack_be_u16, int16_t \ 189 | : pack_be_i16, uint32_t \ 190 | : pack_be_u32, int32_t \ 191 | : pack_be_i32, uint64_t \ 192 | : pack_be_u64, int64_t \ 193 | : pack_be_i64, float \ 194 | : pack_be_f, double \ 195 | : pack_be_d)(_DST, _VAL) 196 | 197 | #ifdef __cplusplus 198 | } 199 | #endif 200 | 201 | #endif /* PACKING_H */ 202 | -------------------------------------------------------------------------------- /libloragw/inc/loragw/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/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/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 utc_acc; /*!> estimated UTC time accuracy (from GPS) */ 45 | struct timespec gps; /*!> reference GPS time (since 01.Jan.1980) */ 46 | double xtal_err; /*!> raw clock error (eg. <1 'slow' XTAL) */ 47 | }; 48 | 49 | /** 50 | @struct coord_s 51 | @brief Geodesic coordinates 52 | */ 53 | struct coord_s { 54 | double lat; /*!> latitude [-90,90] (North +, South -) */ 55 | double lon; /*!> longitude [-180,180] (East +, West -)*/ 56 | float eha; /*!> estimated horizontal accuracy (m) */ 57 | float eva; /*!> estimated vertical accuracy (m) */ 58 | int nsv; /*!> number of satellites used in the position fix */ 59 | short alt; /*!> altitude in meters (WGS 84 geoid ref.) */ 60 | }; 61 | 62 | /** 63 | @enum gps_msg 64 | @brief Type of GPS (and other GNSS) sentences 65 | */ 66 | enum gps_msg { 67 | UNKNOWN, /*!> neutral value */ 68 | IGNORED, /*!> frame was not parsed by the system */ 69 | INVALID, /*!> system try to parse frame but failed */ 70 | INCOMPLETE, /*!> frame parsed was missing bytes */ 71 | /* NMEA messages of interest */ 72 | NMEA_RMC, /*!> Recommended Minimum data (time + date) */ 73 | NMEA_GGA, /*!> Global positioning system fix data (pos + alt) */ 74 | NMEA_GNS, /*!> GNSS fix data (pos + alt, sat number) */ 75 | NMEA_ZDA, /*!> Time and Date */ 76 | /* NMEA message useful for time reference quality assessment */ 77 | NMEA_GBS, /*!> GNSS Satellite Fault Detection */ 78 | NMEA_GST, /*!> GNSS Pseudo Range Error Statistics */ 79 | NMEA_GSA, /*!> GNSS DOP and Active Satellites (sat number) */ 80 | NMEA_GSV, /*!> GNSS Satellites in View (sat SNR) */ 81 | /* Misc. NMEA messages */ 82 | NMEA_GLL, /*!> Latitude and longitude, with time fix and status */ 83 | NMEA_TXT, /*!> Text Transmission */ 84 | NMEA_VTG, /*!> Course over ground and Ground speed */ 85 | /* uBlox proprietary NMEA messages of interest */ 86 | UBX_NAV_TIMEGPS, /*!> GPS Time Solution */ 87 | UBX_NAV_TIMEUTC, /*!> UTC Time Solution */ 88 | UBX_NAV_PVT /*!> Position velocity and time */ 89 | }; 90 | 91 | /* -------------------------------------------------------------------------- */ 92 | /* --- PUBLIC CONSTANTS ----------------------------------------------------- */ 93 | 94 | #define LGW_GPS_SUCCESS 0 95 | #define LGW_GPS_ERROR -1 96 | 97 | #define LGW_GPS_MIN_MSG_SIZE (8) 98 | #define LGW_GPS_UBX_SYNC_CHAR (0xB5) 99 | #define LGW_GPS_NMEA_SYNC_CHAR (0x24) 100 | 101 | /* -------------------------------------------------------------------------- */ 102 | /* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ 103 | 104 | /** 105 | @brief Configure a GPS module 106 | 107 | @param tty_path path to the TTY connected to the GPS 108 | @param gps_familly parameter (eg. ubx6 for uBlox gen.6) 109 | @param target_brate target baudrate for communication (0 keeps default target baudrate) 110 | @param fd_ptr pointer to a variable to receive file descriptor on GPS tty 111 | @return success if the function was able to connect and configure a GPS module 112 | */ 113 | int lgw_gps_enable(char* tty_path, char* gps_familly, speed_t target_brate, int* fd_ptr); 114 | 115 | /** 116 | @brief Restore GPS serial configuration and close serial device 117 | 118 | @param fd file descriptor on GPS tty 119 | @return success if the function was able to complete 120 | */ 121 | int lgw_gps_disable(int fd); 122 | 123 | /** 124 | @brief Parse messages coming from the GPS system (or other GNSS) 125 | 126 | @param serial_buff pointer to the string to be parsed 127 | @param buff_size maximum string lengths for NMEA parsing (incl. null char) 128 | @return type of frame parsed 129 | 130 | The RAW NMEA sentences are parsed to a global set of variables shared with the 131 | lgw_gps_get function. 132 | If the lgw_parse_nmea and lgw_gps_get are used in different threads, a mutex 133 | lock must be acquired before calling either function. 134 | */ 135 | enum gps_msg lgw_parse_nmea(const char* serial_buff, int buff_size); 136 | 137 | /** 138 | @brief Parse Ublox proprietary messages coming from the GPS system 139 | 140 | @param serial_buff pointer to the string to be parsed 141 | @param buff_size maximum string lengths for UBX parsing (incl. null char) 142 | @param msg_size number of bytes parsed as UBX message if found 143 | @return type of frame parsed 144 | 145 | The RAW UBX sentences are parsed to a global set of variables shared with the 146 | lgw_gps_get function. 147 | If the lgw_parse_ubx and lgw_gps_get are used in different threads, a mutex 148 | lock must be acquired before calling either function. 149 | */ 150 | enum gps_msg lgw_parse_ubx(const char* serial_buff, size_t buff_size, size_t *msg_size); 151 | 152 | /** 153 | @brief Get the GPS solution (space & time) for the concentrator 154 | 155 | @param utc pointer to store UTC time, with ns precision (NULL to ignore) 156 | @param gps_time pointer to store GPS time, with ns precision (NULL to ignore) 157 | @param loc pointer to store coordinates (NULL to ignore) 158 | @return success if the chosen elements could be returned 159 | 160 | This function read the global variables generated by the NMEA/UBX parsing 161 | functions lgw_parse_nmea/lgw_parse_ubx. It returns time and location data in a 162 | format that is exploitable by other functions in that library sub-module. 163 | If the lgw_parse_nmea/lgw_parse_ubx and lgw_gps_get are used in different 164 | threads, a mutex lock must be acquired before calling either function. 165 | */ 166 | int lgw_gps_get(struct timespec *utc, struct timespec *utc_acc, struct timespec *gps_time, struct coord_s *loc); 167 | 168 | /** 169 | @brief Get time and position information from the serial GPS last message received 170 | @param utc UTC time, with ns precision (leap seconds are ignored) 171 | @param gps_time timestamp of last time pulse from the GPS module converted to the UNIX epoch 172 | (leap seconds are ignored) 173 | @param loc location information 174 | @param err location error estimate if supported 175 | @return success if timestamp was read and time reference could be refreshed 176 | 177 | Set systime to 0 in ref to trigger initial synchronization. 178 | */ 179 | int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec utc_acc, struct timespec gps_time); 180 | 181 | /** 182 | @brief Convert concentrator timestamp counter value to UTC time 183 | 184 | @param ref time reference structure required for time conversion 185 | @param count_us internal timestamp counter of the LoRa concentrator 186 | @param utc pointer to store UTC time, with ns precision (leap seconds ignored) 187 | @return success if the function was able to convert timestamp to UTC 188 | 189 | This function is typically used when a packet is received to transform the 190 | internal counter-based timestamp in an absolute timestamp with an accuracy in 191 | the order of a couple microseconds (ns resolution). 192 | */ 193 | int lgw_cnt2utc(struct tref ref, uint32_t count_us, struct timespec* utc); 194 | 195 | /** 196 | @brief Convert UTC time to concentrator timestamp counter value 197 | 198 | @param ref time reference structure required for time conversion 199 | @param utc UTC time, with ns precision (leap seconds are ignored) 200 | @param count_us pointer to store internal timestamp counter of LoRa concentrator 201 | @return success if the function was able to convert UTC to timestamp 202 | 203 | This function is typically used when a packet must be sent at an accurate time 204 | (eg. to send a piggy-back response after receiving a packet from a node) to 205 | transform an absolute UTC time into a matching internal concentrator timestamp. 206 | */ 207 | int lgw_utc2cnt(struct tref ref,struct timespec utc, uint32_t* count_us); 208 | 209 | /** 210 | @brief Convert concentrator timestamp counter value to GPS time 211 | 212 | @param ref time reference structure required for time conversion 213 | @param count_us internal timestamp counter of the LoRa concentrator 214 | @param gps_time pointer to store GPS time, with ns precision (leap seconds ignored) 215 | @return success if the function was able to convert timestamp to GPS time 216 | 217 | This function is typically used when a packet is received to transform the 218 | internal counter-based timestamp in an absolute timestamp with an accuracy in 219 | the order of a millisecond. 220 | */ 221 | int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec* gps_time); 222 | 223 | /** 224 | @brief Convert GPS time to concentrator timestamp counter value 225 | 226 | @param ref time reference structure required for time conversion 227 | @param gps_time GPS time, with ns precision (leap seconds are ignored) 228 | @param count_us pointer to store internal timestamp counter of LoRa concentrator 229 | @return success if the function was able to convert GPS time to timestamp 230 | 231 | This function is typically used when a packet must be sent at an accurate time 232 | (eg. to send a piggy-back response after receiving a packet from a node) to 233 | transform an absolute GPS time into a matching internal concentrator timestamp. 234 | */ 235 | int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t* count_us); 236 | 237 | #endif 238 | 239 | /* --- EOF ------------------------------------------------------------------ */ 240 | -------------------------------------------------------------------------------- /libloragw/inc/loragw/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/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/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/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 2 | #include 3 | #include 4 | #include 5 | 6 | struct cursor 7 | cursor_new(void * buf, size_t buflen) { 8 | assert(buf); 9 | return (struct cursor){ 10 | .buf = buf, 11 | .len = buflen, 12 | .pos = 0, 13 | }; 14 | } 15 | 16 | /* Check that buffer has enough bytes left to unpack the desired type out */ 17 | #define SIZE_CHECK(_CSR, _PRIM_TYPE) \ 18 | do { \ 19 | if ((_CSR->pos + sizeof(_PRIM_TYPE)) > _CSR->len) { \ 20 | return cursor_res_err_buf_exhausted; \ 21 | } \ 22 | } while (0) 23 | 24 | #define INC_POS(_CSR, _PRIM_TYPE) \ 25 | do { \ 26 | _CSR->pos += sizeof(_PRIM_TYPE); \ 27 | } while (0) 28 | 29 | #define GENERATE(_PRIM_TYPE, _SHORT_NAME) \ 30 | enum cursor_res cursor_unpack_le_##_SHORT_NAME(struct cursor * csr, \ 31 | _PRIM_TYPE * dst) { \ 32 | SIZE_CHECK(csr, *dst); \ 33 | unpack_le_##_SHORT_NAME(dst, &csr->buf[csr->pos]); \ 34 | INC_POS(csr, *dst); \ 35 | return cursor_res_ok; \ 36 | } \ 37 | enum cursor_res cursor_pack_le_##_SHORT_NAME(struct cursor * csr, \ 38 | _PRIM_TYPE val) { \ 39 | SIZE_CHECK(csr, val); \ 40 | pack_le_##_SHORT_NAME(&csr->buf[csr->pos], val); \ 41 | INC_POS(csr, val); \ 42 | return cursor_res_ok; \ 43 | } \ 44 | enum cursor_res cursor_unpack_be_##_SHORT_NAME(struct cursor * csr, \ 45 | _PRIM_TYPE * dst) { \ 46 | SIZE_CHECK(csr, *dst); \ 47 | unpack_be_##_SHORT_NAME(dst, &csr->buf[csr->pos]); \ 48 | INC_POS(csr, *dst); \ 49 | return cursor_res_ok; \ 50 | } \ 51 | enum cursor_res cursor_pack_be_##_SHORT_NAME(struct cursor * csr, \ 52 | _PRIM_TYPE val) { \ 53 | SIZE_CHECK(csr, val); \ 54 | pack_be_##_SHORT_NAME(&csr->buf[csr->pos], val); \ 55 | INC_POS(csr, val); \ 56 | return cursor_res_ok; \ 57 | } 58 | 59 | 60 | GENERATE(uint8_t, u8) 61 | GENERATE(int8_t, i8) 62 | GENERATE(uint16_t, u16) 63 | GENERATE(int16_t, i16) 64 | GENERATE(uint32_t, u32) 65 | GENERATE(int32_t, i32) 66 | GENERATE(uint64_t, u64) 67 | GENERATE(int64_t, i64) 68 | GENERATE(float, f) 69 | GENERATE(double, d) 70 | 71 | enum cursor_res 72 | cursor_take(struct cursor * csr, size_t n, uint8_t * dst) { 73 | if (csr->pos + n > csr->len) { 74 | return cursor_res_err_buf_exhausted; 75 | } 76 | memmove(dst, csr->buf + csr->pos, n); 77 | csr->pos += n; 78 | return cursor_res_ok; 79 | } 80 | 81 | size_t 82 | cursor_remaining(struct cursor const * csr) { 83 | return csr->len - csr->pos; 84 | } 85 | 86 | size_t 87 | cursor_take_remaining(struct cursor * csr, uint8_t * dst) { 88 | size_t remaining = cursor_remaining(csr); 89 | enum cursor_res res = cursor_take(csr, remaining, dst); 90 | assert(cursor_res_ok == res); 91 | (void)res; 92 | assert(0 == cursor_remaining(csr)); 93 | return remaining; 94 | } 95 | 96 | enum cursor_res 97 | cursor_put(struct cursor * csr, void const * src, size_t src_len) { 98 | if (cursor_remaining(csr) < src_len) { 99 | return cursor_res_err_buf_exhausted; 100 | } 101 | memcpy(&csr->buf[csr->pos], src, src_len); 102 | csr->pos += src_len; 103 | return cursor_res_ok; 104 | } 105 | -------------------------------------------------------------------------------- /libloragw/src/loragw_aux.c: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | (C)2013 Semtech-Cycleo 8 | 9 | Description: 10 | LoRa concentrator HAL auxiliary functions 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 /* 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_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/src/packing.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SRC ((uint8_t *)src) 4 | #define DST ((uint8_t *)dst) 5 | 6 | #ifdef HAVE_ATTRIBUTE_MAY_ALIAS 7 | typedef uint32_t __attribute__((__may_alias__)) uint32_a; 8 | typedef uint64_t __attribute__((__may_alias__)) uint64_a; 9 | #else 10 | typedef uint32_t uint32_a; 11 | typedef uint64_t uint64_a; 12 | #endif 13 | 14 | #define GENERATE_UNPACK_LE_8(_PRIM_TYPE, _SHORT_NAME) \ 15 | void unpack_le_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 16 | *dst = SRC[0]; \ 17 | } 18 | 19 | #define GENERATE_UNPACK_LE_16(_PRIM_TYPE, _SHORT_NAME) \ 20 | void unpack_le_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 21 | *dst = (SRC[1] << 8) | SRC[0]; \ 22 | } 23 | 24 | #define GENERATE_UNPACK_LE_32(_PRIM_TYPE, _SHORT_NAME) \ 25 | void unpack_le_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 26 | uint32_t val = ((uint32_t)SRC[3] << 24) | ((uint32_t)SRC[2] << 16) \ 27 | | ((uint32_t)SRC[1] << 8) | SRC[0]; \ 28 | *((uint32_t *)dst) = val; \ 29 | } 30 | 31 | #define GENERATE_UNPACK_LE_64(_PRIM_TYPE, _SHORT_NAME) \ 32 | void unpack_le_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 33 | uint64_t val = ((uint64_t)SRC[7] << 56) | ((uint64_t)SRC[6] << 48) \ 34 | | ((uint64_t)SRC[5] << 40) | ((uint64_t)SRC[4] << 32) \ 35 | | ((uint64_t)SRC[3] << 24) | ((uint64_t)SRC[2] << 16) \ 36 | | ((uint64_t)SRC[1] << 8) | (uint64_t)SRC[0]; \ 37 | *((uint64_t *)dst) = val; \ 38 | } 39 | 40 | #define GENERATE_UNPACK_BE_8(_PRIM_TYPE, _SHORT_NAME) \ 41 | void unpack_be_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 42 | *dst = SRC[0]; \ 43 | } 44 | 45 | #define GENERATE_UNPACK_BE_16(_PRIM_TYPE, _SHORT_NAME) \ 46 | void unpack_be_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 47 | *dst = (SRC[0] << 8) | SRC[1]; \ 48 | } 49 | 50 | #define GENERATE_UNPACK_BE_32(_PRIM_TYPE, _SHORT_NAME) \ 51 | void unpack_be_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 52 | *((uint32_t *)dst) = ((uint32_t)SRC[0] << 24) \ 53 | | ((uint32_t)SRC[1] << 16) \ 54 | | ((uint32_t)SRC[2] << 8) | SRC[3]; \ 55 | } 56 | 57 | #define GENERATE_UNPACK_BE_64(_PRIM_TYPE, _SHORT_NAME) \ 58 | void unpack_be_##_SHORT_NAME(_PRIM_TYPE * dst, void const * src) { \ 59 | *((uint64_t *)dst) = \ 60 | ((uint64_t)SRC[0] << 56) | ((uint64_t)SRC[1] << 48) \ 61 | | ((uint64_t)SRC[2] << 40) | ((uint64_t)SRC[3] << 32) \ 62 | | ((uint64_t)SRC[4] << 24) | ((uint64_t)SRC[5] << 16) \ 63 | | ((uint64_t)SRC[6] << 8) | (uint64_t)SRC[7]; \ 64 | } 65 | 66 | #define GENERATE_PACK_LE_8(_PRIM_TYPE, _SHORT_NAME) \ 67 | void pack_le_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 68 | DST[0] = val; \ 69 | } 70 | #define GENERATE_PACK_LE_16(_PRIM_TYPE, _SHORT_NAME) \ 71 | void pack_le_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 72 | DST[0] = val & 0xff; \ 73 | DST[1] = (val >> 8) & 0xff; \ 74 | } 75 | 76 | #define GENERATE_PACK_LE_32(_PRIM_TYPE, _SHORT_NAME) \ 77 | void pack_le_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 78 | uint32_t val_cast = *((uint32_a *)&val); \ 79 | DST[0] = val_cast & 0xff; \ 80 | DST[1] = (val_cast >> 8) & 0xff; \ 81 | DST[2] = (val_cast >> 16) & 0xff; \ 82 | DST[3] = (val_cast >> 24) & 0xff; \ 83 | } 84 | 85 | #define GENERATE_PACK_LE_64(_PRIM_TYPE, _SHORT_NAME) \ 86 | void pack_le_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 87 | uint64_t val_cast = *((uint64_a *)&val); \ 88 | DST[0] = val_cast & 0xff; \ 89 | DST[1] = (val_cast >> 8) & 0xff; \ 90 | DST[2] = (val_cast >> 16) & 0xff; \ 91 | DST[3] = (val_cast >> 24) & 0xff; \ 92 | DST[4] = (val_cast >> 32) & 0xff; \ 93 | DST[5] = (val_cast >> 40) & 0xff; \ 94 | DST[6] = (val_cast >> 48) & 0xff; \ 95 | DST[7] = (val_cast >> 56) & 0xff; \ 96 | } 97 | 98 | #define GENERATE_PACK_BE_8(_PRIM_TYPE, _SHORT_NAME) \ 99 | void pack_be_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 100 | DST[0] = val; \ 101 | } 102 | #define GENERATE_PACK_BE_16(_PRIM_TYPE, _SHORT_NAME) \ 103 | void pack_be_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 104 | DST[0] = (val >> 8) & 0xff; \ 105 | DST[1] = val & 0xff; \ 106 | } 107 | 108 | #define GENERATE_PACK_BE_32(_PRIM_TYPE, _SHORT_NAME) \ 109 | void pack_be_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 110 | uint32_t val_cast = *((uint32_a *)&val); \ 111 | DST[0] = (val_cast >> 24) & 0xff; \ 112 | DST[1] = (val_cast >> 16) & 0xff; \ 113 | DST[2] = (val_cast >> 8) & 0xff; \ 114 | DST[3] = val_cast & 0xff; \ 115 | } 116 | 117 | #define GENERATE_PACK_BE_64(_PRIM_TYPE, _SHORT_NAME) \ 118 | void pack_be_##_SHORT_NAME(void * dst, _PRIM_TYPE val) { \ 119 | uint64_t val_cast = *((uint64_a *)&val); \ 120 | DST[0] = (val_cast >> 56) & 0xff; \ 121 | DST[1] = (val_cast >> 48) & 0xff; \ 122 | DST[2] = (val_cast >> 40) & 0xff; \ 123 | DST[3] = (val_cast >> 32) & 0xff; \ 124 | DST[4] = (val_cast >> 24) & 0xff; \ 125 | DST[5] = (val_cast >> 16) & 0xff; \ 126 | DST[6] = (val_cast >> 8) & 0xff; \ 127 | DST[7] = val_cast & 0xff; \ 128 | } 129 | 130 | GENERATE_UNPACK_LE_8(uint8_t, u8) 131 | GENERATE_UNPACK_LE_8(int8_t, i8) 132 | GENERATE_UNPACK_LE_16(uint16_t, u16) 133 | GENERATE_UNPACK_LE_16(int16_t, i16) 134 | GENERATE_UNPACK_LE_32(uint32_t, u32) 135 | GENERATE_UNPACK_LE_32(int32_t, i32) 136 | GENERATE_UNPACK_LE_64(uint64_t, u64) 137 | GENERATE_UNPACK_LE_64(int64_t, i64) 138 | GENERATE_UNPACK_LE_32(float, f) 139 | GENERATE_UNPACK_LE_64(double, d) 140 | 141 | GENERATE_PACK_LE_8(uint8_t, u8) 142 | GENERATE_PACK_LE_8(int8_t, i8) 143 | GENERATE_PACK_LE_16(uint16_t, u16) 144 | GENERATE_PACK_LE_16(int16_t, i16) 145 | GENERATE_PACK_LE_32(uint32_t, u32) 146 | GENERATE_PACK_LE_32(int32_t, i32) 147 | GENERATE_PACK_LE_64(uint64_t, u64) 148 | GENERATE_PACK_LE_64(int64_t, i64) 149 | GENERATE_PACK_LE_32(float, f) 150 | GENERATE_PACK_LE_64(double, d) 151 | 152 | GENERATE_UNPACK_BE_8(uint8_t, u8) 153 | GENERATE_UNPACK_BE_8(int8_t, i8) 154 | GENERATE_UNPACK_BE_16(uint16_t, u16) 155 | GENERATE_UNPACK_BE_16(int16_t, i16) 156 | GENERATE_UNPACK_BE_32(uint32_t, u32) 157 | GENERATE_UNPACK_BE_32(int32_t, i32) 158 | GENERATE_UNPACK_BE_64(uint64_t, u64) 159 | GENERATE_UNPACK_BE_64(int64_t, i64) 160 | GENERATE_UNPACK_BE_32(float, f) 161 | GENERATE_UNPACK_BE_64(double, d) 162 | 163 | GENERATE_PACK_BE_8(uint8_t, u8) 164 | GENERATE_PACK_BE_8(int8_t, i8) 165 | GENERATE_PACK_BE_16(uint16_t, u16) 166 | GENERATE_PACK_BE_16(int16_t, i16) 167 | GENERATE_PACK_BE_32(uint32_t, u32) 168 | GENERATE_PACK_BE_32(int32_t, i32) 169 | GENERATE_PACK_BE_64(uint64_t, u64) 170 | GENERATE_PACK_BE_64(int64_t, i64) 171 | GENERATE_PACK_BE_32(float, f) 172 | GENERATE_PACK_BE_64(double, d) 173 | -------------------------------------------------------------------------------- /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 | struct timespec ppm_utc_acc; 71 | 72 | /* variables for timestamp <-> GPS time conversions */ 73 | uint32_t x, z; 74 | struct timespec y; 75 | 76 | /* get GPS time for synchronization */ 77 | int i = lgw_gps_get(&ppm_utc, &ppm_utc_acc, &ppm_gps, NULL); 78 | if (i != LGW_GPS_SUCCESS) { 79 | printf(" No valid reference GPS time available, synchronization impossible.\n"); 80 | return; 81 | } 82 | 83 | /* get timestamp for synchronization */ 84 | i = lgw_get_trigcnt(&ppm_tstamp); 85 | if (i != LGW_HAL_SUCCESS) { 86 | printf(" Failed to read timestamp, synchronization impossible.\n"); 87 | return; 88 | } 89 | 90 | /* try to update synchronize time reference with the new GPS & timestamp */ 91 | i = lgw_gps_sync(&ppm_ref, ppm_tstamp, ppm_utc, ppm_utc_acc, ppm_gps); 92 | if (i != LGW_GPS_SUCCESS) { 93 | printf(" Synchronization error.\n"); 94 | return; 95 | } 96 | 97 | /* display result */ 98 | printf(" * Synchronization successful *\n"); 99 | printf(" UTC reference time: %lld.%09ld\n", (long long)ppm_ref.utc.tv_sec, ppm_ref.utc.tv_nsec); 100 | printf(" GPS reference time: %lld.%09ld\n", (long long)ppm_ref.gps.tv_sec, ppm_ref.gps.tv_nsec); 101 | printf(" Internal counter reference value: %u\n", ppm_ref.count_us); 102 | printf(" Clock error: %.9f\n", ppm_ref.xtal_err); 103 | 104 | x = ppm_tstamp + 500000; 105 | printf(" * Test of timestamp counter <-> GPS value conversion *\n"); 106 | printf(" Test value: %u\n", x); 107 | lgw_cnt2gps(ppm_ref, x, &y); 108 | printf(" Conversion to GPS: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); 109 | lgw_gps2cnt(ppm_ref, y, &z); 110 | printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); 111 | printf(" * Test of timestamp counter <-> UTC value conversion *\n"); 112 | printf(" Test value: %u\n", x); 113 | lgw_cnt2utc(ppm_ref, x, &y); 114 | printf(" Conversion to UTC: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); 115 | lgw_utc2cnt(ppm_ref, y, &z); 116 | printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); 117 | } 118 | 119 | static void gps_process_coords(void) { 120 | /* position variable */ 121 | struct coord_s coord; 122 | int i = lgw_gps_get(NULL, NULL, NULL, &coord); 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 accuracy: horizontal %.1f m, vertical %.1f m\n", coord.eha, coord.eva); 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 %ld\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] == (char)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=7 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/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(util_lbt_test 2 | src/util_lbt_test.c 3 | ) 4 | target_link_libraries(util_lbt_test 5 | LoraGW::loragw 6 | ) 7 | -------------------------------------------------------------------------------- /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/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(util_pkt_logger 2 | src/parson.c 3 | src/util_pkt_logger.c 4 | ) 5 | 6 | target_include_directories(util_pkt_logger 7 | PRIVATE inc 8 | ) 9 | 10 | target_link_libraries(util_pkt_logger 11 | LoraGW::loragw 12 | ) 13 | -------------------------------------------------------------------------------- /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": 867500000, 9 | "rssi_offset": -166.0, 10 | "tx_enable": false 11 | }, 12 | "radio_1": { 13 | "enable": true, 14 | "type": "SX1257", 15 | "freq": 868500000, 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/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(util_spectral_scan 2 | src/util_spectral_scan.c 3 | ) 4 | 5 | target_include_directories(util_spectral_scan 6 | PRIVATE inc 7 | ) 8 | 9 | target_link_libraries(util_spectral_scan 10 | LoraGW::loragw 11 | ) 12 | -------------------------------------------------------------------------------- /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/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(util_spi_stress 2 | src/util_spi_stress.c 3 | ) 4 | 5 | target_include_directories(util_spi_stress 6 | PRIVATE inc 7 | ) 8 | 9 | target_link_libraries(util_spi_stress 10 | LoraGW::loragw 11 | ) 12 | -------------------------------------------------------------------------------- /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