forest-rain 4 vuotta sitten
sitoutus
5b9867fd4d

+ 12 - 0
SConscript

@@ -0,0 +1,12 @@
+import os
+from building import *
+
+objs = []
+cwd  = GetCurrentDir()
+list = os.listdir(cwd)
+
+for item in list:
+    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
+        objs = objs + SConscript(os.path.join(item, 'SConscript'))
+
+Return('objs')

+ 405 - 0
docs/ReadMe_lora-gw-driver.md

@@ -0,0 +1,405 @@
+# lora-gw-driver-lib软件包使用说明
+
+# 1 简介
+lora-gw-driver软件包(以下简称lgd)是基于RTOS( RT-Thread ) 实现的LoRa网关芯片(SPI)的驱动文件,当前支持SX1302芯片,可以用于创建基于lora网关芯片SX130x的多通道自组网LoRa集中器、LoRa\LoRaWAN网关、多通道LoRa Sniffer工具等。
+
+注:当前lora-gw-driver软件包的SX130x驱动部分,对外版本以二进制lib库形式(lora-gw-driver-lib)提供。
+
+> lora-gw-driver基于Semtech的libloragw进一步构建实现,可参考官方实现如下
+>
+> [libloragw]: https://github.com/Lora-net/sx1302_hal/tree/master/libloragw
+
+## 1.1 功能简介
+
+- 当前支持LoRa网关芯片(sx130x )
+   - [x] SX1302
+     - [x] 支持IF0~IF7 LoRa Multi-SF(BW125)
+- 支持lgd tester功能
+
+   - 丰富shell命令可灵活\动态配置lgd运行参数
+
+   - 支持设定8个LoRa接收信道
+      - 支持自定义频率组方式
+      - 支持自动设置频率组方式等
+      - 支持设定上行\下行(IQ反向接收)
+   - 支持设定TX运行参数(发射功率、发送频点、IQ反向等)
+   - 支持设定LoRaWAN公网\私有等
+- 支持TX\RX同频模式与异频模式
+- 支持常用的射频性能测试、空口数据包监听、单向\双向\主\从通信测试等功能
+   - Concentrator模式
+      - 连续接收与应答
+   - Device模式
+      - 主动发送,支持自定义数据包个数、数据包长度
+   - CW模式
+      - 载波发射,支持设定CW频率等
+   - 支持上述工作模式的动态切换
+- 可以与[lora-radio-driver](https://github.com/Forest-Rain/lora-radio-driver/tree/master)配合进行单向\双向空口loopback测试(ping-pong)
+- 可作为驱动层进一步对接到lora-pkt-sniffer软件包,构建lora抓包工具等
+- 可作为驱动层进一步对接到lora-pkt-fwd软件包,构建lorawan网关等
+- 当前主要测试的LoRa 网关模块
+   - LoRa  Gateway Baseband Processor (SPI)
+      - SX130X
+         - SX1302
+            - [x] [LSD4WN-2K730NE0 (CN470频段)]([产品详情-利尔达科技集团股份有限公司 - 利尔达提供整套LoRaWAN系统解决方案 (lierda.com)](http://wsn.lierda.com/index.php/Home/Product/detail/id/100.html))
+              - TX: 470~510 MHz,最大功率~22dBm
+              - RX: 470~480 MHz
+            - [ ] [LSD4WN-2K830NE0 (EU868频段)]([产品详情-利尔达科技集团股份有限公司 - 利尔达提供整套LoRaWAN系统解决方案 (lierda.com)](http://wsn.lierda.com/index.php/Home/Product/detail/id/99.html))
+              - TX: 860~870 MHz,最大功率~22dBm
+              - RX: 860~870 MHz
+- 当前测试的MCU平台
+   - 硬件平台独立,可用于不同硬件平台
+   - LoRa GW Driver当前功能主要在STM32平台测试通过
+      - [x] STM32H7系列
+      - [ ] ....
+- 当前支持的RTOS
+   - [x] RT-Thread
+- 当前测试的IDE
+   - [x] MDK5.29
+   - [x] RT-Thread Studio 2.x
+
+# 2 lora-gw-driver-lib软件包系统概述
+
+## 2.1 lora-gw-driver-lib软件包功能框图
+
+![lora-gw-driver功能框图](images\lgd-function-block.png)
+
+lora-gw-driver-lib主要实现了SX1302模块的SPI方式寄存器读写控制、数据收发、lorawan解析服务、lora数据包格式化等功能
+
+lora-gw-driver-teser用于测试\演示lora-gw-driver的使用等,基于finish实现,当前支持CW模式、Concentrator模式、Device模式。
+
+- CW模式
+
+- - sx130x发送载波信号,可用于测试发射功率等
+
+- Concentrator模式
+
+- - 上电后,sx130x默认处于Concentrator模式,即一直保持接收模式,接收到lora终端设备数据后,并自动回发ACK给终端设备。
+
+- Device模式
+
+- - Device模式,sx130x模拟lora终端设备,sx130x主动发送ping包,等待接收设备回发ack。
+
+- - ![lgd-ping数据通信流程](images\lgd-ping-testflow.png)
+
+## 2.2 lora-gw-driver-lib软件包组织结构
+
+```
+$ lora-gw-driver-lib
+├── README.md
+├── docs
+|   ├── images
+|   └── readme.md 
+├── libraries
+│   └── libs    
+├── ports
+│   └── stm32_adapter
+├── samples
+    └── lora-gw-driver-tester
+```
+
+- samples
+   - lora gw driver samples
+      - lgd-tester
+         - lgd-tester示例,当前主要实现了Concentrator模式、Device模式、CW模式,可进行lora性能评估与通信功能测试等
+           - ping(pong)单\双向通信测试
+           - CW射频载波性能测试
+           - 空口数据包监听(接收)等
+
+## 2.3 Shell命令
+
+lgd tester当前支持shell命令如下所示
+
+| 序号 | finish命令                         | 说明                                                         |
+| ---- | ---------------------------------- | ------------------------------------------------------------ |
+| 0    | lgd                                | 显示lgd当前支持shell命令                                     |
+| 1    | lgd probe                          | 测试LGD设备(SPI)访问是否正常,读取芯片版本号与芯片固化的EUI  |
+| 2    | lgd txc <freq> <power><sf><bw><iq> | 配置LGD发送参数<br><freq>:发射频率,单位Hz<br><power>:发射功率,单位dBm<br><sf>:扩频因子,有效范围7~12<br/><bw>:带宽,有效范围125,250,500,only for LoRa High Speed IF8<br/><iq>:IQ invert, 0 - No, 1 - Yes |
+| 3    | lgd rxc <para> <val>               | 配置LGD接收参数<br/><para>:变参类型,有效值: "rf0"、"rf1"、"auto"<br/>当<para>="rf0" 或者 "rf1",<br/><val>:为radio chain0\1的channel Center Frequence,单位Hz<br/>当<para>设置为"auto",<br/><val>为LGD工作信道组的起始频点,单位Hz<br/>当<para>设置为"iq",<br/><val>:rx iq invert,0-disable invert,1-enable invert<br/> |
+| 4    | lgd mac <lorawan_public>           | 配置MAC参数<br/><lorawan_public> 设置LoRaWAN公网(同步字),0 - No, 1 - Yes, |
+| 5    | lgd cw <freq> <power>              | CW模式,LGD发送载波(CW)<br>\<freq\>:CW频点,单位Hz<br>\<power\>:功率,单位dBm |
+| 6    | lgd ping <nb> <len>                | Device模式,启动ping通信测试(自动初始化sx130x),LGD作为master<br>\<nb\>: 发送数据包个数<br>\<len\>: 发送用户数据长度 |
+| 7    | lgd rx <rx_only>                   | Concentraror模式,LGD启动数据包接收<br><rx_only>: <br>0 - 回发接收到数据包(loopback功能)<br>1 - 仅接收,并本地以16进制格式与ASCII码显示接收到的数据 |
+| 8    | lgd reboot                         | 重新启动sx130x                                               |
+
+# 3 LoRa GW Driver软件包使用说明
+## 3.1 依赖
+
+### 3.1.1 RT-Thread
+
+- RT-Thread 4.0.x
+- spi device
+- i2c device
+- ulog
+
+lora-gw-driver目前主要基于RT-Thread 4.0.3测试验证
+
+### 3.1.2 BSP SPI外设
+
+- SPI外设——用户需根据实际MCU平台,自定义LoRa网关模块实际所需要使用的SPI外设
+   - 选择SPI外设 
+   - 比如在ART-Pi平台,sdk-bsp-stm32h750-realthread-artpi\libraries\Kconfig
+      - ART-Pi + LRS007的RF_A通道(左通道)使用BSP_USING_SPI2
+      - ART-Pi + LRS007的RF_B通道(右通道)使用BSP_USING_SPI4
+```
+menu "On-chip Peripheral"
+    menuconfig BSP_USING_SPI
+        bool "Enable SPI"
+        default n
+        select RT_USING_SPI
+        if BSP_USING_SPI
+            config BSP_USING_SPI1
+                bool "Enable SPI1"
+                default n
+            config BSP_USING_SPI2
+                bool "Enable SPI2"
+                default n
+            config BSP_USING_SPI4
+                bool "Enable SPI4"
+                default n
+        endif
+endmenu
+```
+### 3.1.3 ulog组件
+
+- ulog组件——lgd-tester.c使用ulog接口,用于打印调试信息、原始16进制数据等
+   - 使能ulog
+      - ulog缓存大小设置≥ 1024 Byte
+      - 使能ulog内置过滤功能"Enable runtime log filter"
+        - 可根据需要过滤不同等级的日志
+      - 使能浮点数,用于显示RSSI、SNR
+
+```
+RT-Thread Components --->
+   Utiliess --->
+       [*] Enable ulog 
+       	    (1024) The log's max width.
+       	    log format  --->
+       	        [*] Enable float number support. It will using more thread stack.                                                   [*] Enable color log.
+            [*]   Enable runtime log filter.	
+```
+
+- ulog使用示例: 设置ulog日志过滤等级为Info
+
+```
+msh >ulog_lvl
+Please input: ulog_lvl <level>.
+Assert  : 0
+Error   : 3
+Warning : 4
+Info    : 6
+Debug   : 7
+msh >ulog_lvl 6
+```
+
+
+
+## 3.2 获取软件包
+
+使用 lora-gw-driver-lib软件包,需要在 RT-Thread 的包管理中选中它,具体路径,如下示例以ART-Pi平台为例:
+```c
+RT-Thread online packages --->
+    peripheral libraries and drivers --->
+        [*] lora_gw_driver_lib: lora-gw-driver-lib is lora gateway chip(SX130x) driver binary libraries. --->
+    	     Select LoRa Gateway Chip (SX1302)  ---> 
+    		(lgd1302) Setup LoRa Gw Driver Spi Device Name
+    	    (spi2) Setup LoRa Gw Driver Spi Bus Name (eg:spi1,spi2..,Define BSP_USING_SPIx in [Target Platform]\Board\Kconfig)
+            (i2c3) Setup LoRa Gw Driver I2C Device Name for Temperture(eg:i2c1,i2c2..,Define BSP_USING_IICx in [Target Platform]\Board\Kconfig)
+    		[ ]   Select lora-gw-driver Services
+    	     [*]   Enable lora-gw-driver GPIO Setup
+    			Select Supported Target Borad  --->
+    				  --- ART-Pi and LRS007[LSD4WN-2K730NE0(SX1302)]                                         
+                          [ ]   Select LoRa Gw GPIO by Pin Name (NEW)                                           
+                          [*]   Select LoRa Gw GPIO by Pin Number (NEW)                                         
+                          (128)   LoRa Gw SPI NSS Pin number (NEW)                                             
+                          (15)    LoRa Gw RESET Pin number (NEW)                                                
+                          (126)   LoRa Gw PowerOn Pin number (NEW)                                                
+                          (127)   LoRa Gw PPS Pin number (NEW)                                                
+                          (119)   LoRa Gw GPIO6 Pin number (NEW)
+   		    Select LoRa GW Driver Samples  --->                                           
+             Version (latest)  --->
+```
+
+1. Select LoRa Chip \ LoRa Module
+   1. "Select LoRa Gateway Chip "
+      1. 选择使用的LoRa网关芯片类型
+         - 当前支持 SX1302
+   2. "Setup LoRa Gw Driver Spi Device Name"
+      1. 设置LoRa GW Spi设备名称,缺省为"lgd1302"
+   3. "Setup LoRa Gw Driver Spi Bus Name"
+      1. 设置LoRa Radio Spi总线名称,eg:ART-Pi中使用spi2
+      1. 需在类似 [Target Platform]\Board\Kconfig选择所使用的BSP_USING_SPIx
+   4. "Setup LoRa Gw Driver I2C Device Name for Temperture"
+      1. 设置板载温度传感器I2C总线名称,可用于RSSI校准等,eg:ART-Pi中使用i2c3
+      2. 需在类似 [Target Platform]\Board\Kconfig选择所使用的BSP_USING_IICx
+   5. "Enable lora-gw-driver GPIO Setup"
+      1. 选择lora网关芯片模块,根据实际使用的MCU硬件平台与lora网关芯片模块,配置关联的GPIO引脚等功能选项
+         1. 设定LoRa模块的GPIO口(比如 RESET、NSS、PowerOn...)
+            - " Select LoRa Chip GPIO by Pin Number "
+               - 支持使用引脚号来定义GPIO,比如 输入 15 代表 PA10 
+            - "Select LoRa Chip GPIO by Pin Name"
+               - 支持使用引脚名来定义GPIO,比如 输入 A15 代表引脚GPIOA的PIN15脚 (STM32)
+2.   Enable LoRa GW Driver Tester
+   1. 根据实际情况,可选择测试示例
+
+# 4 应用层调用说明
+
+用户层调用可以参考如下步骤
+
+1. 定义使用射频参数(发送参数与接收参数),接收参数在lgw启动后不可修改,发送参数可动态调整
+```c
+
+#ifndef LGD_RF_CHIAN0_CENTER_FREQ
+#define LGD_RF_CHIAN0_CENTER_FREQ 475.6e6
+#endif 
+
+#ifndef LGD_RF_CHIAN1_CENTER_FREQ
+#define LGD_RF_CHIAN1_CENTER_FREQ 476.4e6
+#endif 
+
+static struct lgw_conf_usr_s lgd_conf_lgw =
+{
+    .lorawan_public = false,/* true for lorawan */
+
+    .rxrf =
+    {
+        {
+            .enable  = true,
+            .freq_hz = LGD_RF_CHIAN0_CENTER_FREQ,
+            .invert_pol = false, /* false for lorawan */    
+        },
+        
+        {
+            .enable  = true,
+            .freq_hz = LGD_RF_CHIAN1_CENTER_FREQ,
+            .invert_pol = false, /* false for lorawan */    
+        },
+    },
+    
+    .rxif = 
+    {        
+        .channel_if_enable =  { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+        .channel_if_rfchain = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
+        
+        .channel_if_freq =
+        { 
+            -300000, /* for IF0 */
+            -100000, /* for IF1 */
+            100000,  /* for IF2 */
+            300000,  /* for IF3 */
+            -300000, /* for IF4 */
+            -100000, /* for IF5 */
+            100000,  /* for IF6 */
+            300000,  /* for IF7 */
+        },
+    },
+    
+    .txrf =
+    {
+        .freq_hz = 476300000, /* 470~510 MHz..*/
+        .rf_power = 22,/* 8~22dBm .. */
+        .datarate = 7, /* SF7~SF12.. */
+
+        .modulation = MOD_LORA,/* lora */
+        .invert_pol = false, /* true for lorawan downlink */
+        .preamble = 8,
+        .no_header = false, 
+        .no_crc = false, /* true for lorawan downlink */
+        .bandwidth = BW_125KHZ,
+        .coderate = CR_LORA_4_5, 
+        
+        .tx_mode = IMMEDIATE, /* or TIMESTAMPED */
+    },
+};
+
+```
+2. 调用lora-gw网关芯片初始化
+```c
+void main(void)
+{
+    /* lora gw chip initialization */
+    lgw_init(&lgw_conf_tst);
+    
+   /* connect, configure and start the LoRa concentrator */
+    status = lgw_start();
+    //.....
+}
+```
+
+3. 数据发送
+
+```c
+/* user data */
+index = 0
+app_data.payload[index++] = 'P';
+app_data.payload[index++] = 'I';
+app_data.payload[index++] = 'N';
+app_data.payload[index++] = 'G';
+//....
+app_data.size = index;
+
+lgw_tx( &lgw_conf_tst.txrf, &app_data );
+```
+4. 数据接收
+
+```c
+ /* fetch N packets */
+nb_pkt = lgw_receive(ARRAY_SIZE(lora_gw_rxpkt), lora_gw_rxpkt);
+```
+
+# 5 使用示例
+## 5.1 硬件测试平台
+当前所使用的硬件测试平台如下所示
+| 序号 | 硬件平台 | MCU | LoRa模块 | 主要用户接口 |
+| :-- | --- | --- | --- | --- |
+| 1 | ART-Pi  | STM32H750XB | [LSD4WN-2K730NE0](http://bbs.lierda.com/forum.php?mod=viewthread&tid=87)<br />[ ( SX1302 )](http://bbs.lierda.com/forum.php?mod=viewthread&tid=87) | <br />- 用户接口定义<br />   - VCC  - 3.3V<br />   - GND<br />   - SCK    - PI1 (SPI2)<br />   - MISO  - PI2 (SPI2)<br />   - MOSI  - PI3 (SPI2)<br />   - NSS    - PI0<br />   - RESET - PA15<br />   - POWER_ON  - PH14<br />   - GPIO6- PA8<br /> |
+## 5.2 Shell测试命令
+若使能 [* ]  Enable LoRa GW Driver Tester ,则可以通过串口shell(finish)命令直接进行lora-gw-driver的相关功能测试
+
+
+### 5.2.1 shell操作示例
+
+lgd test shell当前支持的shell操作如下所示
+
+![lgd-shell-cmds](images/lgd-shell-cmds.gif)
+
+### 5.2.2 Concentrator模式通信示例
+
+运行lora-gw-driver-shell的Concentrator模式(异频模式),双向ping通信测试示例,如下图所示
+
+- slaver 
+  - ART-Pi[STM32H7] +  LRS007[SX1302] ( 图左 lgd rx ) 
+- master
+  - LSD4RF-TEST2002[STM32L4] +  LRS101[SX1268] (图右 lora ping)
+- 发送频点 471.3
+- 接收频点 473.1
+- master分别采用SF7\SF8\SF9\SF10\SF11\SF12 (BW125)与slaver 进行双向PING通信
+
+![lgd-shell-sx1302-concentrator-mode](images/lgd-shell-sx1302-concentrator-mode.gif)
+
+### 5.2.3 Device模式通信示例
+
+运行lora-gw-driver-test-shell的Device模式,双向ping通信测试示例,如下图所示
+
+- master
+  - ART-Pi[STM32H7] + LRS007[SX1302] ( 图左 lgd ping)
+- slaver
+  - LSD4RF-TEST2002[STM32L4] +  LRS101[SX1268] (图右 lora ping -s)
+- 发送频点 471.3 
+- 接收频点 473.1 
+- master使用SF7 BW125与slaver进行双向通信
+
+![lgd-shell-sx1302-device-mode](images/lgd-shell-sx1302-device-mode.gif)
+
+
+# 6 版本更新历史
+
+- V0.3.0 版本 2021-09-10
+
+   - 支持sx1302模块
+      - LSD4WN-2K730NE0 (CN470频段)
+      - LSD4WN-2K830NE0 (EU868频段)
+   - 支持lgd shell命令
+      - 支持单\双向\主\从通信等
+
+# 7 问题和建议
+如果有什么问题或者建议欢迎提交 [Issue](https://github.com/Forest-Rain/lora-radio-driver/issues) 进行讨论。

BIN
docs/images/lgd-function-block.png


BIN
docs/images/lgd-ping-testflow.png


BIN
docs/images/lgd-shell-cmds.gif


BIN
docs/images/lgd-shell-sx1302-concentrator-mode.gif


BIN
docs/images/lgd-shell-sx1302-device-mode.gif


+ 57 - 0
inc/loragw_board.h

@@ -0,0 +1,57 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+  (C)2019 Semtech
+
+Description:
+    platform specific functions to init sx130x spi、gpio...
+ 
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+*/
+
+
+#ifndef _LORA_GW_DRIVER_BOARD_H
+#define _LORA_GW_DRIVER_BOARD_H
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <stdint.h>        /* C99 types*/
+
+//#include "config.h"    /* library configuration options (dynamically generated) */
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
+
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+
+/**
+@brief LoRa concentrator SPI init
+@param None
+@return status of spi init operation (rt_err_t)
+*/
+int lgw_spi_init(void);
+
+/**
+@brief LoRa concentrator IO init
+@param None
+@return None
+*/
+void lgw_io_init(void);
+
+/**
+@brief LoRa concentrator I2C init
+@param None
+@return status of spi init operation (rt_err_t)
+*/
+int lgw_i2c_init(void);
+
+#endif
+
+/* --- EOF ------------------------------------------------------------------ */

+ 157 - 0
inc/loragw_dbg.h

@@ -0,0 +1,157 @@
+/*
+ * loragw_debug.h 
+ */
+#ifndef _LORAGW_DBG_H
+#define _LORAGW_DBG_H
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+//#include "config.h"    /* library configuration options (dynamically generated) */
+
+#include "rtconfig.h"
+
+#ifdef RT_USING_ULOG
+#include <rtdbg.h>
+#include <ulog.h> 
+#endif
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC MACROS -------------------------------------------------------- */
+
+/* Turn on some of these (set to non-zero) to debug LoRa GW Driver */
+
+#ifndef LGD_DBG_USR
+#define LGD_DBG_USR                          0
+#endif
+
+#ifndef LGD_DBG_LORAWAN_MAC
+#define LGD_DBG_LORAWAN_MAC                  0
+#endif
+
+#ifndef LGD_DBG_HAL
+#define LGD_DBG_HAL                          0
+#endif
+
+#ifndef LGD_DBG_TEMPERTURE
+#define LGD_DBG_TEMPERTURE                   0
+#endif
+
+#ifndef LGD_DBG_TEMPERTURE_TST
+#define LGD_DBG_TEMPERTURE_TST               0
+#endif
+
+#ifndef LGD_DBG_SX1302
+#define LGD_DBG_SX1302                       0
+#endif
+
+#ifndef LGD_DBG_REG
+#define LGD_DBG_REG                          0
+#endif
+
+#ifndef LGD_DBG_SPI_TST
+#define LGD_DBG_SPI_TST                      0
+#endif
+
+#ifndef LGD_DBG_SPI
+#define LGD_DBG_SPI                          0
+#endif
+
+#ifndef LGD_DBG_SPI_TST
+#define LGD_DBG_SPI_TST                      0
+#endif
+
+#ifndef LGD_DBG_I2C
+#define LGD_DBG_I2C                          0
+#endif
+
+#if defined RT_USING_ULOG
+#define LGD_DEBUG_LOG(type, level, ...)                                \
+do                                                                            \
+{                                                                             \
+    if (type)                                                                 \
+    {                                                                         \
+        ulog_output(level, LOG_TAG, RT_TRUE, __VA_ARGS__);                    \
+    }                                                                         \
+}                                                                             \
+while (0)
+
+
+#define LGD_DEBUG_LOG_RAW(type, ...)                                   \
+do                                                                            \
+{                                                                             \
+    if (type)                                                                 \
+    {                                                                         \
+        ulog_raw(__VA_ARGS__);                                                \
+    }                                                                         \
+}                                                                             \
+while (0)
+
+#define LGD_DEBUG_LOG_HEXDUMP(type, buf, size)                \
+do                                                                            \
+{                                                                             \
+    if (type)                                                                 \
+    {                                                                         \
+        ulog_hexdump(LOG_TAG, 16, buf, size);                                 \
+    }                                                                         \
+}                                                                             \
+while (0)
+    
+#else
+
+#define LGD_DEBUG_LOG(type, level, ...)                                \
+do                                                                            \
+{                                                                             \
+    if (type)                                                                 \
+    {                                                                         \
+        rt_kprintf(__VA_ARGS__);                                              \
+        rt_kprintf("\r\n");                                                   \
+    }                                                                         \
+}                                                                             \
+while (0)
+
+#define LGD_DEBUG_LOG_HEXDUMP(type, buf, size) 
+
+#endif
+
+#else /* LGD */
+
+#define LGD_DEBUG_LOG(type, level, ...)
+
+#endif /* LGD */
+
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+
+/**
+@brief
+@param
+*/
+//void dbg_log_buffer_to_file(FILE * file, uint8_t * buffer, uint16_t size);
+
+/**
+@brief
+@param
+*/
+//void dbg_log_payload_diff_to_file(FILE * file, uint8_t * buffer1, uint8_t * buffer2, uint16_t size);
+
+/**
+@brief
+@param
+*/
+void dbg_init_random(void);
+
+/**
+@brief
+@param
+*/
+void dbg_generate_random_payload(uint32_t pkt_cnt, uint8_t * buffer_expected, uint8_t size);
+
+/**
+@brief
+@param
+*/
+//int dbg_check_payload(struct lgw_conf_debug_s * context, FILE * file, uint8_t * payload_received, uint8_t size, uint8_t ref_payload_idx, uint8_t sf);
+
+
+/* --- EOF ------------------------------------------------------------------ */

+ 616 - 0
inc/loragw_hal.h

@@ -0,0 +1,616 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+  (C)2019 Semtech
+
+Description:
+    LoRa concentrator Hardware Abstraction Layer
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+*/
+
+ 
+#ifndef _LORAGW_HAL_H
+#define _LORAGW_HAL_H
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <stdint.h>     /* C99 types */
+#include <stdbool.h>    /* bool type */
+
+//#include "loragw_com.h"
+//#include "config.h"     /* library configuration options (dynamically generated) */
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC MACROS -------------------------------------------------------- */
+
+#define IS_LORA_BW(bw)          ((bw == BW_125KHZ) || (bw == BW_250KHZ) || (bw == BW_500KHZ))
+#define IS_LORA_DR(dr)          ((dr == DR_LORA_SF5) || (dr == DR_LORA_SF6) || (dr == DR_LORA_SF7) || (dr == DR_LORA_SF8) || (dr == DR_LORA_SF9) || (dr == DR_LORA_SF10) || (dr == DR_LORA_SF11) || (dr == DR_LORA_SF12))
+#define IS_LORA_CR(cr)          ((cr == CR_LORA_4_5) || (cr == CR_LORA_4_6) || (cr == CR_LORA_4_7) || (cr == CR_LORA_4_8))
+
+#define IS_FSK_BW(bw)           ((bw >= 1) && (bw <= 7))
+#define IS_FSK_DR(dr)           ((dr >= DR_FSK_MIN) && (dr <= DR_FSK_MAX))
+
+#define IS_TX_MODE(mode)        ((mode == IMMEDIATE) || (mode == TIMESTAMPED) || (mode == ON_GPS))
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
+
+/* return status code */
+#define LGW_HAL_SUCCESS     0
+#define LGW_HAL_ERROR       -1
+#define LGW_LBT_NOT_ALLOWED  1
+
+/* radio-specific parameters */
+#define LGW_XTAL_FREQU      32000000            /* frequency of the RF reference oscillator */
+#define LGW_RF_CHAIN_NB     2                   /* number of RF chains */
+#define LGW_RF_RX_BANDWIDTH {1000000, 1000000}  /* bandwidth of the radios */
+
+/* concentrator chipset-specific parameters */
+/* to use array parameters, declare a local const and use 'if_chain' as index */
+#define LGW_IF_CHAIN_NB     10      /* number of IF+modem RX chains */
+#define LGW_REF_BW          125000  /* typical bandwidth of data channel */
+#define LGW_MULTI_NB        8       /* number of LoRa 'multi SF' chains */
+#define LGW_MULTI_SF_EN     0xFF    /* bitmask to enable/disable SF for multi-sf correlators  (12 11 10 9 8 7 6 5) */
+
+/* values available for the 'modulation' parameters */
+/* NOTE: arbitrary values */
+#define MOD_UNDEFINED   0
+#define MOD_CW          0x08
+#define MOD_LORA        0x10
+#define MOD_FSK         0x20
+
+/* values available for the 'bandwidth' parameters (LoRa & FSK) */
+/* NOTE: directly encode FSK RX bandwidth, do not change */
+#define BW_UNDEFINED    0
+#define BW_500KHZ       0x06
+#define BW_250KHZ       0x05
+#define BW_125KHZ       0x04
+
+/* values available for the 'datarate' parameters */
+/* NOTE: LoRa values used directly to code SF bitmask in 'multi' modem, do not change */
+#define DR_UNDEFINED    0
+#define DR_LORA_SF5     5
+#define DR_LORA_SF6     6
+#define DR_LORA_SF7     7
+#define DR_LORA_SF8     8
+#define DR_LORA_SF9     9
+#define DR_LORA_SF10    10
+#define DR_LORA_SF11    11
+#define DR_LORA_SF12    12
+/* NOTE: for FSK directly use baudrate between 500 bauds and 250 kbauds */
+#define DR_FSK_MIN      500
+#define DR_FSK_MAX      250000
+
+/* values available for the 'coderate' parameters (LoRa only) */
+/* NOTE: arbitrary values */
+#define CR_UNDEFINED    0   /* CR0 exists but is not recommended, so consider it as invalid */
+#define CR_LORA_4_5     0x01
+#define CR_LORA_4_6     0x02
+#define CR_LORA_4_7     0x03
+#define CR_LORA_4_8     0x04
+
+/* values available for the 'status' parameter */
+/* NOTE: values according to hardware specification */
+#define STAT_UNDEFINED  0x00
+#define STAT_NO_CRC     0x01
+#define STAT_CRC_BAD    0x11
+#define STAT_CRC_OK     0x10
+
+/* values available for the 'tx_mode' parameter */
+#define IMMEDIATE       0
+#define TIMESTAMPED     1
+#define ON_GPS          2
+
+/* values available for 'select' in the status function */
+#define TX_STATUS       1
+#define RX_STATUS       2
+
+/* status code for TX_STATUS */
+/* NOTE: arbitrary values */
+#define TX_STATUS_UNKNOWN   0
+#define TX_OFF              1    /* TX modem disabled, it will ignore commands */
+#define TX_FREE             2    /* TX modem is free, ready to receive a command */
+#define TX_SCHEDULED        3    /* TX modem is loaded, ready to send the packet after an event and/or delay */
+#define TX_EMITTING         4    /* TX modem is emitting */
+
+/* status code for RX_STATUS */
+/* NOTE: arbitrary values */
+#define RX_STATUS_UNKNOWN   0
+#define RX_OFF              1    /* RX modem is disabled, it will ignore commands  */
+#define RX_ON               2    /* RX modem is receiving */
+#define RX_SUSPENDED        3    /* RX is suspended while a TX is ongoing */
+
+/* Maximum size of Tx gain LUT */
+#define TX_GAIN_LUT_SIZE_MAX 16
+
+/* Listen-Before-Talk */
+#define LGW_LBT_CHANNEL_NB_MAX 16 /* Maximum number of LBT channels */
+
+/* Spectral Scan */
+#define LGW_SPECTRAL_SCAN_RESULT_SIZE 33 /* The number of results returned by spectral scan function, to be used for memory allocation */
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC TYPES --------------------------------------------------------- */
+
+/**
+@enum lgw_radio_type_t
+@brief Radio types that can be found on the LoRa Gateway
+*/
+typedef enum {
+    LGW_RADIO_TYPE_NONE,
+    LGW_RADIO_TYPE_SX1255,
+    LGW_RADIO_TYPE_SX1257,
+    LGW_RADIO_TYPE_SX1272,
+    LGW_RADIO_TYPE_SX1276,
+    LGW_RADIO_TYPE_SX1250
+} lgw_radio_type_t;
+
+/**
+@struct lgw_conf_board_s
+@brief Configuration structure for board specificities
+*/
+struct lgw_conf_board_s {
+    bool    lorawan_public; /*!> Enable ONLY for *public* networks using the LoRa MAC protocol */
+    uint8_t clksrc;         /*!> Index of RF chain which provides clock to concentrator */
+    bool    full_duplex;    /*!> Indicates if the gateway operates in full duplex mode or not */
+    //char    spidev_path[64];/*!> Path to access the SPI device to connect to the SX1302 */
+    //lgw_com_type_t  com_type;       /*!> The COMmunication interface (SPI/USB) to connect to the SX1302 */
+    //char            com_path[64];   /*!> Path to access the COM device to connect to the SX1302 */
+};
+
+/**
+@struct lgw_rssi_tcomp_s
+@brief Structure containing all coefficients necessary to compute the offset to be applied on RSSI for current temperature
+*/
+struct lgw_rssi_tcomp_s {
+    float coeff_a;
+    float coeff_b;
+    float coeff_c;
+    float coeff_d;
+    float coeff_e;
+};
+
+/**
+@struct lgw_conf_rxrf_s
+@brief Configuration structure for a RF chain
+*/
+struct lgw_conf_rxrf_s {
+    bool                    enable;             /*!> enable or disable that RF chain */
+    uint32_t                freq_hz;            /*!> center frequency of the radio in Hz */
+    float                   rssi_offset;        /*!> Board-specific RSSI correction factor */
+    struct lgw_rssi_tcomp_s rssi_tcomp;         /*!> Board-specific RSSI temperature compensation coefficients */
+    lgw_radio_type_t        type;               /*!> Radio type for that RF chain (SX1255, SX1257....) */
+    bool                    tx_enable;          /*!> enable or disable TX on that RF chain */
+    bool                    single_input_mode;  /*!> Configure the radio in single or differential input mode (SX1250 only) */
+    bool                    invert_pol;         /*!> invert rx signal polarity, for orthogonal uplinks (LoRa only) */
+};
+
+/**
+@struct lgw_conf_rxif_s
+@brief Configuration structure for an IF chain
+*/
+struct lgw_conf_rxif_s {
+    bool        enable;         /*!> enable or disable that IF chain */
+    uint8_t     rf_chain;       /*!> to which RF chain is that IF chain associated */
+    int32_t     freq_hz;        /*!> center frequ of the IF chain, relative to RF chain frequency */
+    uint8_t     bandwidth;      /*!> RX bandwidth, 0 for default */
+    uint32_t    datarate;       /*!> RX datarate, 0 for default */
+    uint8_t     sync_word_size; /*!> size of FSK sync word (number of bytes, 0 for default) */
+    uint64_t    sync_word;      /*!> FSK sync word (ALIGN RIGHT, eg. 0xC194C1) */
+    bool        implicit_hdr;               /*!> LoRa Service implicit header */
+    uint8_t     implicit_payload_length;    /*!> LoRa Service implicit header payload length (number of bytes, 0 for default) */
+    bool        implicit_crc_en;            /*!> LoRa Service implicit header CRC enable */
+    uint8_t     implicit_coderate;          /*!> LoRa Service implicit header coding rate  */
+};
+
+/**
+@struct lgw_conf_demod_s
+@brief Configuration structure for LoRa/FSK demodulators
+*/
+struct lgw_conf_demod_s {
+    uint8_t     multisf_datarate;   /*!> bitmask to enable spreading-factors for correlators (SF12 - SF5) */
+
+};
+/**
+@struct lgw_pkt_rx_s
+@brief Structure containing the metadata of a packet that was received and a pointer to the payload
+*/
+struct lgw_pkt_rx_s {
+    uint32_t    freq_hz;        /*!> central frequency of the IF chain */
+    int32_t     freq_offset;
+    uint8_t     if_chain;       /*!> by which IF chain was packet received */
+    uint8_t     status;         /*!> status of the received packet */
+    uint32_t    count_us;       /*!> internal concentrator counter for timestamping, 1 microsecond resolution */
+    uint8_t     rf_chain;       /*!> through which RF chain the packet was received */
+    uint8_t     modem_id;
+    uint8_t     modulation;     /*!> modulation used by the packet */
+    uint8_t     bandwidth;      /*!> modulation bandwidth (LoRa only) */
+    uint32_t    datarate;       /*!> RX datarate of the packet (SF for LoRa) */
+    uint8_t     coderate;       /*!> error-correcting code of the packet (LoRa only) */
+    float       rssic;          /*!> average RSSI of the channel in dB */
+    float       rssis;          /*!> average RSSI of the signal in dB */
+    float       snr;            /*!> average packet SNR, in dB (LoRa only) */
+    float       snr_min;        /*!> minimum packet SNR, in dB (LoRa only) */
+    float       snr_max;        /*!> maximum packet SNR, in dB (LoRa only) */
+    uint16_t    crc;            /*!> CRC that was received in the payload */
+    uint16_t    size;           /*!> payload size in bytes */
+    uint8_t     payload[256];   /*!> buffer containing the payload */
+    bool        ftime_received; /*!> a fine timestamp has been received */
+    uint32_t    ftime;          /*!> packet fine timestamp (nanoseconds since last PPS) */
+};
+
+/**
+@struct lgw_pkt_tx_s
+@brief Structure containing the configuration of a packet to send and a pointer to the payload
+*/
+struct lgw_pkt_tx_s {
+    uint32_t    freq_hz;        /*!> center frequency of TX */
+    uint8_t     tx_mode;        /*!> select on what event/time the TX is triggered */
+    uint32_t    count_us;       /*!> timestamp or delay in microseconds for TX trigger */
+    uint8_t     rf_chain;       /*!> through which RF chain will the packet be sent */
+    int8_t      rf_power;       /*!> TX power, in dBm */
+    uint8_t     modulation;     /*!> modulation to use for the packet */
+    int8_t      freq_offset;    /*!> frequency offset from Radio Tx frequency (CW mode) */
+    uint8_t     bandwidth;      /*!> modulation bandwidth (LoRa only) */
+    uint32_t    datarate;       /*!> TX datarate (baudrate for FSK, SF for LoRa) */
+    uint8_t     coderate;       /*!> error-correcting code of the packet (LoRa only) */
+    bool        invert_pol;     /*!> invert signal polarity, for orthogonal downlinks (LoRa only) */
+    uint8_t     f_dev;          /*!> frequency deviation, in kHz (FSK only) */
+    uint16_t    preamble;       /*!> set the preamble length, 0 for default */
+    bool        no_crc;         /*!> if true, do not send a CRC in the packet */
+    bool        no_header;      /*!> if true, enable implicit header mode (LoRa), fixed length (FSK) */
+    uint16_t    size;           /*!> payload size in bytes */
+    uint8_t     payload[256];   /*!> buffer containing the payload */
+};
+
+/**
+@struct lgw_tx_gain_s
+@brief Structure containing all gains of Tx chain
+*/
+struct lgw_tx_gain_s {
+    int8_t  rf_power;   /*!> measured TX power at the board connector, in dBm */
+    uint8_t dig_gain;   /*!> (sx125x) 2 bits: control of the digital gain of SX1302 */
+    uint8_t pa_gain;    /*!> (sx125x) 2 bits: control of the external PA (SX1302 I/O)
+                             (sx1250) 1 bits: enable/disable the external PA (SX1302 I/O) */
+    uint8_t dac_gain;   /*!> (sx125x) 2 bits: control of the radio DAC */
+    uint8_t mix_gain;   /*!> (sx125x) 4 bits: control of the radio mixer */
+    int8_t offset_i;    /*!> (sx125x) calibrated I offset */
+    int8_t offset_q;    /*!> (sx125x) calibrated Q offset */
+    uint8_t pwr_idx;    /*!> (sx1250) 6 bits: control the radio power index to be used for configuration */
+};
+
+
+/**
+@struct lgw_tx_gain_lut_s
+@brief Structure defining the Tx gain LUT
+*/
+struct lgw_tx_gain_lut_s {
+    struct lgw_tx_gain_s    lut[TX_GAIN_LUT_SIZE_MAX];  /*!> Array of Tx gain struct */
+    uint8_t                 size;                       /*!> Number of LUT indexes */
+};
+
+/**
+@struct lgw_conf_debug_s
+@brief Configuration structure for debug
+*/
+struct conf_ref_payload_s {
+    uint32_t id;
+    uint8_t payload[255];
+    uint32_t prev_cnt;
+};
+struct lgw_conf_debug_s {
+    uint8_t                     nb_ref_payload;
+    struct conf_ref_payload_s   ref_payload[16];
+    char log_file_name[128];
+};
+
+/**
+@enum lgw_ftime_mode_t
+@brief Fine timestamping modes
+*/
+typedef enum {
+    LGW_FTIME_MODE_HIGH_CAPACITY,   /*!> fine timestamps for SF5 -> SF10 */
+    LGW_FTIME_MODE_ALL_SF           /*!> fine timestamps for SF5 -> SF12 */
+} lgw_ftime_mode_t;
+
+
+/**
+@struct lgw_conf_ftime_s
+@brief Configuration structure for fine timestamping
+*/
+struct lgw_conf_ftime_s {
+    bool enable;              /*!> Enable / Disable fine timestamping */
+    lgw_ftime_mode_t mode;    /*!> Fine timestamping mode */
+};
+
+/**
+@enum lgw_lbt_scan_time_t
+@brief Radio types that can be found on the LoRa Gateway
+*/
+typedef enum {
+    LGW_LBT_SCAN_TIME_128_US    = 128,
+    LGW_LBT_SCAN_TIME_5000_US   = 5000,
+} lgw_lbt_scan_time_t;
+
+/**
+@brief Structure containing a Listen-Before-Talk channel configuration
+*/
+struct lgw_conf_chan_lbt_s{
+    uint32_t            freq_hz;           /*!> LBT channel frequency */
+    uint8_t             bandwidth;         /*!> LBT channel bandwidth */
+    lgw_lbt_scan_time_t scan_time_us;      /*!> LBT channel carrier sense time */
+    uint16_t            transmit_time_ms;  /*!> LBT channel transmission duration when allowed */
+};
+
+/**
+@struct lgw_conf_lbt_s
+@brief Configuration structure for listen-before-talk
+*/
+struct lgw_conf_lbt_s {
+    bool                        enable;             /*!> enable or disable LBT */
+    int8_t                      rssi_target;        /*!> RSSI threshold to detect if channel is busy or not (dBm) */
+    uint8_t                     nb_channel;         /*!> number of LBT channels */
+    struct lgw_conf_chan_lbt_s  channels[LGW_LBT_CHANNEL_NB_MAX];  /*!> LBT channels configuration */
+};
+
+/**
+@struct lgw_conf_sx1261_s
+@brief Configuration structure for additional SX1261 radio used for LBT and Spectral Scan
+*/
+struct lgw_conf_sx1261_s {
+    bool                        enable;             /*!> enable or disable SX1261 radio */
+    char                        spi_path[64];       /*!> Path to access the SPI device to connect to the SX1261 (not used for USB com type) */
+    int8_t                      rssi_offset;        /*!> value to be applied to the sx1261 RSSI value (dBm) */
+    struct lgw_conf_lbt_s       lbt_conf;           /*!> listen-before-talk configuration */
+
+};
+/**
+@struct lgw_context_s
+@brief Configuration context shared across modules
+*/
+typedef struct lgw_context_s {
+    /* Global context */
+    bool                        is_started;
+    struct lgw_conf_board_s     board_cfg;
+    /* RX context */
+    struct lgw_conf_rxrf_s      rf_chain_cfg[LGW_RF_CHAIN_NB];
+    struct lgw_conf_rxif_s      if_chain_cfg[LGW_IF_CHAIN_NB];
+	struct lgw_conf_demod_s     demod_cfg;
+    struct lgw_conf_rxif_s      lora_service_cfg;                       /* LoRa service channel config parameters */
+    struct lgw_conf_rxif_s      fsk_cfg;                                /* FSK channel config parameters */
+    /* TX context */
+    struct lgw_tx_gain_lut_s    tx_gain_lut[LGW_RF_CHAIN_NB];
+    /* Misc */
+    struct lgw_conf_ftime_s     ftime_cfg;
+	struct lgw_conf_sx1261_s    sx1261_cfg;
+    /* Debug */
+    struct lgw_conf_debug_s     debug_cfg;
+} lgw_context_t;
+/**
+@struct lgw_spectral_scan_status_t
+@brief Spectral Scan status
+*/
+typedef enum lgw_spectral_scan_status_e {
+    LGW_SPECTRAL_SCAN_STATUS_NONE,
+    LGW_SPECTRAL_SCAN_STATUS_ON_GOING,
+    LGW_SPECTRAL_SCAN_STATUS_ABORTED,
+    LGW_SPECTRAL_SCAN_STATUS_COMPLETED,
+    LGW_SPECTRAL_SCAN_STATUS_UNKNOWN
+} lgw_spectral_scan_status_t;
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+
+/**
+@brief Configure the gateway board
+@param conf structure containing the configuration parameters
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_board_setconf(struct lgw_conf_board_s * conf);
+
+/**
+@brief Configure an RF chain (must configure before start)
+@param rf_chain number of the RF chain to configure [0, LGW_RF_CHAIN_NB - 1]
+@param conf structure containing the configuration parameters
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_rxrf_setconf(uint8_t rf_chain, struct lgw_conf_rxrf_s * conf);
+
+/**
+@brief Configure an IF chain + modem (must configure before start)
+@param if_chain number of the IF chain + modem to configure [0, LGW_IF_CHAIN_NB - 1]
+@param conf structure containing the configuration parameters
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_rxif_setconf(uint8_t if_chain, struct lgw_conf_rxif_s * conf);
+
+/**
+@brief Get an IF chain + modem (must configure before start)
+@param if_chain number of the IF chain + modem to configure [0, LGW_IF_CHAIN_NB - 1]
+@return conf structure containing the configuration parameters
+*/
+struct lgw_conf_rxif_s* lgw_rxif_getconf(uint8_t if_chain);
+ 
+/**
+@brief Configure LoRa/FSK demodulators
+@param conf structure containing the configuration parameters
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_demod_setconf(struct lgw_conf_demod_s * conf);
+/**
+@brief Configure the Tx gain LUT
+@param pointer to structure defining the LUT
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_txgain_setconf(uint8_t rf_chain, struct lgw_tx_gain_lut_s * conf);
+
+/**
+@brief Configure the fine timestamping
+@param conf pointer to structure defining the config to be applied
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_ftime_setconf(struct lgw_conf_ftime_s * conf);
+/*
+@brief Configure the SX1261 radio for LBT/Spectral Scan
+@param pointer to structure defining the config to be applied
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+
+int lgw_sx1261_setconf(struct lgw_conf_sx1261_s * conf);
+
+/**
+@brief Configure the debug context
+@param pointer to structure defining the config to be applied
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_debug_setconf(struct lgw_conf_debug_s * conf);
+
+/**
+@brief Connect to the LoRa concentrator, reset it and configure it according to previously set parameters
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_start(void);
+
+/**
+@brief Stop the LoRa concentrator and disconnect it
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_stop(void);
+
+/**
+@brief A non-blocking function that will fetch up to 'max_pkt' packets from the LoRa concentrator FIFO and data buffer
+@param max_pkt maximum number of packet that must be retrieved (equal to the size of the array of struct)
+@param pkt_data pointer to an array of struct that will receive the packet metadata and payload pointers
+@return LGW_HAL_ERROR id the operation failed, else the number of packets retrieved
+*/
+int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s * pkt_data);
+
+/**
+@brief Schedule a packet to be send immediately or after a delay depending on tx_mode
+@param pkt_data structure containing the data and metadata for the packet to send
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+
+/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog
+circuitry to start and be stable. This delay is adjusted by the HAL depending
+on the board version (lgw_i_tx_start_delay_us).
+
+In 'timestamp' mode, this is transparent: the modem is started
+lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is
+reached, the preamble of the packet start right when the internal timestamp
+counter reach target value.
+
+In 'immediate' mode, the packet is emitted as soon as possible: transferring the
+packet (and its parameters) from the host to the concentrator takes some time,
+then there is the lgw_i_tx_start_delay_us, then the packet is emitted.
+
+In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
+emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the
+trigger signal. Because there is no way to anticipate the triggering event and
+start the analog circuitry beforehand, that delay must be taken into account in
+the protocol.
+*/
+int lgw_send(struct lgw_pkt_tx_s * pkt_data);
+
+/**
+@brief Give the the status of different part of the LoRa concentrator
+@param select is used to select what status we want to know
+@param code is used to return the status code
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_status(uint8_t rf_chain, uint8_t select, uint8_t * code);
+
+/**
+@brief Abort a currently scheduled or ongoing TX
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_abort_tx(uint8_t rf_chain);
+
+/**
+@brief Return the Tx gain LUT
+@param rf_chain number of the RF chain to configure [0, LGW_RF_CHAIN_NB - 1]
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+extern struct lgw_tx_gain_lut_s * lgw_txgain_getconf(uint8_t rf_chain);
+
+/**
+@brief Return value of internal counter when latest event (eg GPS pulse) was captured
+@param trig_cnt_us pointer to receive timestamp value
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_get_trigcnt(uint32_t * trig_cnt_us);
+
+/**
+@brief Return instateneous value of internal counter
+@param inst_cnt_us pointer to receive timestamp value
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_get_instcnt(uint32_t * inst_cnt_us);
+
+/**
+@brief Return the LoRa concentrator EUI
+@param eui pointer to receive eui
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_get_eui(uint64_t * eui);
+
+/**
+@brief Return the temperature measured by the LoRa concentrator sensor
+@param temperature The temperature measured, in degree celcius
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_get_temperature(float * temperature);
+
+/**
+@brief Allow user to check the version/options of the library once compiled
+@return pointer on a human-readable null terminated string
+*/
+const char* lgw_version_info(void);
+
+/**
+@brief Return time on air of given packet, in milliseconds
+@param packet is a pointer to the packet structure
+@return the packet time on air in milliseconds
+*/
+uint32_t lgw_time_on_air(const struct lgw_pkt_tx_s * packet);
+
+/**
+@brief Start scaning the channel centered on the given frequency
+@param freq_hz channel center frequency
+@param nb_scan number of measures to be done for the scan
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_spectral_scan_start(uint32_t freq_hz, uint16_t nb_scan);
+
+/**
+@brief Get the current scan status
+@param status a pointer to the returned status
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_spectral_scan_get_status(lgw_spectral_scan_status_t * status);
+
+/**
+@brief Get the channel scan results
+@param levels an array containing the power levels for which the scan results are given
+@param values ar array containing the results of the scan for each power levels
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_spectral_scan_get_results(int16_t levels_dbm[static LGW_SPECTRAL_SCAN_RESULT_SIZE], uint16_t results[static LGW_SPECTRAL_SCAN_RESULT_SIZE]);
+
+/**
+@brief Abort the current scan
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_spectral_scan_abort(void);
+
+/**
+@brief Abort the current scan
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+void lgw_hw_reset(void);
+#endif
+
+/* --- EOF ------------------------------------------------------------------ */

+ 58 - 0
inc/loragw_services.h

@@ -0,0 +1,58 @@
+/*
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+#ifndef __LORAGW_SERVICE_H__
+#define __LORAGW_SERVICE_H__
+
+#include "loragw_hal.h"
+
+#define END_DEVICE_FCNT_MODIFY_IGNORE  0xFFFFFFFF
+
+#define HEX16(X)  X[0],X[1],X[2],X[3],X[4],X[5],X[6],X[7],X[8],X[9],X[10],X[11],X[12],X[13],X[14],X[15]
+#define HEX8(X)   X[0],X[1],X[2],X[3],X[4],X[5],X[6],X[7]
+
+typedef struct dev_id_info_tag
+{
+    uint8_t data[8];
+    uint8_t size;
+}dev_id_info_t;
+
+/**
+@brief parse lora/lorawan frame and pack to pcap frame
+@param pkt_data pointer to an array of struct that will receive the packet metadata and payload pointers
+@return LGW_HAL_ERROR id the operation failed, else the lorawan packets retrieved
+*/
+int lgw_service_lora_frame_format(struct lgw_pkt_rx_s *packet);
+
+/**
+@brief register a lorawan end device
+@param  pointer to deveui,appeui,appkey for otaa, devaddr,appskey,nwkskey for abp or otaa
+@return LGW_HAL_ERROR id the operation failed, else the lorawan packets retrieved
+*/
+int lgw_service_lorawan_end_device_register(dev_id_info_t *dev_id, uint8_t *attribute1, uint8_t *attribute2);
+  
+/**
+@brief delete a lorawan end device
+@param pointer to deveui(otaa) or devaddr (abp\otaa)
+@return LGW_HAL_ERROR id the operation failed, else the lorawan packets retrieved
+*/  
+int lgw_service_lorawan_end_device_unregister(dev_id_info_t *dev_id);
+        
+/**
+@brief list all registered lorawan end device
+@param none
+@return LGW_HAL_ERROR id the operation failed, else the lorawan packets retrieved
+*/  
+int lgw_service_lorawan_end_devices_info_list(void);   
+
+/**
+@brief modify lorawan end device frame counter(uplink \ downlink)
+@param none
+@return LGW_HAL_ERROR id the operation failed, else the lorawan packets retrieved
+*/  
+int lgw_service_lorawan_end_device_fcnt_modify(dev_id_info_t *dev_id,uint32_t uplink_counter,uint32_t downlink_counter);
+   
+#endif

+ 140 - 0
inc/loragw_usr.h

@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+#ifndef __LORAGW_USR_H__
+#define __LORAGW_USR_H__
+
+#include "loragw_hal.h"
+#include "loragw_services.h"
+
+/* return status code */
+#define LGD_HAL_SUCCESS     0
+#define LGD_HAL_ERROR       -1
+
+
+#ifdef LORA_GW_DRIVER_USING_SX1302_LSD4WN_2K830NE0
+#ifndef LGD_RF_CHIAN0_CENTER_FREQ
+#define LGD_RF_CHIAN0_CENTER_FREQ 868.4e6
+#endif 
+
+#ifndef LGD_RF_CHIAN1_CENTER_FREQ
+#define LGD_RF_CHIAN1_CENTER_FREQ 869.2e6
+#endif 
+#else
+#ifndef LGD_RF_CHIAN0_CENTER_FREQ
+#define LGD_RF_CHIAN0_CENTER_FREQ 475.6e6
+#endif 
+
+#ifndef LGD_RF_CHIAN1_CENTER_FREQ
+#define LGD_RF_CHIAN1_CENTER_FREQ 476.4e6
+#endif 
+#endif
+
+#ifndef LGD_RF_CHIAN0_CENTER_FREQ_OFFSET
+#define LGD_RF_CHIAN0_CENTER_FREQ_OFFSET 300000//300KHz
+#endif 
+
+#ifndef LGD_RF_CHIAN1_CENTER_FREQ_OFFSET
+#define LGD_RF_CHIAN1_CENTER_FREQ_OFFSET 1100000//1.1MHz
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+    
+/**
+@struct lgw_conf_txrf_usr_t
+@brief Configuration structure for a RF chain
+*/
+typedef struct lgw_conf_txrf_usr_s {
+    uint32_t    freq_hz;        /*!> center frequency of TX */
+    uint32_t    count_us;       /*!> timestamp or delay in microseconds for TX trigger */
+    uint8_t     tx_mode;        /*!> select on what event/time the TX is triggered */
+    int8_t      rf_power;       /*!> TX power, in dBm */
+    uint8_t     modulation;     /*!> modulation to use for the packet */
+    uint16_t    preamble;       /*!> set the preamble length, 0 for default */
+    int8_t      freq_offset;    /*!> frequency offset from Radio Tx frequency (CW mode) */
+    uint8_t     bandwidth;      /*!> modulation bandwidth (LoRa only) */
+    uint8_t     coderate;       /*!> error-correcting code of the packet (LoRa only) */
+    uint32_t    datarate;       /*!> TX datarate (baudrate for FSK, SF for LoRa) */
+    uint8_t     f_dev;          /*!> frequency deviation, in kHz (FSK only) */
+    bool        invert_pol;     /*!> invert signal polarity, for orthogonal downlinks (LoRa only) */
+    bool        no_crc;         /*!> if true, do not send a CRC in the packet */
+    bool        no_header;      /*!> if true, enable implicit header mode (LoRa), fixed length (FSK) */
+    bool        external_pa;    /*!> if true, enable external pa */
+}lgw_conf_txrf_usr_t;
+
+/**
+@struct lgw_conf_rxrf_usr_s
+@brief Configuration structure for a RF chain
+*/
+struct lgw_conf_rxrf_usr_s {
+    bool        enable;             /*!> enable or disable that RF chain */
+    uint32_t    freq_hz;            /*!> center frequency of the radio in Hz */  
+    bool        invert_pol;        /*!> invert signal polarity, for orthogonal uplinks (LoRa only) */     
+};
+
+/**
+@struct lgw_conf_rxif_usr_s
+@brief Configuration structure for an IF chain
+*/
+struct lgw_conf_rxif_usr_s {
+    uint8_t  channel_if_enable[LGW_IF_CHAIN_NB];  /*!> channel if enable */ 
+    uint8_t  channel_if_rfchain[LGW_IF_CHAIN_NB]; /*!> channel if frequence in Hz */
+    int32_t channel_if_freq[LGW_IF_CHAIN_NB];     /*!> channel if rfchain in 0~10 */
+    uint32_t channel_if8_datarate;                /*!> RX datarate, 0 for default */
+    uint8_t  channel_if8_bandwidth;               /*!> RX bandwidth, 0 for default */
+};
+
+typedef struct lgw_payload_usr_s {
+    uint16_t    size;           /*!> payload size in bytes */
+    uint8_t     payload[256];   /*!> buffer containing the payload */
+}lgw_payload_usr_t;
+
+typedef struct lgw_conf_usr_s 
+{
+    uint16_t magic;
+
+    bool lorawan_public; /*!> Enable ONLY for *public* networks using the LoRa MAC protocol */
+    
+    /* rx */
+    struct lgw_conf_rxrf_usr_s rxrf[LGW_RF_CHAIN_NB];
+    struct lgw_conf_rxif_usr_s rxif;
+    
+    /* tx */
+    struct lgw_conf_txrf_usr_s txrf;
+    
+}lgw_conf_usr_t;
+#define LGW_CONF_USR_SIZE  sizeof(struct lgw_conf_usr_s)
+    
+/**
+@brief lora gw driver init
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+int lgw_init(lgw_conf_usr_t *conf);
+
+/**
+@brief Schedule a packet to be send immediately or after a delay depending on tx_mode
+@param pkt_data structure containing the data and metadata for the packet to send
+@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
+*/
+extern int lgw_tx(struct lgw_conf_txrf_usr_s *txrf, lgw_payload_usr_t *payload);
+
+/**
+@brief Return time on air of given packet, in milliseconds
+@param txrf is a pointer to the txrf structure
+@param len is payload length 
+@return the packet time on air in milliseconds
+*/
+extern uint32_t lgw_get_toa_ms(struct lgw_conf_txrf_usr_s *txrf, uint16_t len);
+
+/**
+@brief Get the SX1302 chip version
+@param ver  pointerto the memory holding the sx1302 chip version
+@return LGW_REG_SUCCESS if no error, LGW_REG_ERROR otherwise
+*/
+int lgw_get_ver(int32_t* ver);
+   
+#endif

+ 17 - 0
libraries/SConscript

@@ -0,0 +1,17 @@
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = []
+include_path = [cwd +'/../inc']
+libs = []
+libpath = [cwd + '/libs']
+
+if rtconfig.CROSS_TOOL == 'gcc':
+    libs += ['lora_gw_driver_armcm7_0.3.0_gcc'] # don't add lib prefix for rt-thread studio
+elif rtconfig.CROSS_TOOL == 'keil':
+    libs += ['liblora_gw_driver_armcm7_0.3.0_keil']
+
+group = DefineGroup('lora_gw_driver_lib', src, depend = ['PKG_USING_LORA_GW_DRIVER_LIB'], CPPPATH = include_path, LIBS = libs, LIBPATH = libpath)
+
+Return('group')

BIN
libraries/libs/liblora_gw_driver_armcm7_0.3.0_gcc.a


BIN
libraries/libs/liblora_gw_driver_armcm7_0.3.0_keil.lib


+ 10 - 0
ports/SConscript

@@ -0,0 +1,10 @@
+from building import *
+
+src = []
+
+if GetDepend('LORA_GW_DRIVER_USING_LORA_CHIP_SX1302') or GetDepend('LORA_GW_DRIVER_LIB_USING_LORA_CHIP_SX1302'):
+    src += ['stm32_adapter/loragw_board.c']
+
+group = DefineGroup('lora_gw_driver/board', src, depend = [], CPPPATH = [])
+
+Return('group')

+ 126 - 0
ports/stm32_adapter/loragw_board.c

@@ -0,0 +1,126 @@
+/*!
+ * \file      loragw_board.c
+ *
+ * \brief     spi peripheral initlize,it depend on mcu platform.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * \author    Forest-Rain
+ */
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+#include "board.h"
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#ifdef RT_USING_SPI
+#include "drv_spi.h"
+#endif
+#include "loragw_board.h"
+
+#define LOG_TAG "lora.gw.board"
+#include "loragw_dbg.h"
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+/* for STM32 get GPIO fort,eg GPIOA */
+#ifndef GET_GPIO_PORT
+#define GET_GPIO_PORT(pin) (GPIO_TypeDef *)( GPIOA_BASE + (uint32_t) ( pin >> 4 ) * 0x0400UL )
+#endif
+#ifndef GET_GPIO_PIN
+#define GET_GPIO_PIN(pin) (rt_uint16_t)( 1 << ( pin & 0x0F ) ) // for get GPIO_PIN, eg: GPIO_PIN_6
+#endif
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+extern struct rt_spi_device *spi_dev_sx1302; 
+int lgw_spi_init(void)
+{
+    rt_err_t res;
+
+    res = rt_hw_spi_device_attach(LORA_GW_DRIVER_SPI_BUS_NAME, LORA_GW_DRIVER_SPI_DEVICE_NAME, GET_GPIO_PORT(LORA_GW_DRIVER_SPI_NSS_PIN),GET_GPIO_PIN(LORA_GW_DRIVER_SPI_NSS_PIN));
+
+    if (res != RT_EOK)
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SPI, LOG_LVL_DBG,"rt_spi_bus_attach_device!\r\n");
+        return res;
+    }
+    /* 190807 get spi handle */
+    spi_dev_sx1302 = (struct rt_spi_device *) rt_device_find(LORA_GW_DRIVER_SPI_DEVICE_NAME);
+    
+    if (!spi_dev_sx1302)
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SPI, LOG_LVL_DBG,"sx1302 spi run failed! can't find %s device!\n", LORA_GW_DRIVER_SPI_DEVICE_NAME);
+    }
+    else
+    /* config spi */
+    {
+        struct rt_spi_configuration cfg;
+        cfg.data_width = 8;
+
+        cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
+        cfg.max_hz = 8 * 1000 * 1000; /* max 10M */
+
+        res = rt_spi_configure(spi_dev_sx1302, &cfg);
+        if (res != RT_EOK)
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SPI, LOG_LVL_DBG,"rt_spi_configure failed!\r\n");
+            return res;
+        }
+
+        /* take bus and init spi */
+        res = rt_spi_take_bus(spi_dev_sx1302);
+        if (res != RT_EOK)
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SPI, LOG_LVL_DBG,"rt_spi_take_bus failed!\r\n");
+            return res;
+        }
+        
+        res = rt_spi_release_bus(spi_dev_sx1302);
+        
+        if(res != RT_EOK)
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SPI, LOG_LVL_DBG,"rt_spi_release_bus failed!\r\n");
+            return res;
+        }
+    }
+
+    return RT_EOK;
+}
+
+void lgw_io_init(void)
+{
+    // POWER_ON pin for sx1250 power   
+    rt_pin_mode(LORA_GW_DRIVER_POWER_ON_PIN, PIN_MODE_OUTPUT); 
+    // 1 - power on
+    rt_pin_write(LORA_GW_DRIVER_POWER_ON_PIN, 1); 
+    rt_thread_mdelay(100);
+
+    // GPIO6 pin  
+    rt_pin_mode(LORA_GW_DRIVER_GPIO6_PIN, PIN_MODE_INPUT); 
+
+    // PPS pin  
+    rt_pin_mode(LORA_GW_DRIVER_PPS_PIN, PIN_MODE_INPUT); 
+}
+
+extern struct rt_i2c_bus_device *i2c_bus_sx1302_temperture;
+int lgw_i2c_init(void)
+{   
+    i2c_bus_sx1302_temperture = rt_i2c_bus_device_find(LORA_GW_DRIVER_I2C_DEVICE_NAME);
+    if (i2c_bus_sx1302_temperture == RT_NULL)
+    {
+        LGD_DEBUG_LOG(LGD_DBG_I2C, LOG_LVL_DBG,"Failed to find bus %s\n", LORA_GW_DRIVER_I2C_DEVICE_NAME);
+        return -RT_ERROR;
+    }
+    LGD_DEBUG_LOG(LGD_DBG_I2C, LOG_LVL_DBG,"find bus %s OK\n", LORA_GW_DRIVER_I2C_DEVICE_NAME);
+  
+    return RT_EOK;
+}
+
+
+/* --- EOF ------------------------------------------------------------------ */

+ 404 - 0
readme-semtech.md

@@ -0,0 +1,404 @@
+	 / _____)             _              | |
+	( (____  _____ ____ _| |_ _____  ____| |__
+	 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+	 _____) ) ____| | | || |_| ____( (___| | | |
+	(______/|_____)_|_|_| \__)_____)\____)_| |_|
+	  (C)2019 Semtech
+
+LoRa concentrator HAL user manual
+=================================
+
+## 1. Introduction
+
+The LoRa concentrator Hardware Abstraction Layer is a C library that allow you
+to use a Semtech concentrator chip through a reduced number of high level C
+functions to configure the hardware, send and receive packets.
+
+The Semtech LoRa concentrator is a digital multi-channel multi-standard packet
+radio used to send and receive packets wirelessly using LoRa or FSK modulations.
+
+## 2. Components of the library
+
+The library is composed of the following modules:
+
+* loragw_hal
+* loragw_reg
+* loragw_spi
+* loragw_i2c
+* loragw_aux
+* loragw_gps
+* loragw_sx125x
+* loragw_sx1250
+* loragw_sx1302
+* loragw_sx1302_rx
+* loragw_sx1302_timestamp
+* loragw_stts751
+
+The library also contains basic test programs to demonstrate code use and check
+functionality.
+
+### 2.1. loragw_hal
+
+This is the main module and contains the high level functions to configure and
+use the LoRa concentrator:
+
+* lgw_board_setconf, to set the configuration of the concentrator
+* lgw_rxrf_setconf, to set the configuration of the radio channels
+* lgw_rxif_setconf, to set the configuration of the IF+modem channels
+* lgw_txgain_setconf, to set the configuration of the concentrator gain table
+* lgw_start, to apply the set configuration to the hardware and start it
+* lgw_stop, to stop the hardware
+* lgw_receive, to fetch packets if any was received
+* lgw_send, to send a single packet (non-blocking, see warning in usage section)
+* lgw_status, to check when a packet has effectively been sent
+
+For an standard application, include only this module.
+The use of this module is detailed on the usage section.
+
+/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog
+circuitry to start and be stable. This delay is adjusted by the HAL depending
+on the board version (lgw_i_tx_start_delay_us).
+
+In 'timestamp' mode, this is transparent: the modem is started
+lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is
+reached, the preamble of the packet start right when the internal timestamp
+counter reach target value.
+
+In 'immediate' mode, the packet is emitted as soon as possible: transferring the
+packet (and its parameters) from the host to the concentrator takes some time,
+then there is the lgw_i_tx_start_delay_us, then the packet is emitted.
+
+In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
+emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the
+trigger signal. Because there is no way to anticipate the triggering event and
+start the analog circuitry beforehand, that delay must be taken into account in
+the protocol.
+
+### 2.2. loragw_reg
+
+This module is used to access to the LoRa concentrator registers by name instead
+of by address:
+
+* lgw_connect, to initialise and check the connection with the hardware
+* lgw_disconnect, to disconnect the hardware
+* lgw_reg_r, read a named register
+* lgw_reg_w, write a named register
+* lgw_reg_rb, read a name register in burst
+* lgw_reg_wb, write a named register in burst
+
+This module handles read-only registers protection, multi-byte registers
+management, signed registers management, read-modify-write routines for
+sub-byte registers and read/write burst fragmentation to respect SPI maximum
+burst length constraints.
+
+It make the code much easier to read and to debug.
+Moreover, if registers are relocated between different hardware revisions but
+keep the same function, the code written using register names can be reused "as
+is".
+
+If you need access to all the registers, include this module in your
+application.
+
+**/!\ Warning** please be sure to have a good understanding of the LoRa
+concentrator inner working before accessing the internal registers directly.
+
+### 2.3. loragw_spi
+
+This module contains the functions to access the LoRa concentrator register
+array through the SPI interface:
+
+* lgw_spi_r to read one byte
+* lgw_spi_w to write one byte
+* lgw_spi_rb to read two bytes or more
+* lgw_spi_wb to write two bytes or more
+
+Please *do not* include that module directly into your application.
+
+**/!\ Warning** Accessing the LoRa concentrator register array without the
+checks and safety provided by the functions in loragw_reg is not recommended.
+
+### 2.4. loragw_aux
+
+This module contains a single host-dependant function wait_ms to pause for a
+defined amount of milliseconds.
+
+The procedure to start and configure the LoRa concentrator hardware contained in
+the loragw_hal module requires to wait for several milliseconds at certain
+steps, typically to allow for supply voltages or clocks to stabilize after been
+switched on.
+
+An accuracy of 1 ms or less is ideal.
+If your system does not allow that level of accuracy, make sure that the actual
+delay is *longer* that the time specified when the function is called (ie.
+wait_ms(X) **MUST NOT** before X milliseconds under any circumstance).
+
+If the minimum delays are not guaranteed during the configuration and start
+procedure, the hardware might not work at nominal performance.
+Most likely, it will not work at all.
+
+### 2.5. loragw_gps
+
+This module contains functions to synchronize the concentrator internal
+counter with an absolute time reference, in our case a GPS satellite receiver.
+
+The internal concentrator counter is used to timestamp incoming packets and to
+triggers outgoing packets with a microsecond accuracy.
+In some cases, it might be useful to be able to transform that internal
+timestamp (that is independent for each concentrator running in a typical
+networked system) into an absolute GPS time.
+
+In a typical implementation a GPS specific thread will be called, doing the
+following things after opening the serial port:
+
+* blocking reads on the serial port (using system read() function)
+* parse UBX messages (using lgw_parse_ubx) to get actual native GPS time
+* parse NMEA sentences (using lgw_parse_nmea) to get location and UTC time
+Note: the RMC sentence gives UTC time, not native GPS time.
+
+And each time an NAV-TIMEGPS UBX message has been received:
+
+* get the concentrator timestamp (using lgw_get_trigcnt, mutex needed to
+  protect access to the concentrator)
+* get the GPS time contained in the UBX message (using lgw_gps_get)
+* call the lgw_gps_sync function (use mutex to protect the time reference that
+  should be a global shared variable).
+
+Then, in other threads, you can simply used that continuously adjusted time
+reference to convert internal timestamps to GPS time (using lgw_cnt2gps) or
+the other way around (using lgw_gps2cnt). Inernal concentrator timestamp can
+also be converted to/from UTC time using lgw_cnt2utc/lgw_utc2cnt functions.
+
+### 2.6. loragw_sx125x
+
+This module contains functions to handle the configuration of SX1255 and
+SX1257 radios.
+
+### 2.7. loragw_sx1250
+
+This module contains functions to handle the configuration of SX1250 radios.
+
+### 2.8. loragw_sx1302
+
+This module contains functions to abstract SX1302 concentrator capabilities.
+
+### 2.9. loragw_sx1302_rx
+
+This module is a sub-module of the loragw_sx1302 module focusing on abstracting
+the RX buffer of the SX1302.
+
+### 2.10. loragw_sx1302_timestamp
+
+This module is a sub-module of the loragw_sx1302 module focusing on abstracting
+the timestamp counter of the SX1302.
+It converts the 32-bits 32MHz internal counter of the SX1302 to a 32-bits 1MHz
+counter.
+This module needs to be called regularly by upper layers to maintain counter
+wrapping when converting from 32MHz to 1MHz.
+It also provides function to add correction to the timestamp counter to take
+into account the LoRa demodulation processing time.
+
+### 2.11. loragw_stts751
+
+This module contains a very basic driver for the STmicroelectronics ST751
+temeprature sensor which is on the CoreCell reference design.
+
+### 2.12. loragw_i2c
+
+This module provides basic function to communicate with I2C devices on the board.
+It is used in this project for accessing the temperature sensor.
+
+## 3. Software build process
+
+### 3.1. Details of the software
+
+The library is written following ANSI C conventions but using C99 explicit
+length data type for all data exchanges with hardware and for parameters.
+
+The loragw_aux module contains POSIX dependant functions for millisecond
+accuracy pause.
+For embedded platforms, the function could be rewritten using hardware timers.
+
+### 3.2. Building options
+
+All modules use a fprintf(stderr,...) function to display debug diagnostic
+messages if the DEBUG_xxx is set to 1 in library.cfg
+
+### 3.3. Building procedures
+
+For cross-compilation set the ARCH and CROSS_COMPILE variables in the Makefile,
+or in your shell environment, with the correct toolchain name and path.
+ex:
+export PATH=/home/foo/rpi-toolchain/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH
+export ARCH=arm
+export CROSS_COMPILE=arm-linux-gnueabihf-
+
+The Makefile in the libloragw directory will parse the library.cfg file and
+generate a config.h C header file containing #define options.
+Those options enables and disables sections of code in the loragw_xxx.h files
+and the *.c source files.
+
+The library.cfg is also used directly to select the proper set of dynamic
+libraries to be linked with.
+
+### 3.4. Export
+
+Once build, to use that library on another system, you need to export the
+following files :
+
+* libloragw/library.cfg  -> root configuration file
+* libloragw/libloragw.a  -> static library, to be linked with a program
+* libloragw/readme.md  -> required for license compliance
+* libloragw/inc/config.h  -> C configuration flags, derived from library.cfg
+* libloragw/inc/loragw_*.h  -> take only the ones you need (eg. _hal and _gps)
+
+After statically linking the library to your application, only the license
+is required to be kept or copied inside your program documentation.
+
+## 4. Hardware dependencies
+
+### 4.1. Hardware revision
+
+The loragw_reg and loragw_hal are written for a specific version on the Semtech
+hardware (IP and/or silicon revision).
+
+This code has been written for:
+
+* Semtech SX1302 chip
+* Semtech SX1250, SX1257 or SX1255 I/Q transceivers
+
+The library will not work if there is a mismatch between the hardware version
+and the library version. You can use the test program test_loragw_reg to check
+if the hardware registers match their software declaration.
+
+### 4.2. SPI communication
+
+loragw_spi contains 4 SPI functions (read, write, burst read, burst write) that
+are platform-dependant.
+The functions must be rewritten depending on the SPI bridge you use:
+
+* SPI master matched to the Linux SPI device driver (provided)
+* SPI over USB using FTDI components (not provided)
+* native SPI using a microcontroller peripheral (not provided)
+
+You can use the test program test_loragw_spi to check with a logic analyser
+that the SPI communication is working
+
+### 4.3. GPS receiver (or other GNSS system)
+
+To use the GPS module of the library, the host must be connected to a GPS
+receiver via a serial link (or an equivalent receiver using a different
+satellite constellation).
+The serial link must appear as a "tty" device in the /dev/ directory, and the
+user launching the program must have the proper system rights to read and
+write on that device.
+Use `chmod a+rw` to allow all users to access that specific tty device, or use
+sudo to run all your programs (eg. `sudo ./test_loragw_gps`).
+
+In the current revision, the library only reads data from the serial port,
+expecting to receive NMEA frames that are generally sent by GPS receivers as
+soon as they are powered up, and UBX messages which are proprietary to u-blox
+modules.
+
+The GPS receiver **MUST** send UBX messages shortly after sending a PPS pulse
+on to allow internal concentrator timestamps to be converted to absolute GPS time.
+If the GPS receiver sends a GGA NMEA sentence, the gateway 3D position will
+also be available.
+
+## 5. Usage
+
+### 5.1. Setting the software environment
+
+For a typical application you need to:
+
+* include loragw_hal.h in your program source
+* link to the libloragw.a static library during compilation
+* link to the librt library due to loragw_aux dependencies (timing functions)
+
+For an application that will also access the concentrator configuration
+registers directly (eg. for advanced configuration) you also need to:
+
+* include loragw_reg.h in your program source
+
+### 5.2. Using the software API
+
+To use the HAL in your application, you must follow some basic rules:
+
+* configure the radios path and IF+modem path before starting the radio
+* the configuration is only transferred to hardware when you call the *start*
+  function
+* you cannot receive packets until one (or +) radio is enabled AND one (or +)
+  IF+modem part is enabled AND the concentrator is started
+* you cannot send packets until one (or +) radio is enabled AND the concentrator
+  is started
+* you must stop the concentrator before changing the configuration
+
+A typical application flow for using the HAL is the following:
+
+	<configure the radios and IF+modems>
+	<start the LoRa concentrator>
+	loop {
+		<fetch packets that were received by the concentrator>
+		<process, store and/or forward received packets>
+		<send packets through the concentrator>
+	}
+	<stop the concentrator>
+
+**/!\ Warning** The lgw_send function is non-blocking and returns while the
+LoRa concentrator is still sending the packet, or even before the packet has
+started to be transmitted if the packet is triggered on a future event.
+While a packet is emitted, no packet can be received (limitation intrinsic to
+most radio frequency systems).
+
+Your application *must* take into account the time it takes to send a packet or
+check the status (using lgw_status) before attempting to send another packet.
+
+Trying to send a packet while the previous packet has not finished being send
+will result in the previous packet not being sent or being sent only partially
+(resulting in a CRC error in the receiver).
+
+### 5.3. Debugging mode
+
+To debug your application, it might help to compile the loragw_hal function
+with the debug messages activated (set DEBUG_HAL=1 in library.cfg).
+It then send a lot of details, including detailed error messages to *stderr*.
+
+## 6. Notes
+
+### 6.1. Spreading factor SF5 & SF6
+
+The sx1302 supports SF5 and SF6 spreading factors, and the HAL also. But it is
+important to note that the only syncword supported for SF5 and SF6 is 0x12
+(also known as "private").
+
+This is true whatever how of the "lorawan_public" field of lgw_conf_board_s is
+set.
+
+## 7. License
+
+Copyright (c) 2019, SEMTECH S.A.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+* Neither the name of the Semtech corporation nor the
+  names of its contributors may be used to endorse or promote products
+  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*EOF*

+ 6 - 0
readme.md

@@ -0,0 +1,6 @@
+# lora-gw-driver-lib软件包 简介
+lora-gw-driver软件包(以下简称lgd)是基于RTOS( RT-Thread ) 实现的LoRa网关芯片(SPI)的驱动文件,当前支持SX1302芯片,可以用于创建基于lora网关芯片SX130x的多通道自组网LoRa集中器、LoRa\LoRaWAN网关、多通道LoRa Sniffer工具等。
+
+注:当前lora-gw-driver软件包的SX130x驱动部分,对外版本以二进制lib库形式(lora-gw-driver-lib)提供。
+
+更多详细信息请查看[lora-gw-driver-lib/doc](https://github.com/Forest-Rain/lora-gw-driver-lib/tree/master/docs)

+ 13 - 0
samples/SConscript

@@ -0,0 +1,13 @@
+from building import *
+
+src   = []
+cwd   = GetCurrentDir()
+include_path = [cwd]
+
+if GetDepend('LORA_GW_DRIVER_USING_LGD_TESTER') or GetDepend('LORA_GW_DRIVER_LIB_USING_LGD_TESTER'):
+    src += Glob('lgd_tester/lgd_tester.c')
+
+
+group = DefineGroup('lora_gw_driver/sample', src, depend = [''], CPPPATH = include_path)
+
+Return('group')

+ 1007 - 0
samples/lgd_tester/lgd_tester.c

@@ -0,0 +1,1007 @@
+/*!
+ * \file      lgd-test-shell.c
+ *
+ * \brief     lgd(lora gw driver) shell for test implementation
+ *
+ * \copyright SPDX-License-Identifier: Apache-2.0
+ *
+ * \author    Forest-Rain
+ */
+#include <stdlib.h>
+#include <inttypes.h>       /* PRIx64, PRIu64... */
+#include <stdio.h>          /* fprintf, snprintf, fopen, fputs */
+
+#include "rtthread.h"
+#include "loragw_usr.h"
+#include "lgd_tester.h"
+
+#define LOG_TAG "lora.gw.shell"
+#include "loragw_dbg.h"
+
+#define TX_LED_TOGGLE
+#define TX_LED_ON
+#define TX_LED_OFF
+
+#define RX_LED_TOGGLE
+#define RX_LED_ON
+#define RX_LED_OFF
+
+#define LGD_TIMESTAMP_TO_MS(x) ((x) * 1000 / RT_TICK_PER_SECOND)
+#define LGD_TEST_FETCH_RX_BUFFER_INTERVAL 50 //50ms 1tick @ 1ms
+
+#define DST_ADDRESS_OFFSET 5
+
+const static uint8_t PingMsg[] = "PING";
+
+static float rssic_value = -255;
+static float rssis_value = -255;
+
+static float snr_value_avg = -128;
+static float snr_value_min = -128;
+static float snr_value_max = -128;
+
+static uint32_t master_address = LORA_MASTER_DEVADDR;
+static uint32_t slaver_address = LORA_SLAVER_DEVADDR;
+static uint32_t payload_len = 32;
+
+static uint32_t tx_seq_cnt = 0;
+static uint16_t max_tx_nbtrials = 10;
+static uint32_t rx_correct_cnt = 0;
+static uint32_t rx_error_cnt = 0;
+static uint32_t rx_timeout_cnt = 0;
+
+static uint32_t rx_timestamp;
+static uint32_t tx_timestamp;
+
+static int32_t lgd_tx_frequency_offset = TX_RX_FREQUENCE_OFFSET;
+
+static bool lgd_ack_response_flag = false;
+static bool lgd_tst_initialized = false;
+static bool lgd_rx_only_flag = false;
+
+static uint8_t lgd_test_mode = LGD_TEST_AS_CONCENTRATOR_MODE;
+
+static char *bandwidth_string[3] = {"125", "250", "500"};
+
+/* --- PUBLIC VARIABLES ---------------------------------------------------- */
+static struct lgw_conf_usr_s lgd_conf_lgw =
+{
+    .lorawan_public = false,/* true for lorawan */
+
+    .rxrf =
+    {
+        {
+            .enable  = true,
+            .freq_hz = LGD_RF_CHIAN0_CENTER_FREQ,
+            .invert_pol = false, /* false for lorawan(uplink)*/
+        },
+
+        {
+            .enable  = true,
+            .freq_hz = LGD_RF_CHIAN1_CENTER_FREQ,
+            .invert_pol = false, /* false for lorawan(uplink)*/
+        },
+    },
+
+    .rxif =
+    {
+        .channel_if_enable =  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+        .channel_if_rfchain = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 },
+
+        .channel_if8_datarate  = DR_LORA_SF7, /* only for IF8 */
+        .channel_if8_bandwidth = BW_250KHZ, /* only for IF8 */
+
+        .channel_if_freq =
+        {
+            -300000, /* for IF0 */
+                -100000, /* for IF1 */
+                100000,  /* for IF2 */
+                300000,  /* for IF3 */
+                -300000, /* for IF4 */
+                -100000, /* for IF5 */
+                100000,  /* for IF6 */
+                300000,  /* for IF7 */
+                500000   /* for IF8 */
+            },
+    },
+
+    .txrf =
+    {
+        .freq_hz = 475300000, /* 470~510 MHz */
+        .rf_power = 22,/* 8~22dBm */
+        .datarate = 7, /* SF7~SF12 */
+
+        .modulation = MOD_LORA,/* lora */
+        .invert_pol = false, /* true for lorawan downlink */
+        .preamble = 8,
+        .no_header = false,
+        .no_crc = false, /* true for lorawan downlink */
+        .bandwidth = BW_125KHZ,
+        .coderate = CR_LORA_4_5,
+
+        .tx_mode = IMMEDIATE, /* or TIMESTAMPED */
+    },
+};
+
+static int32_t lgd_tst_sem_main_wait_time = RT_WAITING_FOREVER;
+static int32_t lgd_tst_sem_rx_wait_time = RT_WAITING_FOREVER;
+
+static struct rt_thread lgd_tst_main_thread;
+static rt_uint32_t lgd_tst_main_stack[8192];
+
+static rt_thread_t lgd_tst_rx_thread;
+
+static struct rt_semaphore lgd_tst_sem_main;
+static struct rt_semaphore lgd_tst_sem_rx;
+
+static struct lgw_pkt_rx_s lgd_rxpkt[16]; /* array containing up to 16 inbound packets metadata */
+static struct lgw_pkt_rx_s *lgd_rxpkt_ptr; /* pointer on a RX packet */
+static struct lgw_payload_usr_s lgd_app_data;
+
+static void lgd_tst_main_thread_entry(void* parameter);
+static void lgd_tst_rx_thread_entry(void* parameter);
+
+static int lgd_tst_lgw_init(uint8_t test_mode)
+{
+    int status;
+
+    /* Stop the gateway */
+    status = lgw_stop();
+
+    if (status != LGW_HAL_SUCCESS)
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "ERROR: failed to stop the gateway\n");
+    }
+
+    /* lora gw chip initialization */
+    lgw_init(&lgd_conf_lgw);
+
+    lgd_tst_sem_rx_wait_time = rt_tick_from_millisecond(LGD_TEST_FETCH_RX_BUFFER_INTERVAL);
+
+    /* connect, configure and start the LoRa concentrator */
+    status = lgw_start();
+
+    if (status == LGW_HAL_SUCCESS)
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "*** LoRa GW Driver Test started ***\n");
+    }
+    else
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "*** Impossible to start LoRa GW Driver Test ***\n");
+
+        return -RT_ERROR;
+    }
+
+    if(lgd_tst_rx_thread == RT_NULL)
+    {
+        lgd_tst_rx_thread = rt_thread_create("lgd-rx",
+                                             lgd_tst_rx_thread_entry,
+                                             RT_NULL,
+                                             8192,
+                                             6,
+                                             10);
+
+        if (lgd_tst_rx_thread != RT_NULL)
+        {
+            rt_thread_startup(lgd_tst_rx_thread);
+        }
+        else
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "lora gw test rx thread create failed!\n");
+            return -RT_ERROR;
+        }
+    }
+
+    if(test_mode == LGD_TEST_AS_CONCENTRATOR_MODE)
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Concentrator Mode,Stay to Rx Continuous with start-freq=%d, end-freq=%d, Multi-SF=7~12, BW=%d, Public_Network:%d, IQ_Inversion:%d", lgd_conf_lgw.rxrf[0].freq_hz - 300000,
+                      lgd_conf_lgw.rxrf[1].freq_hz + 300000,
+                      125,
+                      lgd_conf_lgw.lorawan_public,
+                      lgd_conf_lgw.rxrf[0].invert_pol);
+    }
+    return RT_EOK;
+}
+
+bool lgd_tst_main_thread_init(void)
+{
+    if( lgd_tst_main_thread.entry == RT_NULL )
+    {
+        rt_err_t result;
+        result = rt_sem_init(&lgd_tst_sem_main, "lgd_s_m", 0, RT_IPC_FLAG_FIFO);
+
+        if (result != RT_EOK)
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "init lora-gw-main semaphore failed.\n");
+        }
+
+        result = rt_sem_init(&lgd_tst_sem_rx, "lgd_s_r", 0, RT_IPC_FLAG_FIFO);
+
+        if (result != RT_EOK)
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "init lora-gw-rx semaphore failed.\n");
+        }
+
+        result = rt_thread_init(&lgd_tst_main_thread,
+                                "lgd-shell",
+                                lgd_tst_main_thread_entry,
+                                RT_NULL,
+                                &lgd_tst_main_stack[0],
+                                sizeof(lgd_tst_main_stack),
+                                7,
+                                10);
+
+        if (result == RT_EOK)
+        {
+            rt_thread_startup(&lgd_tst_main_thread);
+        }
+        else
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "lora gw test main thread create failed!\n");
+            return false;
+        }
+
+        // wait for first lgw chip init
+        rt_sem_take(&lgd_tst_sem_main, RT_WAITING_FOREVER);
+    }
+
+    return true;
+}
+//INIT_APP_EXPORT(lgd_tst_main_thread_init);
+
+static void send_ping_packet(uint32_t src_addr, uint32_t dst_addr, uint8_t len)
+{
+    int tx_result;
+    // Send the next PING frame
+    uint8_t index = 0;
+
+    tx_seq_cnt++;
+
+    tx_timestamp = rt_tick_get();
+
+    // header
+    lgd_app_data.payload[index++] = 0x00; // echo cmd
+
+    lgd_app_data.payload[index++] = src_addr & 0xFF;
+    lgd_app_data.payload[index++] = src_addr >> 8;
+    lgd_app_data.payload[index++] = src_addr >> 16;
+    lgd_app_data.payload[index++] = src_addr >> 24;
+
+    lgd_app_data.payload[index++] = dst_addr & 0xFF;
+    lgd_app_data.payload[index++] = dst_addr >> 8;
+    lgd_app_data.payload[index++] = dst_addr >> 16;
+    lgd_app_data.payload[index++] = dst_addr >> 24;
+
+    lgd_app_data.payload[index++] = tx_seq_cnt & 0xFF;
+    lgd_app_data.payload[index++] = tx_seq_cnt >> 8;
+    lgd_app_data.payload[index++] = tx_seq_cnt >> 16;
+    lgd_app_data.payload[index++] = tx_seq_cnt >> 24;
+
+    /* user data */
+    lgd_app_data.payload[index++] = 'P';
+    lgd_app_data.payload[index++] = 'I';
+    lgd_app_data.payload[index++] = 'N';
+    lgd_app_data.payload[index++] = 'G';
+
+    /* 00,01,02...*/
+    for( uint8_t i = 0; i < len - index ; i++)
+    {
+        lgd_app_data.payload[index + i] = i;
+    }
+
+    lgd_app_data.size = len;
+
+    rt_thread_mdelay(1);
+
+    tx_result = lgw_tx( &lgd_conf_lgw.txrf, &lgd_app_data );
+
+    if( tx_result != LGW_HAL_SUCCESS )
+    {
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "lgd ping(send) fail");
+    }
+}
+
+static void rx_frame_process(struct lgw_pkt_rx_s *pkt)
+{
+    if( lgd_test_mode == LGD_TEST_AS_DEVICE_MODE )
+    {
+        if( pkt->size > 0 )
+        {
+            if( rt_strncmp( ( const char* )pkt->payload + MAC_HEADER_OVERHEAD, ( const char* )PingMsg, 4 ) == 0 )
+            {
+                /* Indicates on a LED that the received frame is a PING ACK packet */
+                RX_LED_TOGGLE;
+                rx_correct_cnt++;
+
+                uint32_t slaver_addr = 0;
+                slaver_addr = pkt->payload[5];
+                slaver_addr |= pkt->payload[6] << 8;
+                slaver_addr |= pkt->payload[7] << 16;
+                slaver_addr |= pkt->payload[8] << 24;
+
+                uint32_t received_seqno = 0;
+                received_seqno = pkt->payload[9];
+                received_seqno |= pkt->payload[10] << 8;
+                received_seqno |= pkt->payload[11] << 16;
+                received_seqno |= pkt->payload[12] << 24;
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Reply from [0x%X]:seqno=%d, bytes=%d,total time=%d ms,rssic=%+6.1f,rssis=%+6.1f,snr=%+d", slaver_addr,
+                              received_seqno,
+                              pkt->size,
+                              LGD_TIMESTAMP_TO_MS( rx_timestamp - tx_timestamp ),
+                              rssic_value, rssis_value, snr_value_avg );
+
+                if( tx_seq_cnt )
+                {
+                    lgd_ack_response_flag = true;
+                    /* trigger to send the next PING frame imeadiately */
+                    rt_sem_release(&lgd_tst_sem_main);
+                }
+
+            }
+            else /* valid reception but neither a PING ACK */
+            {
+                // continuous rx
+            }
+
+            LGD_DEBUG_LOG_HEXDUMP(LGD_DBG_SHELL_TEST,pkt->payload, pkt->size);
+        }
+        else
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "RX ERR:BufferSize = 0");
+            rx_error_cnt++;
+        }
+    }
+    else
+    {
+        /* Concentrator mode */
+        if( pkt->size > 0 )
+        {
+            uint32_t dst_address = pkt->payload[DST_ADDRESS_OFFSET] | \
+                                   ( pkt->payload[DST_ADDRESS_OFFSET + 1] << 8 ) | \
+                                   ( pkt->payload[DST_ADDRESS_OFFSET + 2] << 16 ) | \
+                                   ( pkt->payload[DST_ADDRESS_OFFSET + 3] << 24);
+
+            if( lgd_rx_only_flag == true )
+            {
+                /* Indicates on a LED that the received frame */
+                RX_LED_TOGGLE;
+
+                uint32_t src_addr, dst_addr = 0;
+                src_addr = pkt->payload[1];
+                src_addr |= pkt->payload[2] << 8;
+                src_addr |= pkt->payload[3] << 16;
+                src_addr |= pkt->payload[4] << 24;
+
+                dst_addr = pkt->payload[5];
+                dst_addr |= pkt->payload[6] << 8;
+                dst_addr |= pkt->payload[7] << 16;
+                dst_addr |= pkt->payload[8] << 24;
+
+                rx_correct_cnt++;
+
+                /* RX continuous */
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Received: Totals=%d,bytes=%d,timestamp=%d,rssic=%+6.1f,rssis=%+6.1f,snr=%+d\n", rx_correct_cnt, pkt->size, rx_timestamp, rssic_value, rssis_value, snr_value_avg );
+            }
+            else if( ( dst_address == slaver_address || dst_address == 0xFFFFFFFF ) && \
+                     ( rt_strncmp( ( const char* )pkt->payload + MAC_HEADER_OVERHEAD, ( const char* )PingMsg, 4 ) == 0 ))
+            {
+                /* Indicates on a LED that the received frame is a PING */
+                RX_LED_TOGGLE;
+                /* echo the receive packet to master */
+                {
+                    /* wait for master going to be ready to rx */
+                    rt_thread_mdelay(5);
+                    {
+                        /* send back received packet with the same freq and datarate immediately */
+                        lgd_conf_lgw.txrf.freq_hz = pkt->freq_hz + lgd_tx_frequency_offset;;
+                        lgd_conf_lgw.txrf.datarate = pkt->datarate;
+                        lgd_conf_lgw.txrf.tx_mode = IMMEDIATE;
+
+                        lgd_app_data.size = pkt->size;
+                        rt_memcpy(lgd_app_data.payload, pkt->payload, lgd_app_data.size);
+
+                        int tx_result = lgw_tx( &lgd_conf_lgw.txrf, &lgd_app_data );
+
+                        if( tx_result != LGW_HAL_SUCCESS )
+                        {
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "lgd send back fail");
+                        }
+                    }
+                }
+            }
+            else
+            {
+                /* RX continuous */
+            }
+
+            LGD_DEBUG_LOG_HEXDUMP(LGD_DBG_SHELL_TEST,pkt->payload, pkt->size);
+        }
+    }
+}
+
+static void lgd_tst_rx_thread_entry(void* parameter)
+{
+    int nb_pkt;
+
+    while( 1 )
+    {
+        /* fetch N packets */
+        nb_pkt = lgw_receive(ARRAY_SIZE(lgd_rxpkt), lgd_rxpkt);
+
+        if ( nb_pkt == 0 || lgd_tst_sem_rx_wait_time == RT_WAITING_FOREVER )
+        {
+            rt_sem_take(&lgd_tst_sem_rx, lgd_tst_sem_rx_wait_time);
+        }
+        else
+        {
+            rx_timestamp = rt_tick_get();
+
+            /* display received packets */
+            for(uint8_t i = 0; i < nb_pkt; ++i)
+            {
+                lgd_rxpkt_ptr = &lgd_rxpkt[i];
+
+                if (lgd_rxpkt_ptr->status != STAT_CRC_BAD)
+                {
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " tstamp   : %010u", lgd_rxpkt_ptr->count_us);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " size     :%3u", lgd_rxpkt_ptr->size);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " if_chain :%2d", lgd_rxpkt_ptr->if_chain);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " frequency: %d", lgd_rxpkt_ptr->freq_hz);
+
+                    switch (lgd_rxpkt_ptr-> modulation)
+                    {
+                        case MOD_LORA:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " LoRa");
+                            break;
+
+                        case MOD_FSK:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " FSK");
+                            break;
+
+                        default:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " modulation?");
+                    }
+
+                    switch (lgd_rxpkt_ptr->datarate)
+                    {
+                        case DR_LORA_SF7:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " SF7");
+                            break;
+
+                        case DR_LORA_SF8:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " SF8");
+                            break;
+
+                        case DR_LORA_SF9:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " SF9");
+                            break;
+
+                        case DR_LORA_SF10:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " SF10");
+                            break;
+
+                        case DR_LORA_SF11:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " SF11");
+                            break;
+
+                        case DR_LORA_SF12:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " SF12");
+                            break;
+
+                        default:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " datarate?");
+                    }
+
+                    switch (lgd_rxpkt_ptr->coderate)
+                    {
+                        case CR_LORA_4_5:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " CR1(4/5)");
+                            break;
+
+                        case CR_LORA_4_6:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " CR2(2/3)");
+                            break;
+
+                        case CR_LORA_4_7:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " CR3(4/7)");
+                            break;
+
+                        case CR_LORA_4_8:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " CR4(1/2)");
+                            break;
+
+                        default:
+                            LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " coderate?");
+                    }
+
+                    rssic_value = lgd_rxpkt_ptr->rssic;
+                    rssis_value = lgd_rxpkt_ptr->rssis;
+
+                    snr_value_max = lgd_rxpkt_ptr->snr_max;
+                    snr_value_min = lgd_rxpkt_ptr->snr_min;
+                    snr_value_avg = lgd_rxpkt_ptr->snr;
+
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " rssi:%+6.1f (avg:%.1f) snr :%+5.1f (min:%+5.1f, max:%+5.1f)", lgd_rxpkt_ptr->rssis, lgd_rxpkt_ptr->rssic, lgd_rxpkt_ptr->snr, lgd_rxpkt_ptr->snr_min, lgd_rxpkt_ptr->snr_max);
+
+                    rx_frame_process(lgd_rxpkt_ptr);
+                }
+                else
+                {
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " \n\nCRC error, damaged packet");
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " if_chain:%2d", lgd_rxpkt_ptr->if_chain);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " tstamp:%010u", lgd_rxpkt_ptr->count_us);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_RX_INFO, LOG_LVL_DBG, " size:%3u\n", lgd_rxpkt_ptr->size);
+                }
+            }
+        }
+    }
+}
+
+static void lgd_tst_main_thread_entry(void* parameter)
+{
+    while( 1 )
+    {
+        if( lgd_tst_initialized == false )
+        {
+            /* init lgd */
+            if(lgd_tst_lgw_init(lgd_test_mode) == RT_EOK)
+            {
+                if(lgd_test_mode <= LGD_TEST_AS_DEVICE_MODE)
+                {
+                    /* start lgw rx thread */
+                    rt_sem_release(&lgd_tst_sem_rx);
+                }
+
+                /* first init\reboot done */
+                rt_sem_release(&lgd_tst_sem_main);
+            }
+            lgd_tst_initialized = true;
+        }
+
+        if(lgd_test_mode == LGD_TEST_AS_DEVICE_MODE)
+        {
+            /* tx_seq_cnt start from 0 */
+            if( tx_seq_cnt < max_tx_nbtrials )
+            {
+                /* for first ping to printf some info */
+                if( !tx_seq_cnt )
+                {
+                    uint32_t packet_toa = lgw_get_toa_ms(&lgd_conf_lgw.txrf, payload_len);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Mater Address(MA):[0x%X]", master_address);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Pinging [SA=0x%X] with %d bytes(ToA=%d ms) of data for %d counters:", slaver_address, payload_len, packet_toa, max_tx_nbtrials);
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "With radio parameters: freq=%d, TxPower=%d, SF=%d, BW=%s(%d), CR=%d, Public_Network:%d, IQ_Inversion:%d", lgd_conf_lgw.txrf.freq_hz, lgd_conf_lgw.txrf.rf_power,
+                                  lgd_conf_lgw.txrf.datarate, bandwidth_string[lgd_conf_lgw.txrf.bandwidth - 4], lgd_conf_lgw.txrf.bandwidth, lgd_conf_lgw.txrf.coderate,
+                                  lgd_conf_lgw.lorawan_public, lgd_conf_lgw.txrf.invert_pol);
+                }
+
+                lgd_ack_response_flag = false;
+                send_ping_packet(master_address, slaver_address, payload_len);
+
+                /* periodic send by lgd_tst_sem_main_wait_time or immediate send by rxdone */
+                lgd_tst_sem_main.value = 0;
+                rt_sem_take(&lgd_tst_sem_main, lgd_tst_sem_main_wait_time);
+
+                if( lgd_ack_response_flag == false )
+                {
+                    rx_timeout_cnt++;
+                    rx_timestamp = rt_tick_get();
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Request [SA=0x%X] timed out: seqno=%d, time=%d ms", slaver_address, tx_seq_cnt, LGD_TIMESTAMP_TO_MS( rx_timestamp - tx_timestamp ));
+                }
+            }
+            else
+            {
+                uint16_t per = 100 - ( (float) rx_correct_cnt / tx_seq_cnt ) * 100;
+                uint32_t tx_total_byte = tx_seq_cnt * ( payload_len + MAC_HEADER_OVERHEAD );
+                uint32_t tx_total_kbyte_integer = tx_total_byte >> 10;   // / 1024
+                uint32_t tx_total_kbyte_decimal = tx_total_byte & 0x3FF; // % 1024
+
+                uint32_t rx_total_byte = rx_correct_cnt * ( payload_len + MAC_HEADER_OVERHEAD );
+                uint32_t rx_total_kbyte_integer = rx_total_byte >> 10;   // / 1024
+                uint32_t rx_total_kbyte_decimal = rx_total_byte & 0x3FF; // % 1024
+
+                /* wait for PHY log output done */
+                rt_thread_mdelay(10);
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "\r\n====== LoRa GW Driver Ping statistics for [MA=0x%X <-> SA=0x%X] with [TxPower=%d,SF=%d] ======", master_address, slaver_address, lgd_conf_lgw.txrf.rf_power, lgd_conf_lgw.txrf.datarate);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "-> Tx pakcets: sent = %d, tx_total = %d.%d KByte", tx_seq_cnt, tx_total_kbyte_integer, tx_total_kbyte_decimal);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "-> Rx pakcets: received = %d, lost = %d, per = %d %%, rx_total = %d.%d KByte", rx_correct_cnt, rx_timeout_cnt, per, rx_total_kbyte_integer, rx_total_kbyte_decimal);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "--> Rx rssi: rssic = %+6.1f, rssis = %+6.1f", rssic_value, rssis_value);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "--> Rx snr: max_snr  = %+d, min_snr  = %+d, avg_snr  = %+d", snr_value_max, snr_value_min, snr_value_avg);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "====== LoRa GW Driver Ping Test Finished ======\r\n");
+
+                /* initlize the statistics parameters */
+                rssis_value = -128;
+                rssic_value = -128;
+
+                snr_value_avg = -128;
+                snr_value_min = -128;
+                snr_value_max = -128;
+
+                tx_seq_cnt = 0;
+                rx_timeout_cnt = 0;
+                rx_error_cnt = 0;
+                rx_correct_cnt = 0;
+
+                rt_sem_take(&lgd_tst_sem_main, RT_WAITING_FOREVER);
+            }
+        }
+        else if( lgd_test_mode == LGD_TEST_AS_CW_MODE )
+        {
+            lgd_conf_lgw.txrf.modulation = MOD_CW;
+            lgd_conf_lgw.txrf.freq_offset = 0;
+            lgd_conf_lgw.txrf.f_dev = 0;
+            lgd_conf_lgw.txrf.tx_mode = IMMEDIATE;
+
+            lgd_app_data.size = 1;
+            lgw_tx( &lgd_conf_lgw.txrf, &lgd_app_data );
+
+            rt_sem_take(&lgd_tst_sem_main, RT_WAITING_FOREVER);
+        }
+        else
+        {
+            /* concentrator mode£¬pend here */
+            rt_sem_take(&lgd_tst_sem_main, RT_WAITING_FOREVER);
+        }
+    }
+}
+
+/* for lgd(lora gw driver)finish\msh */
+typedef enum
+{
+    CMD_LGD_CHIP_PROBE_INDEX = 0,         // LoRa Chip probe
+    CMD_LGD_TXC_CONFIG_INDEX,             // txc
+    CMD_LGD_RXC_CONFIG_INDEX,             // rxc
+    CMD_LGD_MAC_CONFIG_INDEX,             // mac
+    CMD_LGD_TX_CW_INDEX,                  // tx cw
+    CMD_LGD_PING_INDEX,                   // ping-pong
+    CMD_LGD_RX_PACKET_INDEX,              // rx packet only
+    CMD_LGD_RESTART_INDEX,                // restart
+} lora_gw_shell_cmd_t;
+
+const char* lgd_help_info[] =
+{
+    [CMD_LGD_CHIP_PROBE_INDEX]            = "lgd probe                - lora gw driver probe",
+    [CMD_LGD_TXC_CONFIG_INDEX]            = "lgd txc <para> <val>     - config tx parameters,<para>:<freq><power><sf><bw><iq>..",
+    [CMD_LGD_RXC_CONFIG_INDEX]            = "lgd rxc <para> <val>     - config rx parameters,<para>:<rf0><rf1><auto><iq>..",
+    [CMD_LGD_MAC_CONFIG_INDEX]            = "lgd mac <lorawan_public> - config <lorawan_public>..",
+    [CMD_LGD_TX_CW_INDEX]                 = "lgd cw <freq> <power>    - send a tx carrier wave",
+    [CMD_LGD_PING_INDEX]                  = "lgd ping <nb> <len>      - ping with nbtrials and payload len(master)",
+    [CMD_LGD_RX_PACKET_INDEX]             = "lgd rx <only>            - rx data, 0 - ping(slaver), 1 - only(sniffer)",
+    [CMD_LGD_RESTART_INDEX]               = "lgd reboot               - restart sx130x",
+};
+
+/* LoRa GW Driver Test Shell */
+static int lgd(int argc, char *argv[])
+{
+    size_t i = 0;
+
+    if (argc < 2)
+    {
+        /* parameter error */
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Usage:\n");
+
+        for (i = 0; i < sizeof(lgd_help_info) / sizeof(char*); i++)
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "%s\n", lgd_help_info[i]);
+        }
+
+        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "\n");
+    }
+    else
+    {
+        const char *cmd = argv[1];
+
+        /* creat lgd test thread init */
+        if( lgd_tst_main_thread_init() == false )
+        {
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "LoRa GW Driver Test Init Failed");
+            return 0;
+        }
+
+        /* if lora concentrator mode is working, release and stop it forever */
+        {
+            rt_sem_release(&lgd_tst_sem_rx);
+            lgd_tst_sem_rx_wait_time = RT_WAITING_FOREVER;
+        }
+
+        if (!rt_strcmp(cmd, "probe"))
+        {
+            int32_t sx1302_chip_version = 0;
+            int status = lgw_get_ver(&sx1302_chip_version);
+
+            if( status == LGW_HAL_SUCCESS && sx1302_chip_version == 0x10 )
+            {
+                rt_kprintf("SX1302 Access Successed\r\n");
+            }
+            else
+            {
+                rt_kprintf("SX1302 Access Failed\r\n");
+            }
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "SX1302 version: 0x%02X", sx1302_chip_version);
+
+            // get sx1302 eui
+            uint64_t gw_eui;
+            char gweui_string[17] = {0};
+            status = lgw_get_eui(&gw_eui);
+
+            snprintf(gweui_string, sizeof gweui_string, "%016"PRIX64, gw_eui);
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "SX1302 EUI:%s",gweui_string);
+        }
+        else if (!rt_strcmp(cmd, "cw"))
+        {
+            if (argc >= 3)
+            {
+                lgd_conf_lgw.txrf.freq_hz = atol(argv[2]);
+            }
+
+            if (argc >= 4)
+            {
+                lgd_conf_lgw.txrf.rf_power = atol(argv[3]);
+            }
+
+            /* hardware reset sx130x to stop cw*/
+            lgw_hw_reset();
+            /* as cw mode */
+            lgd_tst_initialized = false;
+            lgd_test_mode = LGD_TEST_AS_CW_MODE;
+            rt_sem_release(&lgd_tst_sem_main);
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Switch to CW Mode");
+        }
+        else if (!rt_strcmp(cmd, "ping"))
+        {
+            if( argc >= 3 )
+            {
+                /* max_tx_nbtrials for setup tx counter */
+                max_tx_nbtrials = atol(argv[2]);
+            }
+
+            if( argc >= 4 )
+            {
+                /* payload_len for setup tx payload length */
+                payload_len = atoi(argv[3]);
+
+                if( payload_len < MIN_TETS_APP_DATA_SIZE )
+                {
+                    LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Waring: This size will not supported [lgd ping]");
+                }
+            }
+
+            /* as device mode */
+            lgd_rx_only_flag = false;
+
+            if( lgd_test_mode != LGD_TEST_AS_DEVICE_MODE )
+            {
+                // reinit sx130x
+                lgd_tst_initialized = false;
+                lgd_test_mode = LGD_TEST_AS_DEVICE_MODE;
+            }
+            else
+            {
+                /* start to rx */
+                lgd_tst_sem_rx_wait_time = rt_tick_from_millisecond(LGD_TEST_FETCH_RX_BUFFER_INTERVAL);
+                rt_sem_release(&lgd_tst_sem_rx);
+            }
+
+            /* send wait for ack timeout = 2 * toa + process time */
+            lgd_tst_sem_main_wait_time = rt_tick_from_millisecond(2 * lgw_get_toa_ms(&lgd_conf_lgw.txrf, payload_len) + ( LGD_TEST_FETCH_RX_BUFFER_INTERVAL * 3) );
+            
+            /* start to tx */
+            rt_sem_release(&lgd_tst_sem_main);
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Switch to Devcice Mode");
+        }
+        else if( !rt_strcmp(cmd, "rx"))
+        {
+            /* as concentrator */
+            lgd_tst_initialized = false;
+            lgd_test_mode = LGD_TEST_AS_CONCENTRATOR_MODE;
+
+            if (argc >= 3)
+            {
+                /* if lgd_rx_only_flag = 1, not ack otherwise send back */
+                lgd_rx_only_flag = atol(argv[2]);
+            }
+
+            rt_sem_release(&lgd_tst_sem_main);
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Switch to Concentrator Mode");
+        }
+        else if (!rt_strcmp(cmd, "rxc"))
+        {
+            const char *cmd1 = argv[2];
+            uint32_t freq  = atol(argv[3]);
+
+            uint8_t enable = 1;/* default enable */
+
+            if( argc >= 5 )
+            {
+                enable = atol(argv[4]);
+            }
+
+            if( argc >= 3 )
+            {
+                if (!rt_strcmp(cmd1, "rf0")) /* radio0 center frequence */
+                {
+                    lgd_conf_lgw.rxrf[0].enable   = enable;
+
+                    lgd_conf_lgw.rxrf[0].freq_hz  = freq;
+                }
+                else if (!rt_strcmp(cmd1, "rf1")) /* radio1 center frequence */
+                {
+                    lgd_conf_lgw.rxrf[1].enable   = enable;
+
+                    lgd_conf_lgw.rxrf[1].freq_hz  = freq;
+                }
+                else if (!rt_strcmp(cmd1, "auto")) /* setup start freq, 200k space, lgw auto setup radio0 and radio1 chanenl freq. */
+                {
+                    lgd_conf_lgw.rxrf[0].enable   = enable;
+                    lgd_conf_lgw.rxrf[1].enable   = enable;
+
+                    /* eg:set start 475.3 -> radio0 = 475.6 ,radio1 = 476.4 */
+                    lgd_conf_lgw.rxrf[0].freq_hz = freq + LGD_RF_CHIAN0_CENTER_FREQ_OFFSET;
+                    lgd_conf_lgw.rxrf[1].freq_hz = freq + LGD_RF_CHIAN1_CENTER_FREQ_OFFSET;
+                }
+                else if (!rt_strcmp(cmd1, "iq"))
+                {
+                    if (argc >= 4)
+                    {
+                        lgd_conf_lgw.rxrf[0].invert_pol = atoi(argv[3]);
+                    }
+                }
+            }
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "-items-                     -{cmd}-   -Value-");
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " radio-chain-0 center freq   {rf0}:   %d,%d", lgd_conf_lgw.rxrf[0].freq_hz,lgd_conf_lgw.rxrf[0].enable);
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " radio-chain-1 center freq   {rf1}:   %d,%d", lgd_conf_lgw.rxrf[1].freq_hz,lgd_conf_lgw.rxrf[1].enable);
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " radio-chain-0-1 start freq  {auto}:  %d,%d", (lgd_conf_lgw.rxrf[0].freq_hz - LGD_RF_CHIAN0_CENTER_FREQ_OFFSET),lgd_conf_lgw.rxrf[0].enable);
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " rx iq invert                {iq}:    %d", lgd_conf_lgw.rxrf[0].invert_pol);
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_DBG, "== Channels Frequence Table: ==\r\n");
+            for(uint8_t i = 0; i < 8; i++)
+            {
+                uint32_t ch_freq = lgd_conf_lgw.rxrf[lgd_conf_lgw.rxif.channel_if_rfchain[i]].freq_hz + lgd_conf_lgw.rxif.channel_if_freq[i];
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "CH[%i]:%d,rfchain:%d,%s\r\n", i, ch_freq, lgd_conf_lgw.rxif.channel_if_rfchain[i], lgd_conf_lgw.rxif.channel_if_enable[i] ? "enable" : "disable");
+
+                if( i == 0 )
+                {
+                    // defaultly, update tx freq equal to first rx channel frequence plus tx freq offset
+                    lgd_conf_lgw.txrf.freq_hz = ch_freq + lgd_tx_frequency_offset;
+                }
+            }
+        }
+        else if (!rt_strcmp(cmd, "txc"))
+        {
+            const char *cmd1 = argv[2];
+
+            /* config radio paramters, such as frequency,txPower,sf,bw...*/
+            if (!rt_strcmp(cmd1, "freq"))
+            {
+                if (argc >= 4)
+                {
+                    lgd_conf_lgw.txrf.freq_hz = atol(argv[3]);
+                }
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Tx Frequency: %d", lgd_conf_lgw.txrf.freq_hz);
+            }
+            else if (!rt_strcmp(cmd1, "foffset"))
+            {
+                if (argc >= 4)
+                {
+                    lgd_tx_frequency_offset = atol(argv[3]);
+                }
+
+                lgd_conf_lgw.txrf.freq_hz = lgd_conf_lgw.rxrf[0].freq_hz + lgd_tx_frequency_offset;
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Tx Frequency Offset: %d", lgd_tx_frequency_offset);
+            }
+            else if (!rt_strcmp(cmd1, "power"))
+            {
+                if (argc >= 4)
+                {
+                    lgd_conf_lgw.txrf.rf_power = atoi(argv[3]);
+                }
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "TxPower  : %d", lgd_conf_lgw.txrf.rf_power);
+            }
+            else if (!rt_strcmp(cmd1, "sf"))
+            {
+                if (argc >= 4)
+                {
+                    if (!rt_strcmp(argv[3], "?"))
+                    {
+                        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "SF:7|8|9|10|11|12");
+                    }
+                    else
+                    {
+                        lgd_conf_lgw.txrf.datarate = atoi(argv[3]);
+                    }
+                }
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "SF: %d", lgd_conf_lgw.txrf.datarate);
+            }
+            else if (!rt_strcmp(cmd1, "bw"))
+            {
+                uint32_t bandwidth;
+
+                if (argc >= 4)
+                {
+                    if (!rt_strcmp(argv[3], "?"))
+                    {
+                        LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "LoRa BW:125kHz|250kHz|500kHz");
+                    }
+                    else
+                    {
+                        bandwidth = atoi(argv[3]);
+                    }
+                }
+
+                if( bandwidth == 125 )
+                {
+                    lgd_conf_lgw.txrf.bandwidth = BW_125KHZ;
+                }
+                else if( bandwidth == 250 )
+                {
+                    lgd_conf_lgw.txrf.bandwidth = BW_250KHZ;
+                }
+                else if( bandwidth == 500 )
+                {
+                    lgd_conf_lgw.txrf.bandwidth = BW_500KHZ;
+                }
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "LoRa BW: %s(%d)", bandwidth_string[lgd_conf_lgw.txrf.bandwidth - BW_125KHZ], lgd_conf_lgw.txrf.bandwidth);
+            }
+            else if (!rt_strcmp(cmd1, "iq"))
+            {
+                if (argc >= 4)
+                {
+                    lgd_conf_lgw.txrf.invert_pol = atoi(argv[3]);
+                }
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "IQ Inversion: %d", lgd_conf_lgw.txrf.invert_pol);
+            }
+
+            if (argc < 3)
+            {
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "-items-         -{cmd}-   -Value-");
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " TX Freq         {freq}:   %d", lgd_conf_lgw.txrf.freq_hz);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " TX Freq Offset  {foffset}:%d", lgd_tx_frequency_offset);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " Tx Power        {power}:  %d", lgd_conf_lgw.txrf.rf_power);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " SF              {sf}:     %d", lgd_conf_lgw.txrf.datarate);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " BW              {bw}:     %s(%d)", bandwidth_string[lgd_conf_lgw.txrf.bandwidth - 4], lgd_conf_lgw.txrf.bandwidth);
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " Tx IQ Inversion {iq}:     %d", lgd_conf_lgw.txrf.invert_pol);
+
+            }
+        }
+        else if (!rt_strcmp(cmd, "mac"))
+        {
+            const char *cmd1 = argv[2];
+
+            if (!rt_strcmp(cmd1, "lorawan"))
+            {
+                if (argc >= 4)
+                {
+                    lgd_conf_lgw.lorawan_public = atoi(argv[3]);
+                }
+
+                LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "Public Network: %d", lgd_conf_lgw.lorawan_public);
+            }
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, "-items-         -{cmd}-     -Value-");
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " Public Network  {lorawan}:  %d(%s)", lgd_conf_lgw.lorawan_public,lgd_conf_lgw.lorawan_public?"public":"private");
+        }
+        else if (!rt_strcmp(cmd, "reboot"))
+        {
+            lgd_tst_initialized = false;
+            rt_sem_release(&lgd_tst_sem_main);
+
+            LGD_DEBUG_LOG(LGD_DBG_SHELL_TEST, LOG_LVL_INFO, " Restart SX130x..");
+        }
+    }
+
+    return 1;
+}
+MSH_CMD_EXPORT(lgd, lora gw driver test shell);
+

+ 81 - 0
samples/lgd_tester/lgd_tester.h

@@ -0,0 +1,81 @@
+/*!
+ * \file      lgd-test-shell.h
+ *
+ * \brief     lora gw driver test implementation
+ *
+ * \copyright SPDX-License-Identifier: Apache-2.0
+ *
+ * \author    Forest-Rain
+ */
+
+#ifndef __LORA_GW_DRIVER_TESTER_H__
+#define __LORA_GW_DRIVER_TESTER_H__
+
+#ifndef LGD_DBG_SHELL_TEST
+    #define LGD_DBG_SHELL_TEST                          0
+#endif
+
+#ifndef LGD_DBG_SHELL_RX_INFO
+    #define LGD_DBG_SHELL_RX_INFO                       0
+#endif
+
+#if defined( LORA_GW_DRIVER_USING_PHY_REGION_AS923 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_AS923 )
+
+    #define RF_FREQUENCY                                923000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_AU915 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_AU915 )
+
+    #define RF_FREQUENCY                                915000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_CN470 ) || defined ( LORA_GW_DRIVER_USING_PHY_REGION_CN470S ) || \
+      defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_CN470 ) || defined ( LORA_GW_DRIVER_LIB_USING_PHY_REGION_CN470S )
+
+    #define RF_FREQUENCY                                470300000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_CN779 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_CN779 )
+
+    #define RF_FREQUENCY                                779000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_EU433 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_EU433 )
+
+    #define RF_FREQUENCY                                433000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_EU868 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_EU868 )
+
+    #define RF_FREQUENCY                                868000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_KR920 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_KR920 )
+
+    #define RF_FREQUENCY                                920000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_IN865 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_IN865 )
+
+    #define RF_FREQUENCY                                865000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_US915 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_US915 )
+
+    #define RF_FREQUENCY                                915000000 // Hz
+
+#elif defined( LORA_GW_DRIVER_USING_PHY_REGION_RU864 ) || defined( LORA_GW_DRIVER_LIB_USING_PHY_REGION_RU864 )
+
+    #define RF_FREQUENCY                                864000000 // Hz
+
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+
+#define TX_RX_FREQUENCE_OFFSET            0  // 0       TX Freq = RX Freq
+                                             // 1800000 TX Freq = RX Freq + 1.8M   
+
+#define LORA_MASTER_DEVADDR               0x11223344
+#define LORA_SLAVER_DEVADDR               0x01020304
+#define MAC_HEADER_OVERHEAD               13
+#define MIN_TETS_APP_DATA_SIZE            17 // for PING protocol
+
+// lora gw driver test state
+#define LGD_TEST_AS_CONCENTRATOR_MODE     0x00
+#define LGD_TEST_AS_DEVICE_MODE           0x01
+#define LGD_TEST_AS_CW_MODE               0x02
+
+#endif
+