Add files via upload

This commit is contained in:
loradar
2020-06-04 03:20:44 +10:00
committed by GitHub
parent 3b0885dfeb
commit 9372172a22
52 changed files with 9294 additions and 0 deletions
@@ -0,0 +1,49 @@
Copyright (C) 2013, 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.
--- For the parson library ---
Parson ( http://kgabis.github.com/parson/ )
Copyright (C) 2012 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,22 @@
### Environment constants
LGW_PATH ?= ../../lora_gateway/libloragw
ARCH ?=
CROSS_COMPILE ?=
export
### general build targets
all:
$(MAKE) all -e -C lora_pkt_fwd
$(MAKE) all -e -C util_ack
$(MAKE) all -e -C util_sink
$(MAKE) all -e -C util_tx_test
clean:
$(MAKE) clean -e -C lora_pkt_fwd
$(MAKE) clean -e -C util_ack
$(MAKE) clean -e -C util_sink
$(MAKE) clean -e -C util_tx_test
### EOF
@@ -0,0 +1,455 @@
______ _
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Basic communication protocol between Lora gateway and server
=============================================================
1. Introduction
----------------
The protocol between the gateway and the server is purposefully very basic and
for demonstration purpose only, or for use on private and reliable networks.
There is no authentication of the gateway or the server, and the acknowledges
are only used for network quality assessment, not to correct UDP datagrams
losses (no retries).
2. System schematic and definitions
------------------------------------
((( Y )))
|
|
+ - -|- - - - - - - - - - - - - + xxxxxxxxxxxx +--------+
| +--+-----------+ +------+ | xx x x xxx | |
| | | | | | xx Internet xx | |
| | Concentrator |<--->| Host |<-------xx or xx-------->| |
| | | SPI | | | xx Intranet xx | Server |
| +--------------+ +------+ | xxxx x xxxx | |
| ^ ^ | xxxxxxxx | |
| | PPS +-------+ NMEA | | | |
| +-----| GPS |-------+ | +--------+
| | (opt) | |
| +-------+ |
| |
| Gateway |
+- - - - - - - - - - - - - - - -+
__Concentrator__: radio RX/TX board, based on Semtech multichannel modems
(SX130x), transceivers (SX135x) and/or low-power stand-alone modems (SX127x).
__Host__: embedded computer on which the packet forwarder is run. Drives the
concentrator through a SPI link.
__GPS__: GNSS (GPS, Galileo, GLONASS, etc) receiver with a "1 Pulse Per Second"
output and a serial link to the host to send NMEA frames containing time and
geographical coordinates data. Optional.
__Gateway__: a device composed of at least one radio concentrator, a host, some
network connection to the internet or a private network (Ethernet, 3G, Wifi,
microwave link), and optionally a GPS receiver for synchronization.
__Server__: an abstract computer that will process the RF packets received and
forwarded by the gateway, and issue RF packets in response that the gateway
will have to emit.
It is assumed that the gateway can be behind a NAT or a firewall stopping any
incoming connection.
It is assumed that the server has an static IP address (or an address solvable
through a DNS service) and is able to receive incoming connections on a
specific port.
3. Upstream protocol
---------------------
### 3.1. Sequence diagram ###
+---------+ +---------+
| Gateway | | Server |
+---------+ +---------+
| -----------------------------------\ |
|-| When 1-N RF packets are received | |
| ------------------------------------ |
| |
| PUSH_DATA (token X, GW MAC, JSON payload) |
|------------------------------------------------------------->|
| |
| PUSH_ACK (token X) |
|<-------------------------------------------------------------|
| ------------------------------\ |
| | process packets *after* ack |-|
| ------------------------------- |
| |
### 3.2. PUSH_DATA packet ###
That packet type is used by the gateway mainly to forward the RF packets
received, and associated metadata, to the server.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | random token
3 | PUSH_DATA identifier 0x00
4-11 | Gateway unique identifier (MAC address)
12-end | JSON object, starting with {, ending with }, see section 4
### 3.3. PUSH_ACK packet ###
That packet type is used by the server to acknowledge immediately all the
PUSH_DATA packets received.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | same token as the PUSH_DATA packet to acknowledge
3 | PUSH_ACK identifier 0x01
4. Upstream JSON data structure
--------------------------------
The root object can contain an array named "rxpk":
``` json
{
"rxpk":[ {...}, ...]
}
```
That array contains at least one JSON object, each object contain a RF packet
and associated metadata with the following fields:
Name | Type | Function
:----:|:------:|--------------------------------------------------------------
time | string | UTC time of pkt RX, us precision, ISO 8601 'compact' format
tmst | number | Internal timestamp of "RX finished" event (32b unsigned)
freq | number | RX central frequency in MHz (unsigned float, Hz precision)
chan | number | Concentrator "IF" channel used for RX (unsigned integer)
rfch | number | Concentrator "RF chain" used for RX (unsigned integer)
stat | number | CRC status: 1 = OK, -1 = fail, 0 = no CRC
modu | string | Modulation identifier "LORA" or "FSK"
datr | string | LoRa datarate identifier (eg. SF12BW500)
datr | number | FSK datarate (unsigned, in bits per second)
codr | string | LoRa ECC coding rate identifier
rssi | number | RSSI in dBm (signed integer, 1 dB precision)
lsnr | number | Lora SNR ratio in dB (signed float, 0.1 dB precision)
size | number | RF packet payload size in bytes (unsigned integer)
data | string | Base64 encoded RF packet payload, padded
Example (white-spaces, indentation and newlines added for readability):
``` json
{"rxpk":[
{
"time":"2013-03-31T16:21:17.528002Z",
"tmst":3512348611,
"chan":2,
"rfch":0,
"freq":866.349812,
"stat":1,
"modu":"LORA",
"datr":"SF7BW125",
"codr":"4/6",
"rssi":-35,
"lsnr":5.1,
"size":32,
"data":"-DS4CGaDCdG+48eJNM3Vai-zDpsR71Pn9CPA9uCON84"
},{
"time":"2013-03-31T16:21:17.530974Z",
"tmst":3512348514,
"chan":9,
"rfch":1,
"freq":869.1,
"stat":1,
"modu":"FSK",
"datr":50000,
"rssi":-75,
"size":16,
"data":"VEVTVF9QQUNLRVRfMTIzNA=="
},{
"time":"2013-03-31T16:21:17.532038Z",
"tmst":3316387610,
"chan":0,
"rfch":0,
"freq":863.00981,
"stat":1,
"modu":"LORA",
"datr":"SF10BW125",
"codr":"4/7",
"rssi":-38,
"lsnr":5.5,
"size":32,
"data":"ysgRl452xNLep9S1NTIg2lomKDxUgn3DJ7DE+b00Ass"
}
]}
```
The root object can also contain an object named "stat" :
``` json
{
"rxpk":[ {...}, ...],
"stat":{...}
}
```
It is possible for a packet to contain no "rxpk" array but a "stat" object.
``` json
{
"stat":{...}
}
```
That object contains the status of the gateway, with the following fields:
Name | Type | Function
:----:|:------:|--------------------------------------------------------------
time | string | UTC 'system' time of the gateway, ISO 8601 'expanded' format
lati | number | GPS latitude of the gateway in degree (float, N is +)
long | number | GPS latitude of the gateway in degree (float, E is +)
alti | number | GPS altitude of the gateway in meter RX (integer)
rxnb | number | Number of radio packets received (unsigned integer)
rxok | number | Number of radio packets received with a valid PHY CRC
rxfw | number | Number of radio packets forwarded (unsigned integer)
ackr | number | Percentage of upstream datagrams that were acknowledged
dwnb | number | Number of downlink datagrams received (unsigned integer)
txnb | number | Number of packets emitted (unsigned integer)
Example (white-spaces, indentation and newlines added for readability):
``` json
{"stat":{
"time":"2014-01-12 08:59:28 GMT",
"lati":46.24000,
"long":3.25230,
"alti":145,
"rxnb":2,
"rxok":2,
"rxfw":2,
"ackr":100.0,
"dwnb":2,
"txnb":2
}}
```
5. Downstream protocol
-----------------------
### 5.1. Sequence diagram ###
+---------+ +---------+
| Gateway | | Server |
+---------+ +---------+
| -----------------------------------\ |
|-| Every N seconds (keepalive time) | |
| ------------------------------------ |
| |
| PULL_DATA (token Y, MAC@) |
|------------------------------------------------------------->|
| |
| PULL_ACK (token Y) |
|<-------------------------------------------------------------|
| |
+---------+ +---------+
| Gateway | | Server |
+---------+ +---------+
| ------------------------------------------------------\ |
| | Anytime after first PULL_DATA for each packet to TX |-|
| ------------------------------------------------------- |
| |
| PULL_RESP (token Z, JSON payload) |
|<-------------------------------------------------------------|
| |
| TX_ACK (token Z, JSON payload) |
|------------------------------------------------------------->|
### 5.2. PULL_DATA packet ###
That packet type is used by the gateway to poll data from the server.
This data exchange is initialized by the gateway because it might be
impossible for the server to send packets to the gateway if the gateway is
behind a NAT.
When the gateway initialize the exchange, the network route towards the
server will open and will allow for packets to flow both directions.
The gateway must periodically send PULL_DATA packets to be sure the network
route stays open for the server to be used at any time.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | random token
3 | PULL_DATA identifier 0x02
4-11 | Gateway unique identifier (MAC address)
### 5.3. PULL_ACK packet ###
That packet type is used by the server to confirm that the network route is
open and that the server can send PULL_RESP packets at any time.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | same token as the PULL_DATA packet to acknowledge
3 | PULL_ACK identifier 0x04
### 5.4. PULL_RESP packet ###
That packet type is used by the server to send RF packets and associated
metadata that will have to be emitted by the gateway.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | random token
3 | PULL_RESP identifier 0x03
4-end | JSON object, starting with {, ending with }, see section 6
### 5.5. TX_ACK packet ###
That packet type is used by the gateway to send a feedback to the server
to inform if a downlink request has been accepted or rejected by the gateway.
The datagram may optionnaly contain a JSON string to give more details on
acknoledge. If no JSON is present (empty string), this means than no error
occured.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | same token as the PULL_RESP packet to acknowledge
3 | TX_ACK identifier 0x05
4-end | [optional] JSON object, starting with {, ending with }, see section 6
6. Downstream JSON data structure
----------------------------------
The root object of PULL_RESP packet must contain an object named "txpk":
``` json
{
"txpk": {...}
}
```
That object contain a RF packet to be emitted and associated metadata with the following fields:
Name | Type | Function
:----:|:------:|--------------------------------------------------------------
imme | bool | Send packet immediately (will ignore tmst & time)
tmst | number | Send packet on a certain timestamp value (will ignore time)
time | string | Send packet at a certain time (GPS synchronization required)
freq | number | TX central frequency in MHz (unsigned float, Hz precision)
rfch | number | Concentrator "RF chain" used for TX (unsigned integer)
powe | number | TX output power in dBm (unsigned integer, dBm precision)
modu | string | Modulation identifier "LORA" or "FSK"
datr | string | LoRa datarate identifier (eg. SF12BW500)
datr | number | FSK datarate (unsigned, in bits per second)
codr | string | LoRa ECC coding rate identifier
fdev | number | FSK frequency deviation (unsigned integer, in Hz)
ipol | bool | Lora modulation polarization inversion
prea | number | RF preamble size (unsigned integer)
size | number | RF packet payload size in bytes (unsigned integer)
data | string | Base64 encoded RF packet payload, padding optional
ncrc | bool | If true, disable the CRC of the physical layer (optional)
Most fields are optional.
If a field is omitted, default parameters will be used.
Examples (white-spaces, indentation and newlines added for readability):
``` json
{"txpk":{
"imme":true,
"freq":864.123456,
"rfch":0,
"powe":14,
"modu":"LORA",
"datr":"SF11BW125",
"codr":"4/6",
"ipol":false,
"size":32,
"data":"H3P3N2i9qc4yt7rK7ldqoeCVJGBybzPY5h1Dd7P7p8v"
}}
```
``` json
{"txpk":{
"imme":true,
"freq":861.3,
"rfch":0,
"powe":12,
"modu":"FSK",
"datr":50000,
"fdev":3000,
"size":32,
"data":"H3P3N2i9qc4yt7rK7ldqoeCVJGBybzPY5h1Dd7P7p8v"
}}
```
The root object of TX_ACK packet must contain an object named "txpk_ack":
``` json
{
"txpk_ack": {...}
}
```
That object contain status information concerning the associated PULL_RESP packet.
Name | Type | Function
:----:|:------:|------------------------------------------------------------------------------
error | string | Indication about success or type of failure that occured for downlink request.
The possible values of "error" field are:
Value | Definition
:-----------------:|---------------------------------------------------------------------
NONE | Packet has been programmed for downlink
TOO_LATE | Rejected because it was already too late to program this packet for downlink
TOO_EARLY | Rejected because downlink packet timestamp is too much in advance
COLLISION_PACKET | Rejected because there was already a packet programmed in requested timeframe
COLLISION_BEACON | Rejected because there was already a beacon planned in requested timeframe
TX_FREQ | Rejected because requested frequency is not supported by TX RF chain
TX_POWER | Rejected because requested power is not supported by gateway
GPS_UNLOCKED | Rejected because GPS is unlocked, so GPS timestamp cannot be used
Examples (white-spaces, indentation and newlines added for readability):
``` json
{"txpk_ack":{
"error":"COLLISION_PACKET"
}}
```
7. Revisions
-------------
### v1.3 ###
* Added downlink feedback from gateway to server (PULL_RESP -> TX_ACK)
### v1.2 ###
* Added value of FSK bitrate for upstream.
* Added parameters for FSK bitrate and frequency deviation for downstream.
### v1.1 ###
* Added syntax for status report JSON object on upstream.
### v1.0 ###
* Initial version.
@@ -0,0 +1 @@
3.0.0
@@ -0,0 +1,71 @@
### Application-specific constants
APP_NAME := lora_pkt_fwd
### Environment constants
LGW_PATH ?= ../../lora_gateway/libloragw
ARCH ?=
CROSS_COMPILE ?=
OBJDIR = obj
INCLUDES = $(wildcard inc/*.h)
### External constant definitions
# must get library build option to know if mpsse must be linked or not
include $(LGW_PATH)/library.cfg
RELEASE_VERSION := `cat ../VERSION`
### Constant symbols
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I.
VFLAG := -D VERSION_STRING="\"$(RELEASE_VERSION)\""
### Constants for Lora concentrator HAL library
# List the library sub-modules that are used by the application
LGW_INC =
ifneq ($(wildcard $(LGW_PATH)/inc/config.h),)
# only for HAL version 1.3 and beyond
LGW_INC += $(LGW_PATH)/inc/config.h
endif
LGW_INC += $(LGW_PATH)/inc/loragw_hal.h
LGW_INC += $(LGW_PATH)/inc/loragw_gps.h
### Linking options
ifeq ($(CFG_SPI),native)
LIBS := -lloragw -lrt -lpthread -lm
else ifeq ($(CFG_SPI),ftdi)
LIBS := -lloragw -lrt -lpthread -lm -lmpsse
endif
### General build targets
all: $(APP_NAME)
clean:
rm -f $(OBJDIR)/*.o
rm -f $(APP_NAME)
### Sub-modules compilation
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR)
$(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@
### Main program compilation and assembly
$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) $(INCLUDES) | $(OBJDIR)
$(CC) -c $(CFLAGS) $(VFLAG) -I$(LGW_PATH)/inc $< -o $@
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a $(OBJDIR)/parson.o $(OBJDIR)/base64.o $(OBJDIR)/jitqueue.o $(OBJDIR)/timersync.o
$(CC) -L$(LGW_PATH) $< $(OBJDIR)/parson.o $(OBJDIR)/base64.o $(OBJDIR)/jitqueue.o $(OBJDIR)/timersync.o -o $@ $(LIBS)
### EOF
@@ -0,0 +1,216 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 1, /* radio_1 provides clock to concentrator */
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 867500000,
"rssi_offset": -166.0,
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 868500000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 868.1 MHz */
"enable": true,
"radio": 1,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 868.5 MHz */
"enable": true,
"radio": 1,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 867.1 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 867.3 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 867.5 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 867.7 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 867.9 MHz */
"enable": true,
"radio": 0,
"if": 400000
},
"chan_Lora_std": {
/* Lora MAC channel, 250kHz, SF7, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000,
"bandwidth": 250000,
"spread_factor": 7
},
"chan_FSK": {
/* FSK 50kbps channel, 868.8 MHz */
"enable": true,
"radio": 1,
"if": 300000,
"bandwidth": 125000,
"datarate": 50000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 0
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 0
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 12,
"rf_power": 0,
"dig_gain": 0
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 1,
"mix_gain": 8,
"rf_power": 3,
"dig_gain": 0
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 0
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 0
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 11,
"dig_gain": 0
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 15,
"rf_power": 13,
"dig_gain": 0
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 2,
"mix_gain": 10,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 16,
"dig_gain": 0
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 3,
"mix_gain": 9,
"rf_power": 20,
"dig_gain": 0
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 23,
"dig_gain": 0
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 3,
"mix_gain": 11,
"rf_power": 25,
"dig_gain": 0
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 12,
"rf_power": 26,
"dig_gain": 0
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 14,
"rf_power": 27,
"dig_gain": 0
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false
}
}
@@ -0,0 +1,231 @@
{
"SX1301_conf": {
"lorawan_public": true,
<<<<<<< HEAD:beacon_pkt_fwd/global_conf.json
"clksrc": 0, /* radio_0 provides clock to concentrator */
=======
"clksrc": 1, /* radio_1 provides clock to concentrator */
"antenna_gain": 0, /* antenna gain, in dBi */
>>>>>>> f31ee2e4eb6bcbb0c93ef685716842e151c8bd9f:lora_pkt_fwd/cfg/global_conf.json.PCB_E286.EU868.beacon
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 868200000,
"rssi_offset": -166.0,
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 869200000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 868.1 MHz */
"enable": true,
"radio": 0,
"if": -100000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 868.3 MHz */
"enable": true,
"radio": 0,
"if": 100000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 868.5 MHz */
"enable": true,
"radio": 0,
"if": 300000
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 868.85 MHz */
"enable": true,
"radio": 1,
"if": -350000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 869.05 MHz */
"enable": true,
"radio": 1,
"if": -150000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 869.525 MHz */
"enable": true,
"radio": 1,
"if": 325000
},
"chan_multiSF_6": {
"enable": false,
"radio": 0,
"if": 0
},
"chan_multiSF_7": {
"enable": false,
"radio": 0,
"if": 0
},
"chan_Lora_std": {
/* Lora MAC channel, 250kHz, SF7, 868.3 MHz */
"enable": true,
"radio": 0,
"if": 100000,
"bandwidth": 250000,
"spread_factor": 7
},
"chan_FSK": {
/* FSK 50kbps channel, 868.3 MHz */
"enable": true,
"radio": 0,
"if": 100000,
"bandwidth": 125000,
"datarate": 50000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 0
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 0
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 12,
"rf_power": 0,
"dig_gain": 0
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 1,
"mix_gain": 8,
"rf_power": 3,
"dig_gain": 0
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 0
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 0
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 11,
"dig_gain": 0
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 15,
"rf_power": 13,
"dig_gain": 0
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 2,
"mix_gain": 10,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 16,
"dig_gain": 0
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 20,
"dig_gain": 0
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 3,
"mix_gain": 11,
"rf_power": 23,
"dig_gain": 0
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 3,
"mix_gain": 12,
"rf_power": 24,
"dig_gain": 0
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 13,
"rf_power": 25,
"dig_gain": 0
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 15,
"rf_power": 26,
"dig_gain": 0
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
<<<<<<< HEAD:beacon_pkt_fwd/global_conf.json
"forward_crc_disabled": false
=======
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyAMA0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0,
/* Beaconing parameters */
"beacon_period": 128,
"beacon_freq_hz": 869525000
>>>>>>> f31ee2e4eb6bcbb0c93ef685716842e151c8bd9f:lora_pkt_fwd/cfg/global_conf.json.PCB_E286.EU868.beacon
}
}
@@ -0,0 +1,227 @@
{
"SX1301_conf": {
"lorawan_public": true,
<<<<<<< HEAD:gps_pkt_fwd/global_conf.json
"clksrc": 0, /* radio_0 provides clock to concentrator */
=======
"clksrc": 1, /* radio_1 provides clock to concentrator */
"antenna_gain": 0, /* antenna gain, in dBi */
>>>>>>> f31ee2e4eb6bcbb0c93ef685716842e151c8bd9f:lora_pkt_fwd/cfg/global_conf.json.PCB_E286.EU868.gps
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 868200000,
"rssi_offset": -166.0,
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 869200000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 868.1 MHz */
"enable": true,
"radio": 0,
"if": -100000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 868.3 MHz */
"enable": true,
"radio": 0,
"if": 100000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 868.5 MHz */
"enable": true,
"radio": 0,
"if": 300000
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 868.85 MHz */
"enable": true,
"radio": 1,
"if": -350000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 869.05 MHz */
"enable": true,
"radio": 1,
"if": -150000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 869.525 MHz */
"enable": true,
"radio": 1,
"if": 325000
},
"chan_multiSF_6": {
"enable": false,
"radio": 0,
"if": 0
},
"chan_multiSF_7": {
"enable": false,
"radio": 0,
"if": 0
},
"chan_Lora_std": {
/* Lora MAC channel, 250kHz, SF7, 868.3 MHz */
"enable": true,
"radio": 0,
"if": 100000,
"bandwidth": 250000,
"spread_factor": 7
},
"chan_FSK": {
/* FSK 50kbps channel, 868.3 MHz */
"enable": true,
"radio": 0,
"if": 100000,
"bandwidth": 125000,
"datarate": 50000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 0
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 0
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 12,
"rf_power": 0,
"dig_gain": 0
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 1,
"mix_gain": 8,
"rf_power": 3,
"dig_gain": 0
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 0
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 0
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 11,
"dig_gain": 0
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 15,
"rf_power": 13,
"dig_gain": 0
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 2,
"mix_gain": 10,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 16,
"dig_gain": 0
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 20,
"dig_gain": 0
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 3,
"mix_gain": 11,
"rf_power": 23,
"dig_gain": 0
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 3,
"mix_gain": 12,
"rf_power": 24,
"dig_gain": 0
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 13,
"rf_power": 25,
"dig_gain": 0
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 15,
"rf_power": 26,
"dig_gain": 0
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
<<<<<<< HEAD:gps_pkt_fwd/global_conf.json
"forward_crc_disabled": false
=======
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyAMA0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0
>>>>>>> f31ee2e4eb6bcbb0c93ef685716842e151c8bd9f:lora_pkt_fwd/cfg/global_conf.json.PCB_E286.EU868.gps
}
}
@@ -0,0 +1,225 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 1, /* radio_1 provides clock to concentrator */
"lbt_cfg": {
"enable": false,
"rssi_target": 160, /* rssi in dBm = -lbt_rssi_target/2 */
"nb_channel": 1,
"start_freq": 869525000,
"scan_time_us": 5000,
"tx_delay_1ch_us": 4000000,
"tx_delay_2ch_us": 4000000
},
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 867500000,
"rssi_offset": -165.0,
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 868500000,
"rssi_offset": -165.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 868.1 MHz */
"enable": true,
"radio": 1,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 868.5 MHz */
"enable": true,
"radio": 1,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 867.1 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 867.3 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 867.5 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 867.7 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 867.9 MHz */
"enable": true,
"radio": 0,
"if": 400000
},
"chan_Lora_std": {
/* Lora MAC channel, 250kHz, SF7, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000,
"bandwidth": 250000,
"spread_factor": 7
},
"chan_FSK": {
/* FSK 50kbps channel, 868.8 MHz */
"enable": true,
"radio": 1,
"if": 300000,
"bandwidth": 125000,
"datarate": 50000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 3
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 3
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": 0,
"dig_gain": 1
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 0,
"mix_gain": 14,
"rf_power": 3,
"dig_gain": 2
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 3
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 2
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 11,
"dig_gain": 1
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 14,
"rf_power": 13,
"dig_gain": 2
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 16,
"dig_gain": 2
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 20,
"dig_gain": 1
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 2,
"mix_gain": 13,
"rf_power": 23,
"dig_gain": 1
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 2,
"mix_gain": 15,
"rf_power": 25,
"dig_gain": 2
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 26,
"dig_gain": 2
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 27,
"dig_gain": 1
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false
}
}
@@ -0,0 +1,234 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 1, /* radio_1 provides clock to concentrator */
"lbt_cfg": {
"enable": false,
"rssi_target": 160, /* rssi in dBm = -lbt_rssi_target/2 */
"nb_channel": 1,
"start_freq": 869525000,
"scan_time_us": 5000,
"tx_delay_1ch_us": 4000000,
"tx_delay_2ch_us": 4000000
},
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 867500000,
"rssi_offset": -165.0,
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 868500000,
"rssi_offset": -165.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 868.1 MHz */
"enable": true,
"radio": 1,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 868.5 MHz */
"enable": true,
"radio": 1,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 867.1 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 867.3 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 867.5 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 867.7 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 867.9 MHz */
"enable": true,
"radio": 0,
"if": 400000
},
"chan_Lora_std": {
/* Lora MAC channel, 250kHz, SF7, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000,
"bandwidth": 250000,
"spread_factor": 7
},
"chan_FSK": {
/* FSK 50kbps channel, 868.8 MHz */
"enable": true,
"radio": 1,
"if": 300000,
"bandwidth": 125000,
"datarate": 50000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 3
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 3
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": 0,
"dig_gain": 1
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 0,
"mix_gain": 14,
"rf_power": 3,
"dig_gain": 2
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 3
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 2
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 11,
"dig_gain": 1
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 14,
"rf_power": 13,
"dig_gain": 2
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 16,
"dig_gain": 2
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 20,
"dig_gain": 1
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 2,
"mix_gain": 13,
"rf_power": 23,
"dig_gain": 1
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 2,
"mix_gain": 15,
"rf_power": 25,
"dig_gain": 2
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 26,
"dig_gain": 2
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 27,
"dig_gain": 1
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyAMA0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0,
/* Beaconing parameters */
"beacon_period": 128,
"beacon_freq_hz": 869525000
}
}
@@ -0,0 +1,231 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 1, /* radio_1 provides clock to concentrator */
"lbt_cfg": {
"enable": false,
"rssi_target": 160, /* rssi in dBm = -lbt_rssi_target/2 */
"nb_channel": 1,
"start_freq": 869525000,
"scan_time_us": 5000,
"tx_delay_1ch_us": 4000000,
"tx_delay_2ch_us": 4000000
},
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 867500000,
"rssi_offset": -165.0,
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 868500000,
"rssi_offset": -165.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 868.1 MHz */
"enable": true,
"radio": 1,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 868.5 MHz */
"enable": true,
"radio": 1,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 867.1 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 867.3 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 867.5 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 867.7 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 867.9 MHz */
"enable": true,
"radio": 0,
"if": 400000
},
"chan_Lora_std": {
/* Lora MAC channel, 250kHz, SF7, 868.3 MHz */
"enable": true,
"radio": 1,
"if": -200000,
"bandwidth": 250000,
"spread_factor": 7
},
"chan_FSK": {
/* FSK 50kbps channel, 868.8 MHz */
"enable": true,
"radio": 1,
"if": 300000,
"bandwidth": 125000,
"datarate": 50000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 3
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 3
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": 0,
"dig_gain": 1
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 0,
"mix_gain": 14,
"rf_power": 3,
"dig_gain": 2
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 3
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 2
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 11,
"dig_gain": 1
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 14,
"rf_power": 13,
"dig_gain": 2
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 16,
"dig_gain": 2
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 20,
"dig_gain": 1
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 2,
"mix_gain": 13,
"rf_power": 23,
"dig_gain": 1
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 2,
"mix_gain": 15,
"rf_power": 25,
"dig_gain": 2
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 26,
"dig_gain": 2
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 27,
"dig_gain": 1
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyAMA0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0
}
}
@@ -0,0 +1,104 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 1, /* radio_1 provides clock to concentrator */
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 902700000,
"rssi_offset": -166.0,
"tx_enable": true,
"tx_freq_min": 902000000,
"tx_freq_max": 928000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 903400000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 902.3 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 902.5 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 902.7 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 902.9 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 903.1 MHz */
"enable": true,
"radio": 1,
"if": -300000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 903.3 MHz */
"enable": true,
"radio": 1,
"if": -100000
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 903.5 MHz */
"enable": true,
"radio": 1,
"if": 100000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 903.7 MHz */
"enable": true,
"radio": 1,
"if": 300000
},
"chan_Lora_std": {
/* Lora MAC channel, 500kHz, SF8, 903.0 MHz */
"enable": true,
"radio": 0,
"if": 300000,
"bandwidth": 500000,
"spread_factor": 8
},
"chan_FSK": {
/* FSK 100kbps channel, 903.0 MHz */
"enable": false,
"radio": 0,
"if": 300000,
"bandwidth": 250000,
"datarate": 100000
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false
}
}
@@ -0,0 +1,110 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 1, /* radio_1 provides clock to concentrator */
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 902700000,
"rssi_offset": -166.0,
"tx_enable": true,
"tx_freq_min": 902000000,
"tx_freq_max": 928000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 903400000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 902.3 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 902.5 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 902.7 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 902.9 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 903.1 MHz */
"enable": true,
"radio": 1,
"if": -300000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 903.3 MHz */
"enable": true,
"radio": 1,
"if": -100000
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 903.5 MHz */
"enable": true,
"radio": 1,
"if": 100000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 903.7 MHz */
"enable": true,
"radio": 1,
"if": 300000
},
"chan_Lora_std": {
/* Lora MAC channel, 500kHz, SF8, 903.0 MHz */
"enable": true,
"radio": 0,
"if": 300000,
"bandwidth": 500000,
"spread_factor": 8
},
"chan_FSK": {
/* FSK 100kbps channel, 903.0 MHz */
"enable": false,
"radio": 0,
"if": 300000,
"bandwidth": 250000,
"datarate": 100000
}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports, or overwrite in local_conf.json */
"server_address": "localhost",
"serv_port_up": 1680,
"serv_port_down": 1680,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyAMA0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0
}
}
@@ -0,0 +1,237 @@
{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 0, /* radio_1 provides clock to concentrator */
"antenna_gain": 0, /* antenna gain, in dBi */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 917200000,
"rssi_offset": -166.0,
"tx_enable": true,
"tx_freq_min": 915000000,
"tx_freq_max": 928000000
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 917900000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
/* Lora MAC channel, 125kHz, all SF, 916.8 MHz */
"enable": true,
"radio": 0,
"if": -400000
},
"chan_multiSF_1": {
/* Lora MAC channel, 125kHz, all SF, 917.0 MHz */
"enable": true,
"radio": 0,
"if": -200000
},
"chan_multiSF_2": {
/* Lora MAC channel, 125kHz, all SF, 917.2 MHz */
"enable": true,
"radio": 0,
"if": 0
},
"chan_multiSF_3": {
/* Lora MAC channel, 125kHz, all SF, 917.4 MHz */
"enable": true,
"radio": 0,
"if": 200000
},
"chan_multiSF_4": {
/* Lora MAC channel, 125kHz, all SF, 917.6 MHz */
"enable": true,
"radio": 1,
"if": -300000
},
"chan_multiSF_5": {
/* Lora MAC channel, 125kHz, all SF, 917.8 MHz */
"enable": true,
"radio": 1,
"if": -100000
},
"chan_multiSF_6": {
/* Lora MAC channel, 125kHz, all SF, 918.0 MHz */
"enable": true,
"radio": 1,
"if": 100000
},
"chan_multiSF_7": {
/* Lora MAC channel, 125kHz, all SF, 918.2 MHz */
"enable": true,
"radio": 1,
"if": 300000
},
"chan_Lora_std": {
/* Lora MAC channel, 500kHz, SF8, 917.5 MHz */
"enable": true,
"radio": 0,
"if": 300000,
"bandwidth": 500000,
"spread_factor": 8
},
"chan_FSK": {
/* Disabled */
"enable": false,
"radio": 0,
"if": 300000,
"bandwidth": 250000,
"datarate": 100000,
"freq_deviation" : 25000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 0
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 0
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 12,
"rf_power": 0,
"dig_gain": 0
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 1,
"mix_gain": 8,
"rf_power": 3,
"dig_gain": 0
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 0
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 0
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 11,
"dig_gain": 0
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 15,
"rf_power": 13,
"dig_gain": 0
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 2,
"mix_gain": 10,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 16,
"dig_gain": 0
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 3,
"mix_gain": 9,
"rf_power": 20,
"dig_gain": 0
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 23,
"dig_gain": 0
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 3,
"mix_gain": 11,
"rf_power": 25,
"dig_gain": 0
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 12,
"rf_power": 26,
"dig_gain": 0
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 14,
"rf_power": 27,
"dig_gain": 0
}
},
"gateway_conf": {
/* change with default server address/ports, or overwrite in local_conf.json */
"gateway_ID": "b827ebFFFE9e119b",
/* Devices */
"gps": false,
"beacon": false,
"monitor": false,
"upstream": false,
"downstream": false,
"ghoststream": false,
"radiostream": false,
"statusstream": false,
/* node server */
"server_address": "router.au.thethings.network",
"serv_port_up": 1700,
"serv_port_down": 1700,
/* node servers for poly packet server (max 4) */
"servers":
[ { "server_address": "localhost",
"serv_port_up": 1700,
"serv_port_down": 1700,
"serv_enabled": true }],
/* adjust the following parameters for your network */
"keepalive_interval": 12,
"stat_interval": 20,
"push_timeout_ms": 120,
"synch_word": 52 ,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": true,
"forward_crc_disabled": true,
/* Email of gateway operator, max 40 chars*/
"contact_email": "operator@gateway.tst",
/* Public description of this device, max 64 chars */
"description": "Update me"
}
}
@@ -0,0 +1,62 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Base64 encoding & decoding library
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
#ifndef _BASE64_H
#define _BASE64_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Encode binary data in Base64 string (no padding)
@param in pointer to a table of binary data
@param size number of bytes to be encoded to base64
@param out pointer to a string where the function will output encoded data
@param max_len max length of the out string (including null char)
@return >=0 length of the resulting string (w/o null char), -1 for error
*/
int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len);
/**
@brief Decode Base64 string to binary data (no padding)
@param in string containing only base64 valid characters
@param size number of characters to be decoded from base64 (w/o null char)
@param out pointer to a data buffer where the function will output decoded data
@param out_max_len usable size of the output data buffer
@return >=0 number of bytes written to the data buffer, -1 for error
*/
int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len);
/* === derivative functions === */
/**
@brief Encode binary data in Base64 string (with added padding)
*/
int bin_to_b64(const uint8_t * in, int size, char * out, int max_len);
/**
@brief Decode Base64 string to binary data (remove padding if necessary)
*/
int b64_to_bin(const char * in, int size, uint8_t * out, int max_len);
#endif
/* --- EOF ------------------------------------------------------------------ */
@@ -0,0 +1,156 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
LoRa concentrator : Just In Time TX scheduling queue
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Michael Coracin
*/
#ifndef _LORA_PKTFWD_JIT_H
#define _LORA_PKTFWD_JIT_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <sys/time.h> /* timeval */
#include "loragw_hal.h"
#include "loragw_gps.h"
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define JIT_QUEUE_MAX 32 /* Maximum number of packets to be stored in JiT queue */
#define JIT_NUM_BEACON_IN_QUEUE 3 /* Number of beacons to be loaded in JiT queue at any time */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
enum jit_pkt_type_e {
JIT_PKT_TYPE_DOWNLINK_CLASS_A,
JIT_PKT_TYPE_DOWNLINK_CLASS_B,
JIT_PKT_TYPE_DOWNLINK_CLASS_C,
JIT_PKT_TYPE_BEACON
};
enum jit_error_e {
JIT_ERROR_OK, /* Packet ok to be sent */
JIT_ERROR_TOO_LATE, /* Too late to send this packet */
JIT_ERROR_TOO_EARLY, /* Too early to queue this packet */
JIT_ERROR_FULL, /* Downlink queue is full */
JIT_ERROR_EMPTY, /* Downlink queue is empty */
JIT_ERROR_COLLISION_PACKET, /* A packet is already enqueued for this timeframe */
JIT_ERROR_COLLISION_BEACON, /* A beacon is planned for this timeframe */
JIT_ERROR_TX_FREQ, /* The required frequency for downlink is not supported */
JIT_ERROR_TX_POWER, /* The required power for downlink is not supported */
JIT_ERROR_GPS_UNLOCKED, /* GPS timestamp could not be used as GPS is unlocked */
JIT_ERROR_INVALID /* Packet is invalid */
};
struct jit_node_s {
/* API fields */
struct lgw_pkt_tx_s pkt; /* TX packet */
enum jit_pkt_type_e pkt_type; /* Packet type: Downlink, Beacon... */
/* Internal fields */
uint32_t pre_delay; /* Amount of time before packet timestamp to be reserved */
uint32_t post_delay; /* Amount of time after packet timestamp to be reserved (time on air) */
};
struct jit_queue_s {
uint8_t num_pkt; /* Total number of packets in the queue (downlinks, beacons...) */
uint8_t num_beacon; /* Number of beacons in the queue */
struct jit_node_s nodes[JIT_QUEUE_MAX]; /* Nodes/packets array in the queue */
};
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Check if a JiT queue is full.
@param queue[in] Just in Time queue to be checked.
@return true if queue is full, false otherwise.
*/
bool jit_queue_is_full(struct jit_queue_s *queue);
/**
@brief Check if a JiT queue is empty.
@param queue[in] Just in Time queue to be checked.
@return true if queue is empty, false otherwise.
*/
bool jit_queue_is_empty(struct jit_queue_s *queue);
/**
@brief Initialize a Just in Time queue.
@param queue[in] Just in Time queue to be initialized. Memory should have been allocated already.
This function is used to reset every elements in the allocated queue.
*/
void jit_queue_init(struct jit_queue_s *queue);
/**
@brief Add a packet in a Just-in-Time queue
@param queue[in/out] Just in Time queue in which the packet should be inserted
@param time[in] Current concentrator time
@param packet[in] Packet to be queued in JiT queue
@param pkt_type[in] Type of packet to be queued: Downlink, Beacon
@return success if the function was able to queue the packet
This function is typically used when a packet is received from server for downlink.
It will check if packet can be queued, with several criterias. Once the packet is queued, it has to be
sent over the air. So all checks should happen before the packet being actually in the queue.
*/
enum jit_error_e jit_enqueue(struct jit_queue_s *queue, struct timeval *time, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e pkt_type);
/**
@brief Dequeue a packet from a Just-in-Time queue
@param queue[in/out] Just in Time queue from which the packet should be removed
@param index[in] in the queue where to get the packet to be removed
@param packet[out] that was at index
@param pkt_type[out] Type of packet dequeued: Downlink, Beacon
@return success if the function was able to dequeue the packet
This function is typically used when a packet is about to be placed on concentrator buffer for TX.
The index is generally got using the jit_peek function.
*/
enum jit_error_e jit_dequeue(struct jit_queue_s *queue, int index, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e *pkt_type);
/**
@brief Check if there is a packet soon to be sent from the JiT queue.
@param queue[in] Just in Time queue to parse for peeking a packet
@param time[in] Current concentrator time
@param pkt_idx[out] Packet index which is soon to be dequeued.
@return success if the function was able to parse the queue. pkt_idx is set to -1 if no packet found.
This function is typically used to check in JiT queue if there is a packet soon to be sent.
It search the packet with the highest priority in queue, and check if its timestamp is near
enough the current concentrator time.
*/
enum jit_error_e jit_peek(struct jit_queue_s *queue, struct timeval *time, int *pkt_idx);
/**
@brief Debug function to print the queue's content on console
@param queue[in] Just in Time queue to be displayed
@param show_all[in] Indicates if empty nodes have to be displayed or not
*/
void jit_print_queue(struct jit_queue_s *queue, bool show_all, int debug_level);
#endif
/* --- EOF ------------------------------------------------------------------ */
@@ -0,0 +1,111 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (C) 2013 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef parson_parson_h
#define parson_parson_h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h> /* size_t */
#define PARSON_VERSION 20131130
/* Types and enums */
typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
typedef struct json_value_t JSON_Value;
typedef enum json_value_type {
JSONError = 0,
JSONNull = 1,
JSONString = 2,
JSONNumber = 3,
JSONObject = 4,
JSONArray = 5,
JSONBoolean = 6
} JSON_Value_Type;
/* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename);
/* Parses first JSON value in a file and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_file_with_comments(const char *filename);
/* Parses first JSON value in a string, returns NULL in case of error */
JSON_Value * json_parse_string(const char *string);
/* Parses first JSON value in a string and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_string_with_comments(const char *string);
/* JSON Object */
JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
const char * json_object_get_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name);
int json_object_get_boolean(const JSON_Object *object, const char *name);
/* dotget functions enable addressing values with dot notation in nested objects,
just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
Because valid names in JSON can contain dots, some values may be inaccessible
this way. */
JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name);
const char * json_object_dotget_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name);
int json_object_dotget_boolean(const JSON_Object *object, const char *name);
/* Functions to get available names */
size_t json_object_get_count(const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index);
/* JSON Array */
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
const char * json_array_get_string (const JSON_Array *array, size_t index);
JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index);
int json_array_get_boolean(const JSON_Array *array, size_t index);
size_t json_array_get_count (const JSON_Array *array);
/* JSON Value */
JSON_Value_Type json_value_get_type (const JSON_Value *value);
JSON_Object * json_value_get_object (const JSON_Value *value);
JSON_Array * json_value_get_array (const JSON_Value *value);
const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value);
void json_value_free (JSON_Value *value);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,32 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
LoRa concentrator : Just In Time TX scheduling queue
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Michael Coracin
*/
#ifndef _LORA_PKTFWD_TIMERSYNC_H
#define _LORA_PKTFWD_TIMERSYNC_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <sys/time.h> /* timeval */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
int get_concentrator_time(struct timeval *concent_time, struct timeval unix_time);
void thread_timersync(void);
#endif
@@ -0,0 +1,37 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
LoRa concentrator : Packet Forwarder trace helpers
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Michael Coracin
*/
#ifndef _LORA_PKTFWD_TRACE_H
#define _LORA_PKTFWD_TRACE_H
#define DEBUG_PKT_FWD 0
#define DEBUG_JIT 0
#define DEBUG_JIT_ERROR 1
#define DEBUG_TIMERSYNC 0
#define DEBUG_BEACON 0
#define DEBUG_LOG 1
#define MSG(args...) printf(args) /* message that is destined to the user */
#define MSG_DEBUG(FLAG, fmt, ...) \
do { \
if (FLAG) \
fprintf(stdout, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
} while (0)
#endif
/* --- EOF ------------------------------------------------------------------ */
@@ -0,0 +1,7 @@
{
/* Put there parameters that are different for each gateway (eg. pointing one gateway to a test server while the others stay in production) */
/* Settings defined in global_conf will be overwritten by those in local_conf */
"gateway_conf": {
"gateway_ID": "b827ebFFFE9e119b"
}
}
@@ -0,0 +1,329 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Lora Gateway packet forwarder
=============================
1. Introduction
----------------
The packet forwarder is a program running on the host of a Lora gateway that
forwards RF packets receive by the concentrator to a server through a IP/UDP
link, and emits RF packets that are sent by the server. It can also emit a
network-wide GPS-synchronous beacon signal used for coordinating all nodes of
the network.
To learn more about the network protocol between the gateway and the server,
please read the PROTOCOL.TXT document.
2. System schematic and definitions
------------------------------------
((( Y )))
|
|
+- -|- - - - - - - - - - - - -+ xxxxxxxxxxxx +--------+
|+--+-----------+ +------+| xx x x xxx | |
|| | | || xx Internet xx | |
|| Concentrator |<----+ Host |<------xx or xx-------->| |
|| | SPI | || xx Intranet xx | Server |
|+--------------+ +------+| xxxx x xxxx | |
| ^ ^ | xxxxxxxx | |
| | PPS +-----+ NMEA | | | |
| +------| GPS |-------+ | +--------+
| +-----+ |
| |
| Gateway |
+- - - - - - - - - - - - - - -+
Concentrator: radio RX/TX board, based on Semtech multichannel modems (SX130x),
transceivers (SX135x) and/or low-power stand-alone modems (SX127x).
Host: embedded computer on which the packet forwarder is run. Drives the
concentrator through a SPI link.
Gateway: a device composed of at least one radio concentrator, a host, some
network connection to the internet or a private network (Ethernet, 3G, Wifi,
microwave link), and optionally a GPS receiver for synchronization.
Server: an abstract computer that will process the RF packets received and
forwarded by the gateway, and issue RF packets in response that the gateway
will have to emit.
3. Dependencies
----------------
This program uses the Parson library (http://kgabis.github.com/parson/) by
Krzysztof Gabis for JSON parsing.
Many thanks to him for that very practical and well written library.
This program is statically linked with the libloragw Lora concentrator library.
It was tested with v1.3.0 of the library but should work with any later
version provided the API is v1 or a later backward-compatible API.
Data structures of the received packets are accessed by name (ie. not at a
binary level) so new functionalities can be added to the API without affecting
that program at all.
This program follows the v1.3 version of the gateway-to-server protocol.
The last dependency is the hardware concentrator (based on FPGA or SX130x
chips) that must be matched with the proper version of the HAL.
4. Usage
---------
* Pick the global_conf.json file from cfg/ directory that fit with your
platform, region and feature need.
* Update the JSON configuration (global and local) files, as explained below.
* For IoT Starter Kit only, run:
./reset_lgw.sh stop
./reset_lgw.sh start
* Run:
./update_gwid.sh local_conf.json (OPTIONAL)
./lora_pkt_fwd
To stop the application, press Ctrl+C.
Unless it is manually stopped or encounter a critical error, the program will
run forever.
There are no command line launch options.
The way the program takes configuration files into account is the following:
* if there is a debug_conf.json parse it, others are ignored
* if there is a global_conf.json parse it, look for the next file
* if there is a local_conf.json parse it
If some parameters are defined in both global and local configuration files,
the local definition overwrites the global definition.
The global configuration file should be exactly the same throughout your
network, contain all global parameters (parameters for "sensor" radio
channels) and preferably default "safe" values for parameters that are
specific for each gateway (eg. specify a default MAC address).
As some of the parameters (like 'rssi_offset', 'tx_lut_*') are board dependant,
several flavours of the global_conf.json file are provided in the cfg/
directory.
* global_conf.json.PCB_E286.EU868.*: to be used for Semtech reference design
board with PCB name PCB_E286 (also called Gateway Board v1.0 (no FPGA)).
Configured for Europe 868MHz channels.
* global_conf.json.PCB_E336.EU868.*:to be used for Semtech reference design
board with PCB name PCB_E336 (also called Gateway Board v1.5 (with FPGA)).
Configured for Europe 868MHz channels.
* global_conf.json.US902.*: to be used for Semtech reference design v1.0 or
v1.5. (No calibration done for RSSI offset and TX gains yet).
Configured for US 902MHz channels.
Beside board related flavours, there are "features" flavours named "basic",
"gps", "beacon".
* global_conf.json.*.basic: to be used for basic packet forwarder usage, with
no GPS.
* global_conf.json.*.gps: to be used when the platform has a GPS receiver.
* global_conf.json.*.beacon: to be used when the platform has a GPS receiver
and we want the packet forwarder to emit beacons for synchronized networks.
Rename the one you need to global_conf.json before launching the packet
forwarder.
The local configuration file should contain parameters that are specific to
each gateway (eg. MAC address, frequency for backhaul radio channels).
In each configuration file, the program looks for a JSON object named
"SX1301_conf" that should contain the parameters for the Lora concentrator
board (RF channels definition, modem parameters, etc) and another JSON object
called "gateway_conf" that should contain the gateway parameters (gateway MAC
address, IP address of the server, keep-alive time, etc).
To learn more about the JSON configuration format, read the provided JSON
files and the libloragw API documentation.
Every X seconds (parameter settable in the configuration files) the program
display statistics on the RF packets received and sent, and the network
datagrams received and sent.
The program also send some statistics to the server in JSON format.
5. "Just-In-Time" downlink scheduling
-------------------------------------
The LoRa concentrator can have only one TX packet programmed for departure at a
time. The actual departure of a downlink packet will be done based on its
timestamp, when the concentrator internal counter reaches timestamps value.
The departure of a beacon will be done based on a GPS PPS event.
It may happen that, due to network variable latency, the gateway receives one
or many downlink packets from the server while a TX is already programmed in the
concentrator. The packet forwarder has to store and order incoming downlink
packets (in a queue), so that they can all be programmed in the concentrator at
the proper time and sent over the air.
Possible failures that may occur and that have to be reported to the server are:
- It is too early or too late to send a given packet
- A packet collides with another packet already queued
- A packet collides with a beacon
- TX RF parameters (frequency, power) are not supported by gateway
- Gateways GPS is unlocked, so cannot process Class B downlink
It is called "Just-in-Time" (JiT) scheduling, because the packet forwarder will
program a downlink or a beacon packet in the concentrator just before it has to
be sent over the air.
Another benefit of JiT is to optimize the gateway downlink capacity by avoiding
to keep the concentrator TX buffer busy for too long.
In order to achieve "Just-in-Time" scheduling, the following software elements
have been added:
- A JiT queue, with associated enqueue/peek/dequeue functions and packet
acceptance criterias. It is where downlink packets are stored, waiting to be
sent.
- A JiT thread, which regularly checks if there is a packet in the JiT queue
ready to be programmed in the concentrator, based on current concentrator
internal time.
- A Timer synchronization thread to keep the concentrator clock and Unix clock
synchronized so that host processor can determine if a packet with a given
timestamp can be programmed in the concentrator or not.
5.1. Concentrator vs Unix time synchronization
In order for the host to know if an incoming downlink packet can or cannot be
queued in JiT queue for later transmission, it has to check if the timestamp of
the packet designates a time later than the current concentrator counter or if
it is already too late to be passed to the concentrator.
In order to get current concentrator time, we can use the lgw_get_trigcnt() HAL
function. The problem is that the sample register used to read this value can be
configured in 2 different ways:
- Real time mode: when GPS is disabled, the value read in sample register is
the actual concentrator counter value.
- PPS mode: when GPS is enabled, the value read in sample register is the
value
that the concentrator counter had when last GPSs PPS occurred. So this changes
every second only.
As in our case GPS is enabled (LGW_GPS_EN==1), we need to have a way to get the
actual concentrator current time, at any time.
For this, a new thread has been added to the packet forwarder (thread_timersync)
which will regularly:
- Disable GPS mode of SX1301 counter sampler
- Get current Unix time
- Get current SX1301 counter
- Compute the offset between Unix and SX1301 clocks and store it
- Re-enable GPS mode of SX1301 counter sampler
Then a new function has been added to estimate the current concentrator counter
at any time based on the current Unix time and offset computed by the timersync
thread.
In addition to this, the Concentrator vs Unix time synchronization is used by
the JiT thread to determine if a packet in the JiT queue has to be sent to the
concentrator for transmission.
So basically it is used for queueing and dequeuing packets to/from the JiT queue.
5.2. Concentrator vs GPS time synchronization
There are 2 cases for which we need to convert a GPS time to concentrator
counter:
- Class B downlink: when the “time” field of JSON “txpk” is filled instead
of the “tmst” field, we need to be able to determine if the packet can be
queued in JiT queue or not, based on its corresponding concentrator
counter value.
Note: even if a Class-B downlink is given with a GPS timestamp, the
concentrator TX mode is configured as “TIMESTAMP”, and not “ON_GPS”. So
at the end, it is the counter value which will be used for transmission.
- Beacons: beacons transmission time is based on GPS clock, and the
concentrator TX mode is configured as “ON_GPS” for accurate beacon
transmission on GPS PPS event. In this case, the concentrator does not
need the packet counter to be set. But, as the JiT thread decides if a
packet has to be peeked or not, based on its concentrator counter, we need
to have the beacon packet counter set (see next chapter for more details
on JiT scheduling).
We also need to convert a SX1301 counter value to GPS UTC time when we receive
an uplink, in order to fill the “time” field of JSON “rxpk” structure.
5.3. TX scheduling
The JiT queue implemented is a static array of nodes, where each node contains:
- the downlink packet, with its type (beacon, downlink class A, B or C)
- a “pre delay” which depends on packet type (BEACON_GUARD, TX_START_DELAY…)
- a “post delay” which depends on packet type (“time on air” of this packet
computed based on its size, datarate and coderate, or BEACON_RESERVED)
Several functions are implemented to manipulate this queue or get info from it:
- init: initialize array with default values
- is full / is empty: gives queue status
- enqueue: checks if the given packet can be queued or not, based on several
criterias
- peek: checks if the queue contains a packet that must be passed
immediately to the concentrator for transmission and returns corresponding
index if any.
- dequeue: actually removes from the queue the packet at index given by peek
function
The queue is always kept sorted on ascending timestamp order.
The JiT thread will regularly check in the JiT queue if there is a packet to be
sent soon. If a packet is matching, it is dequeued and programmed in the
concentrator TX buffer.
5.4. Fine tuning parameters
There are few parameters of the JiT queue which could be tweaked to adapt to
different system constraints.
- inc/jitqueue.h:
JIT_QUEUE_MAX: The maximum number of nodes in the queue.
- src/jitqueue.c:
TX_JIT_DELAY: The number of milliseconds a packet is programmed in the
concentrator TX buffer before its actual departure time.
TX_MARGIN_DELAY: Packet collision check margin
6. License
-----------
Copyright (C) 2013, 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.
6. License for Parson library
------------------------------
Parson ( http://kgabis.github.com/parson/ )
Copyright (C) 2012 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*EOF*
@@ -0,0 +1,308 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Base64 encoding & decoding library
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "base64.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE)
//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */
#define DEBUG(args...)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */
static char code_62 = '+'; /* RFC 1421 standard character for code 62 */
static char code_63 = '/'; /* RFC 1421 standard character for code 63 */
static char code_pad = '='; /* RFC 1421 padding character if padding */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
/**
@brief Convert a code in the range 0-63 to an ASCII character
*/
char code_to_char(uint8_t x);
/**
@brief Convert an ASCII character to a code in the range 0-63
*/
uint8_t char_to_code(char x);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
char code_to_char(uint8_t x) {
if (x <= 25) {
return 'A' + x;
} else if ((x >= 26) && (x <= 51)) {
return 'a' + (x-26);
} else if ((x >= 52) && (x <= 61)) {
return '0' + (x-52);
} else if (x == 62) {
return code_62;
} else if (x == 63) {
return code_63;
} else {
DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x);
exit(EXIT_FAILURE);
} //TODO: improve error management
}
uint8_t char_to_code(char x) {
if ((x >= 'A') && (x <= 'Z')) {
return (uint8_t)x - (uint8_t)'A';
} else if ((x >= 'a') && (x <= 'z')) {
return (uint8_t)x - (uint8_t)'a' + 26;
} else if ((x >= '0') && (x <= '9')) {
return (uint8_t)x - (uint8_t)'0' + 52;
} else if (x == code_62) {
return 62;
} else if (x == code_63) {
return 63;
} else {
DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x);
exit(EXIT_FAILURE);
} //TODO: improve error management
}
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) {
int i;
int result_len; /* size of the result */
int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
int last_bytes; /* number of unsigned chars <3 in the last block */
int last_chars; /* number of characters <4 in the last block */
uint32_t b;
/* check input values */
if ((out == NULL) || (in == NULL)) {
DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n");
return -1;
}
if (size == 0) {
*out = 0; /* null string */
return 0;
}
/* calculate the number of base64 'blocks' */
full_blocks = size / 3;
last_bytes = size % 3;
switch (last_bytes) {
case 0: /* no byte left to encode */
last_chars = 0;
break;
case 1: /* 1 byte left to encode -> +2 chars */
last_chars = 2;
break;
case 2: /* 2 bytes left to encode -> +3 chars */
last_chars = 3;
break;
default:
CRIT("switch default that should not be possible");
}
/* check if output buffer is big enough */
result_len = (4*full_blocks) + last_chars;
if (max_len < (result_len + 1)) { /* 1 char added for string terminator */
DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n");
return -1;
}
/* process all the full blocks */
for (i=0; i < full_blocks; ++i) {
b = (0xFF & in[3*i] ) << 16;
b |= (0xFF & in[3*i + 1]) << 8;
b |= 0xFF & in[3*i + 2];
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
out[4*i + 3] = code_to_char( b & 0x3F);
}
/* process the last 'partial' block and terminate string */
i = full_blocks;
if (last_chars == 0) {
out[4*i] = 0; /* null character to terminate string */
} else if (last_chars == 2) {
b = (0xFF & in[3*i] ) << 16;
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = 0; /* null character to terminate string */
} else if (last_chars == 3) {
b = (0xFF & in[3*i] ) << 16;
b |= (0xFF & in[3*i + 1]) << 8;
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
out[4*i + 3] = 0; /* null character to terminate string */
}
return result_len;
}
int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) {
int i;
int result_len; /* size of the result */
int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
int last_chars; /* number of characters <4 in the last block */
int last_bytes; /* number of unsigned chars <3 in the last block */
uint32_t b;
;
/* check input values */
if ((out == NULL) || (in == NULL)) {
DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
return -1;
}
if (size == 0) {
return 0;
}
/* calculate the number of base64 'blocks' */
full_blocks = size / 4;
last_chars = size % 4;
switch (last_chars) {
case 0: /* no char left to decode */
last_bytes = 0;
break;
case 1: /* only 1 char left is an error */
DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n");
return -1;
case 2: /* 2 chars left to decode -> +1 byte */
last_bytes = 1;
break;
case 3: /* 3 chars left to decode -> +2 bytes */
last_bytes = 2;
break;
default:
CRIT("switch default that should not be possible");
}
/* check if output buffer is big enough */
result_len = (3*full_blocks) + last_bytes;
if (max_len < result_len) {
DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n");
return -1;
}
/* process all the full blocks */
for (i=0; i < full_blocks; ++i) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
b |= 0x3F & char_to_code(in[4*i + 3]);
out[3*i + 0] = (b >> 16) & 0xFF;
out[3*i + 1] = (b >> 8 ) & 0xFF;
out[3*i + 2] = b & 0xFF;
}
/* process the last 'partial' block */
i = full_blocks;
if (last_bytes == 1) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
out[3*i + 0] = (b >> 16) & 0xFF;
if (((b >> 12) & 0x0F) != 0) {
DEBUG("WARNING: last character contains unusable bits\n");
}
} else if (last_bytes == 2) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
out[3*i + 0] = (b >> 16) & 0xFF;
out[3*i + 1] = (b >> 8 ) & 0xFF;
if (((b >> 6) & 0x03) != 0) {
DEBUG("WARNING: last character contains unusable bits\n");
}
}
return result_len;
}
int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) {
int ret;
ret = bin_to_b64_nopad(in, size, out, max_len);
if (ret == -1) {
return -1;
}
switch (ret%4) {
case 0: /* nothing to do */
return ret;
case 1:
DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n");
return -1;
case 2: /* 2 chars in last block, must add 2 padding char */
if (max_len >= (ret + 2 + 1)) {
out[ret] = code_pad;
out[ret+1] = code_pad;
out[ret+2] = 0;
return ret+2;
} else {
DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
return -1;
}
case 3: /* 3 chars in last block, must add 1 padding char */
if (max_len >= (ret + 1 + 1)) {
out[ret] = code_pad;
out[ret+1] = 0;
return ret+1;
} else {
DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
return -1;
}
default:
CRIT("switch default that should not be possible");
}
}
int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) {
if (in == NULL) {
DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
return -1;
}
if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */
if (in[size-2] == code_pad) { /* 2 padding char to ignore */
return b64_to_bin_nopad(in, size-2, out, max_len);
} else if (in[size-1] == code_pad) { /* 1 padding char to ignore */
return b64_to_bin_nopad(in, size-1, out, max_len);
} else { /* no padding to ignore */
return b64_to_bin_nopad(in, size, out, max_len);
}
} else { /* treat as unpadded Base64 */
return b64_to_bin_nopad(in, size, out, max_len);
}
}
/* --- EOF ------------------------------------------------------------------ */
@@ -0,0 +1,528 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
LoRa concentrator : Just In Time TX scheduling queue
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Michael Coracin
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#define _GNU_SOURCE /* needed for qsort_r to be defined */
#include <stdlib.h> /* qsort_r */
#include <stdio.h> /* printf, fprintf, snprintf, fopen, fputs */
#include <string.h> /* memset, memcpy */
#include <pthread.h>
#include <assert.h>
#include <math.h>
#include "trace.h"
#include "jitqueue.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */
#define TX_START_DELAY 1500 /* microseconds */
/* TODO: get this value from HAL? */
#define TX_MARGIN_DELAY 1000 /* Packet overlap margin in microseconds */
/* TODO: How much margin should we take? */
#define TX_JIT_DELAY 30000 /* Pre-delay to program packet for TX in microseconds */
#define TX_MAX_ADVANCE_DELAY ((JIT_NUM_BEACON_IN_QUEUE + 1) * 128 * 1E6) /* Maximum advance delay accepted for a TX packet, compared to current time */
#define BEACON_GUARD 3000000 /* Interval where no ping slot can be placed,
to ensure beacon can be sent */
#define BEACON_RESERVED 2120000 /* Time on air of the beacon, with some margin */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */
static pthread_mutex_t mx_jit_queue = PTHREAD_MUTEX_INITIALIZER; /* control access to JIT queue */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
static uint32_t time_on_air(struct lgw_pkt_tx_s *packet, bool isBeacon) {
uint8_t SF, H, DE;
uint16_t BW;
uint32_t payloadSymbNb, Tpacket;
double Tsym, Tpreamble, Tpayload;
switch (packet->bandwidth) {
case BW_125KHZ:
BW = 125;
break;
case BW_250KHZ:
BW = 250;
break;
case BW_500KHZ:
BW = 500;
break;
default:
MSG("ERROR: Cannot compute time on air for this packet, unsupported bandwidth (%u)\n", packet->bandwidth);
return 0;
}
switch (packet->datarate) {
case DR_LORA_SF7:
SF = 7;
break;
case DR_LORA_SF8:
SF = 8;
break;
case DR_LORA_SF9:
SF = 9;
break;
case DR_LORA_SF10:
SF = 10;
break;
case DR_LORA_SF11:
SF = 11;
break;
case DR_LORA_SF12:
SF = 12;
break;
default:
MSG("ERROR: Cannot compute time on air for this packet, unsupported datarate (%u)\n", packet->datarate);
return 0;
}
/* Duration of 1 symbol */
Tsym = pow(2, SF) / BW;
/* Duration of preamble */
Tpreamble = (8 + 4.25) * Tsym; /* 8 programmed symbols in preamble */
/* Duration of payload */
H = (isBeacon==false)?0:1; /* header is always enabled, except for beacons */
DE = (SF >= 11)?1:0; /* Low datarate optimization enabled for SF11 and SF12 */
payloadSymbNb = 8 + (ceil((double)(8*packet->size - 4*SF + 28 + 16 - 20*H) / (double)(4*(SF - 2*DE))) * (packet->coderate + 4)); /* Explicitely cast to double to keep precision of the division */
Tpayload = payloadSymbNb * Tsym;
Tpacket = Tpreamble + Tpayload;
return Tpacket;
}
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ----------------------------------------- */
bool jit_queue_is_full(struct jit_queue_s *queue) {
bool result;
pthread_mutex_lock(&mx_jit_queue);
result = (queue->num_pkt == JIT_QUEUE_MAX)?true:false;
pthread_mutex_unlock(&mx_jit_queue);
return result;
}
bool jit_queue_is_empty(struct jit_queue_s *queue) {
bool result;
pthread_mutex_lock(&mx_jit_queue);
result = (queue->num_pkt == 0)?true:false;
pthread_mutex_unlock(&mx_jit_queue);
return result;
}
void jit_queue_init(struct jit_queue_s *queue) {
int i;
pthread_mutex_lock(&mx_jit_queue);
memset(queue, 0, sizeof(*queue));
for (i=0; i<JIT_QUEUE_MAX; i++) {
queue->nodes[i].pre_delay = 0;
queue->nodes[i].post_delay = 0;
}
pthread_mutex_unlock(&mx_jit_queue);
}
int compare(const void *a, const void *b, void *arg)
{
struct jit_node_s *p = (struct jit_node_s *)a;
struct jit_node_s *q = (struct jit_node_s *)b;
int *counter = (int *)arg;
int p_count, q_count;
p_count = p->pkt.count_us;
q_count = q->pkt.count_us;
if (p_count > q_count)
*counter = *counter + 1;
return p_count - q_count;
}
void jit_sort_queue(struct jit_queue_s *queue) {
int counter = 0;
if (queue->num_pkt == 0) {
return;
}
MSG_DEBUG(DEBUG_JIT, "sorting queue in ascending order packet timestamp - queue size:%u\n", queue->num_pkt);
qsort_r(queue->nodes, queue->num_pkt, sizeof(queue->nodes[0]), compare, &counter);
MSG_DEBUG(DEBUG_JIT, "sorting queue done - swapped:%d\n", counter);
}
bool jit_collision_test(uint32_t p1_count_us, uint32_t p1_pre_delay, uint32_t p1_post_delay, uint32_t p2_count_us, uint32_t p2_pre_delay, uint32_t p2_post_delay) {
if (((p1_count_us - p2_count_us) <= (p1_pre_delay + p2_post_delay + TX_MARGIN_DELAY)) ||
((p2_count_us - p1_count_us) <= (p2_pre_delay + p1_post_delay + TX_MARGIN_DELAY))) {
return true;
} else {
return false;
}
}
enum jit_error_e jit_enqueue(struct jit_queue_s *queue, struct timeval *time, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e pkt_type) {
int i = 0;
uint32_t time_us = time->tv_sec * 1000000UL + time->tv_usec; /* convert time in µs */
uint32_t packet_post_delay = 0;
uint32_t packet_pre_delay = 0;
uint32_t target_pre_delay = 0;
enum jit_error_e err_collision;
uint32_t asap_count_us;
MSG_DEBUG(DEBUG_JIT, "Current concentrator time is %u, pkt_type=%d\n", time_us, pkt_type);
if (packet == NULL) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if (jit_queue_is_full(queue)) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: cannot enqueue packet, JIT queue is full\n");
return JIT_ERROR_FULL;
}
/* Compute packet pre/post delays depending on packet's type */
switch (pkt_type) {
case JIT_PKT_TYPE_DOWNLINK_CLASS_A:
case JIT_PKT_TYPE_DOWNLINK_CLASS_B:
case JIT_PKT_TYPE_DOWNLINK_CLASS_C:
packet_pre_delay = TX_START_DELAY + TX_JIT_DELAY;
packet_post_delay = time_on_air(packet, false) * 1000UL; /* in us */
break;
case JIT_PKT_TYPE_BEACON:
/* As defined in LoRaWAN spec */
packet_pre_delay = TX_START_DELAY + BEACON_GUARD + TX_JIT_DELAY;
packet_post_delay = BEACON_RESERVED;
break;
default:
break;
}
pthread_mutex_lock(&mx_jit_queue);
/* An immediate downlink becomes a timestamped downlink "ASAP" */
/* Set the packet count_us to the first available slot */
if (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_C) {
/* change tx_mode to timestamped */
packet->tx_mode = TIMESTAMPED;
/* Search for the ASAP timestamp to be given to the packet */
asap_count_us = time_us + 1E6; /* TODO: Take 1 second margin, to be refined */
if (queue->num_pkt == 0) {
/* If the jit queue is empty, we can insert this packet */
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink, first in JiT queue (count_us=%u)\n", asap_count_us);
} else {
/* Else we can try to insert it:
- ASAP meaning NOW + MARGIN
- at the last index of the queue
- between 2 downlinks in the queue
*/
/* First, try if the ASAP time collides with an already enqueued downlink */
for (i=0; i<queue->num_pkt; i++) {
if (jit_collision_test(asap_count_us, packet_pre_delay, packet_post_delay, queue->nodes[i].pkt.count_us, queue->nodes[i].pre_delay, queue->nodes[i].post_delay) == true) {
MSG_DEBUG(DEBUG_JIT, "DEBUG: cannot insert IMMEDIATE downlink at count_us=%u, collides with %u (index=%d)\n", asap_count_us, queue->nodes[i].pkt.count_us, i);
break;
}
}
if (i == queue->num_pkt) {
/* No collision with ASAP time, we can insert it */
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink ASAP at %u (no collision)\n", asap_count_us);
} else {
/* Search for the best slot then */
for (i=0; i<queue->num_pkt; i++) {
asap_count_us = queue->nodes[i].pkt.count_us + queue->nodes[i].post_delay + packet_pre_delay + TX_JIT_DELAY + TX_MARGIN_DELAY;
if (i == (queue->num_pkt - 1)) {
/* Last packet index, we can insert after this one */
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink, last in JiT queue (count_us=%u)\n", asap_count_us);
} else {
/* Check if packet can be inserted between this index and the next one */
MSG_DEBUG(DEBUG_JIT, "DEBUG: try to insert IMMEDIATE downlink (count_us=%u) between index %d and index %d?\n", asap_count_us, i, i+1);
if (jit_collision_test(asap_count_us, packet_pre_delay, packet_post_delay, queue->nodes[i+1].pkt.count_us, queue->nodes[i+1].pre_delay, queue->nodes[i+1].post_delay) == true) {
MSG_DEBUG(DEBUG_JIT, "DEBUG: failed to insert IMMEDIATE downlink (count_us=%u), continue...\n", asap_count_us);
continue;
} else {
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink (count_us=%u)\n", asap_count_us);
break;
}
}
}
}
}
/* Set packet with ASAP timestamp */
packet->count_us = asap_count_us;
}
/* Check criteria_1: is it already too late to send this packet ?
* The packet should arrive at least at (tmst - TX_START_DELAY) to be programmed into concentrator
* Note: - Also add some margin, to be checked how much is needed, if needed
* - Valid for both Downlinks and Beacon packets
*
* Warning: unsigned arithmetic (handle roll-over)
* t_packet < t_current + TX_START_DELAY + MARGIN
*/
if ((packet->count_us - time_us) <= (TX_START_DELAY + TX_MARGIN_DELAY + TX_JIT_DELAY)) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet REJECTED, already too late to send it (current=%u, packet=%u, type=%d)\n", time_us, packet->count_us, pkt_type);
pthread_mutex_unlock(&mx_jit_queue);
return JIT_ERROR_TOO_LATE;
}
/* Check criteria_2: Does packet timestamp seem plausible compared to current time
* We do not expect the server to program a downlink too early compared to current time
* Class A: downlink has to be sent in a 1s or 2s time window after RX
* Class B: downlink has to occur in a 128s time window
* Class C: no check needed, departure time has been calculated previously
* So let's define a safe delay above which we can say that the packet is out of bound: TX_MAX_ADVANCE_DELAY
* Note: - Valid for Downlinks only, not for Beacon packets
*
* Warning: unsigned arithmetic (handle roll-over)
t_packet > t_current + TX_MAX_ADVANCE_DELAY
*/
if ((pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_A) || (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_B)) {
if ((packet->count_us - time_us) > TX_MAX_ADVANCE_DELAY) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet REJECTED, timestamp seems wrong, too much in advance (current=%u, packet=%u, type=%d)\n", time_us, packet->count_us, pkt_type);
pthread_mutex_unlock(&mx_jit_queue);
return JIT_ERROR_TOO_EARLY;
}
}
/* Check criteria_3: does this new packet overlap with a packet already enqueued ?
* Note: - need to take into account packet's pre_delay and post_delay of each packet
* - Valid for both Downlinks and beacon packets
* - Beacon guard can be ignored if we try to queue a Class A downlink
*/
for (i=0; i<queue->num_pkt; i++) {
/* We ignore Beacon Guard for Class A/C downlinks */
if (((pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_A) || (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_C)) && (queue->nodes[i].pkt_type == JIT_PKT_TYPE_BEACON)) {
target_pre_delay = TX_START_DELAY;
} else {
target_pre_delay = queue->nodes[i].pre_delay;
}
/* Check if there is a collision
* Warning: unsigned arithmetic (handle roll-over)
* t_packet_new - pre_delay_packet_new < t_packet_prev + post_delay_packet_prev (OVERLAP on post delay)
* t_packet_new + post_delay_packet_new > t_packet_prev - pre_delay_packet_prev (OVERLAP on pre delay)
*/
if (jit_collision_test(packet->count_us, packet_pre_delay, packet_post_delay, queue->nodes[i].pkt.count_us, target_pre_delay, queue->nodes[i].post_delay) == true) {
switch (queue->nodes[i].pkt_type) {
case JIT_PKT_TYPE_DOWNLINK_CLASS_A:
case JIT_PKT_TYPE_DOWNLINK_CLASS_B:
case JIT_PKT_TYPE_DOWNLINK_CLASS_C:
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet (type=%d) REJECTED, collision with packet already programmed at %u (%u)\n", pkt_type, queue->nodes[i].pkt.count_us, packet->count_us);
err_collision = JIT_ERROR_COLLISION_PACKET;
break;
case JIT_PKT_TYPE_BEACON:
if (pkt_type != JIT_PKT_TYPE_BEACON) {
/* do not overload logs for beacon/beacon collision, as it is expected to happen with beacon pre-scheduling algorith used */
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet (type=%d) REJECTED, collision with beacon already programmed at %u (%u)\n", pkt_type, queue->nodes[i].pkt.count_us, packet->count_us);
}
err_collision = JIT_ERROR_COLLISION_BEACON;
break;
default:
MSG("ERROR: Unknown packet type, should not occur, BUG?\n");
assert(0);
break;
}
pthread_mutex_unlock(&mx_jit_queue);
return err_collision;
}
}
/* Finally enqueue it */
/* Insert packet at the end of the queue */
memcpy(&(queue->nodes[queue->num_pkt].pkt), packet, sizeof(struct lgw_pkt_tx_s));
queue->nodes[queue->num_pkt].pre_delay = packet_pre_delay;
queue->nodes[queue->num_pkt].post_delay = packet_post_delay;
queue->nodes[queue->num_pkt].pkt_type = pkt_type;
if (pkt_type == JIT_PKT_TYPE_BEACON) {
queue->num_beacon++;
}
queue->num_pkt++;
/* Sort the queue in ascending order of packet timestamp */
jit_sort_queue(queue);
/* Done */
pthread_mutex_unlock(&mx_jit_queue);
jit_print_queue(queue, false, DEBUG_JIT);
MSG_DEBUG(DEBUG_JIT, "enqueued packet with count_us=%u (size=%u bytes, toa=%u us, type=%u)\n", packet->count_us, packet->size, packet_post_delay, pkt_type);
return JIT_ERROR_OK;
}
enum jit_error_e jit_dequeue(struct jit_queue_s *queue, int index, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e *pkt_type) {
if (packet == NULL) {
MSG("ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if ((index < 0) || (index >= JIT_QUEUE_MAX)) {
MSG("ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if (jit_queue_is_empty(queue)) {
MSG("ERROR: cannot dequeue packet, JIT queue is empty\n");
return JIT_ERROR_EMPTY;
}
pthread_mutex_lock(&mx_jit_queue);
/* Dequeue requested packet */
memcpy(packet, &(queue->nodes[index].pkt), sizeof(struct lgw_pkt_tx_s));
queue->num_pkt--;
*pkt_type = queue->nodes[index].pkt_type;
if (*pkt_type == JIT_PKT_TYPE_BEACON) {
queue->num_beacon--;
MSG_DEBUG(DEBUG_BEACON, "--- Beacon dequeued ---\n");
}
/* Replace dequeued packet with last packet of the queue */
memcpy(&(queue->nodes[index]), &(queue->nodes[queue->num_pkt]), sizeof(struct jit_node_s));
memset(&(queue->nodes[queue->num_pkt]), 0, sizeof(struct jit_node_s));
/* Sort queue in ascending order of packet timestamp */
jit_sort_queue(queue);
/* Done */
pthread_mutex_unlock(&mx_jit_queue);
jit_print_queue(queue, false, DEBUG_JIT);
MSG_DEBUG(DEBUG_JIT, "dequeued packet with count_us=%u from index %d\n", packet->count_us, index);
return JIT_ERROR_OK;
}
enum jit_error_e jit_peek(struct jit_queue_s *queue, struct timeval *time, int *pkt_idx) {
/* Return index of node containing a packet inline with given time */
int i = 0;
int idx_highest_priority = -1;
uint32_t time_us;
if ((time == NULL) || (pkt_idx == NULL)) {
MSG("ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if (jit_queue_is_empty(queue)) {
return JIT_ERROR_EMPTY;
}
time_us = time->tv_sec * 1000000UL + time->tv_usec;
pthread_mutex_lock(&mx_jit_queue);
/* Search for highest priority packet to be sent */
for (i=0; i<queue->num_pkt; i++) {
/* First check if that packet is outdated:
* If a packet seems too much in advance, and was not rejected at enqueue time,
* it means that we missed it for peeking, we need to drop it
*
* Warning: unsigned arithmetic
* t_packet > t_current + TX_MAX_ADVANCE_DELAY
*/
if ((queue->nodes[i].pkt.count_us - time_us) >= TX_MAX_ADVANCE_DELAY) {
/* We drop the packet to avoid lock-up */
queue->num_pkt--;
if (queue->nodes[i].pkt_type == JIT_PKT_TYPE_BEACON) {
queue->num_beacon--;
MSG("WARNING: --- Beacon dropped (current_time=%u, packet_time=%u) ---\n", time_us, queue->nodes[i].pkt.count_us);
} else {
MSG("WARNING: --- Packet dropped (current_time=%u, packet_time=%u) ---\n", time_us, queue->nodes[i].pkt.count_us);
}
/* Replace dropped packet with last packet of the queue */
memcpy(&(queue->nodes[i]), &(queue->nodes[queue->num_pkt]), sizeof(struct jit_node_s));
memset(&(queue->nodes[queue->num_pkt]), 0, sizeof(struct jit_node_s));
/* Sort queue in ascending order of packet timestamp */
jit_sort_queue(queue);
/* restart loop after purge to find packet to be sent */
i = 0;
continue;
}
/* Then look for highest priority packet to be sent:
* Warning: unsigned arithmetic (handle roll-over)
* t_packet < t_highest
*/
if ((idx_highest_priority == -1) || (((queue->nodes[i].pkt.count_us - time_us) < (queue->nodes[idx_highest_priority].pkt.count_us - time_us)))) {
idx_highest_priority = i;
}
}
/* Peek criteria 1: look for a packet to be sent in next TX_JIT_DELAY ms timeframe
* Warning: unsigned arithmetic (handle roll-over)
* t_packet < t_current + TX_JIT_DELAY
*/
if ((queue->nodes[idx_highest_priority].pkt.count_us - time_us) < TX_JIT_DELAY) {
*pkt_idx = idx_highest_priority;
MSG_DEBUG(DEBUG_JIT, "peek packet with count_us=%u at index %d\n",
queue->nodes[idx_highest_priority].pkt.count_us, idx_highest_priority);
} else {
*pkt_idx = -1;
}
pthread_mutex_unlock(&mx_jit_queue);
return JIT_ERROR_OK;
}
void jit_print_queue(struct jit_queue_s *queue, bool show_all, int debug_level) {
int i = 0;
int loop_end;
if (jit_queue_is_empty(queue)) {
MSG_DEBUG(debug_level, "INFO: [jit] queue is empty\n");
} else {
pthread_mutex_lock(&mx_jit_queue);
MSG_DEBUG(debug_level, "INFO: [jit] queue contains %d packets:\n", queue->num_pkt);
MSG_DEBUG(debug_level, "INFO: [jit] queue contains %d beacons:\n", queue->num_beacon);
loop_end = (show_all == true) ? JIT_QUEUE_MAX : queue->num_pkt;
for (i=0; i<loop_end; i++) {
MSG_DEBUG(debug_level, " - node[%d]: count_us=%u - type=%d\n",
i,
queue->nodes[i].pkt.count_us,
queue->nodes[i].pkt_type);
}
pthread_mutex_unlock(&mx_jit_queue);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,782 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (C) 2013 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "parson.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define ERROR 0
#define SUCCESS 1
#define STARTING_CAPACITY 15
#define ARRAY_MAX_CAPACITY 122880 /* 15*(2^13) */
#define OBJECT_MAX_CAPACITY 960 /* 15*(2^6) */
#define MAX_NESTING 19
#define sizeof_token(a) (sizeof(a) - 1)
#define skip_char(str) ((*str)++)
#define skip_whitespaces(str) while (isspace(**str)) { skip_char(str); }
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define parson_malloc(a) malloc(a)
#define parson_free(a) free((void*)a)
#define parson_realloc(a, b) realloc(a, b)
/* Type definitions */
typedef union json_value_value {
const char *string;
double number;
JSON_Object *object;
JSON_Array *array;
int boolean;
int null;
} JSON_Value_Value;
struct json_value_t {
JSON_Value_Type type;
JSON_Value_Value value;
};
struct json_object_t {
const char **names;
JSON_Value **values;
size_t count;
size_t capacity;
};
struct json_array_t {
JSON_Value **items;
size_t count;
size_t capacity;
};
/* Various */
static char * read_file(const char *filename);
static void remove_comments(char *string, const char *start_token, const char *end_token);
static int try_realloc(void **ptr, size_t new_size);
static char * parson_strndup(const char *string, size_t n);
static int is_utf(const unsigned char *string);
static int is_decimal(const char *string, size_t length);
/* JSON Object */
static JSON_Object * json_object_init(void);
static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value);
static int json_object_resize(JSON_Object *object, size_t capacity);
static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n);
static void json_object_free(JSON_Object *object);
/* JSON Array */
static JSON_Array * json_array_init(void);
static int json_array_add(JSON_Array *array, JSON_Value *value);
static int json_array_resize(JSON_Array *array, size_t capacity);
static void json_array_free(JSON_Array *array);
/* JSON Value */
static JSON_Value * json_value_init_object(void);
static JSON_Value * json_value_init_array(void);
static JSON_Value * json_value_init_string(const char *string);
static JSON_Value * json_value_init_number(double number);
static JSON_Value * json_value_init_boolean(int boolean);
static JSON_Value * json_value_init_null(void);
/* Parser */
static void skip_quotes(const char **string);
static const char * get_processed_string(const char **string);
static JSON_Value * parse_object_value(const char **string, size_t nesting);
static JSON_Value * parse_array_value(const char **string, size_t nesting);
static JSON_Value * parse_string_value(const char **string);
static JSON_Value * parse_boolean_value(const char **string);
static JSON_Value * parse_number_value(const char **string);
static JSON_Value * parse_null_value(const char **string);
static JSON_Value * parse_value(const char **string, size_t nesting);
/* Various */
static int try_realloc(void **ptr, size_t new_size) {
void *reallocated_ptr = parson_realloc(*ptr, new_size);
if (!reallocated_ptr)
return ERROR;
*ptr = reallocated_ptr;
return SUCCESS;
}
static char * parson_strndup(const char *string, size_t n) {
char *output_string = (char*)parson_malloc(n + 1);
if (!output_string)
return NULL;
output_string[n] = '\0';
strncpy(output_string, string, n);
return output_string;
}
static int is_utf(const unsigned char *s) {
return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]);
}
static int is_decimal(const char *string, size_t length) {
if (length > 1 && string[0] == '0' && string[1] != '.')
return 0;
if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.')
return 0;
while (length--)
if (strchr("xX", string[length]))
return 0;
return 1;
}
static char * read_file(const char * filename) {
FILE *fp = fopen(filename, "r");
size_t file_size;
char *file_contents;
if (!fp)
return NULL;
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
rewind(fp);
file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1));
if (!file_contents) {
fclose(fp);
return NULL;
}
if (fread(file_contents, file_size, 1, fp) < 1) {
if (ferror(fp)) {
fclose(fp);
parson_free(file_contents);
return NULL;
}
}
fclose(fp);
file_contents[file_size] = '\0';
return file_contents;
}
static void remove_comments(char *string, const char *start_token, const char *end_token) {
int in_string = 0, escaped = 0;
size_t i;
char *ptr = NULL, current_char;
size_t start_token_len = strlen(start_token);
size_t end_token_len = strlen(end_token);
if (start_token_len == 0 || end_token_len == 0)
return;
while ((current_char = *string) != '\0') {
if (current_char == '\\' && !escaped) {
escaped = 1;
string++;
continue;
} else if (current_char == '\"' && !escaped) {
in_string = !in_string;
} else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
for(i = 0; i < start_token_len; i++)
string[i] = ' ';
string = string + start_token_len;
ptr = strstr(string, end_token);
if (!ptr)
return;
for (i = 0; i < (ptr - string) + end_token_len; i++)
string[i] = ' ';
string = ptr + end_token_len - 1;
}
escaped = 0;
string++;
}
}
/* JSON Object */
static JSON_Object * json_object_init(void) {
JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
if (!new_obj)
return NULL;
new_obj->names = (const char**)NULL;
new_obj->values = (JSON_Value**)NULL;
new_obj->capacity = 0;
new_obj->count = 0;
return new_obj;
}
static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) {
size_t index;
if (object->count >= object->capacity) {
size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY);
if (new_capacity > OBJECT_MAX_CAPACITY)
return ERROR;
if (json_object_resize(object, new_capacity) == ERROR)
return ERROR;
}
if (json_object_get_value(object, name) != NULL)
return ERROR;
index = object->count;
object->names[index] = parson_strndup(name, strlen(name));
if (!object->names[index])
return ERROR;
object->values[index] = value;
object->count++;
return SUCCESS;
}
static int json_object_resize(JSON_Object *object, size_t capacity) {
if (try_realloc((void**)&object->names, capacity * sizeof(char*)) == ERROR)
return ERROR;
if (try_realloc((void**)&object->values, capacity * sizeof(JSON_Value*)) == ERROR)
return ERROR;
object->capacity = capacity;
return SUCCESS;
}
static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) {
size_t i, name_length;
for (i = 0; i < json_object_get_count(object); i++) {
name_length = strlen(object->names[i]);
if (name_length != n)
continue;
if (strncmp(object->names[i], name, n) == 0)
return object->values[i];
}
return NULL;
}
static void json_object_free(JSON_Object *object) {
while(object->count--) {
parson_free(object->names[object->count]);
json_value_free(object->values[object->count]);
}
parson_free(object->names);
parson_free(object->values);
parson_free(object);
}
/* JSON Array */
static JSON_Array * json_array_init(void) {
JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array));
if (!new_array)
return NULL;
new_array->items = (JSON_Value**)NULL;
new_array->capacity = 0;
new_array->count = 0;
return new_array;
}
static int json_array_add(JSON_Array *array, JSON_Value *value) {
if (array->count >= array->capacity) {
size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
if (new_capacity > ARRAY_MAX_CAPACITY)
return ERROR;
if (!json_array_resize(array, new_capacity))
return ERROR;
}
array->items[array->count] = value;
array->count++;
return SUCCESS;
}
static int json_array_resize(JSON_Array *array, size_t capacity) {
if (try_realloc((void**)&array->items, capacity * sizeof(JSON_Value*)) == ERROR)
return ERROR;
array->capacity = capacity;
return SUCCESS;
}
static void json_array_free(JSON_Array *array) {
while (array->count--)
json_value_free(array->items[array->count]);
parson_free(array->items);
parson_free(array);
}
/* JSON Value */
static JSON_Value * json_value_init_object(void) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value)
return NULL;
new_value->type = JSONObject;
new_value->value.object = json_object_init();
if (!new_value->value.object) {
parson_free(new_value);
return NULL;
}
return new_value;
}
static JSON_Value * json_value_init_array(void) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value)
return NULL;
new_value->type = JSONArray;
new_value->value.array = json_array_init();
if (!new_value->value.array) {
parson_free(new_value);
return NULL;
}
return new_value;
}
static JSON_Value * json_value_init_string(const char *string) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value)
return NULL;
new_value->type = JSONString;
new_value->value.string = string;
return new_value;
}
static JSON_Value * json_value_init_number(double number) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value)
return NULL;
new_value->type = JSONNumber;
new_value->value.number = number;
return new_value;
}
static JSON_Value * json_value_init_boolean(int boolean) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value)
return NULL;
new_value->type = JSONBoolean;
new_value->value.boolean = boolean;
return new_value;
}
static JSON_Value * json_value_init_null(void) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value)
return NULL;
new_value->type = JSONNull;
return new_value;
}
/* Parser */
static void skip_quotes(const char **string) {
skip_char(string);
while (**string != '\"') {
if (**string == '\0')
return;
if (**string == '\\') {
skip_char(string);
if (**string == '\0')
return;
}
skip_char(string);
}
skip_char(string);
}
/* Returns contents of a string inside double quotes and parses escaped
characters inside.
Example: "\u006Corem ipsum" -> lorem ipsum */
static const char * get_processed_string(const char **string) {
const char *string_start = *string;
char *output, *processed_ptr, *unprocessed_ptr, current_char;
unsigned int utf_val;
skip_quotes(string);
if (**string == '\0')
return NULL;
output = parson_strndup(string_start + 1, *string - string_start - 2);
if (!output)
return NULL;
processed_ptr = unprocessed_ptr = output;
while (*unprocessed_ptr) {
current_char = *unprocessed_ptr;
if (current_char == '\\') {
unprocessed_ptr++;
current_char = *unprocessed_ptr;
switch (current_char) {
case '\"': case '\\': case '/': break;
case 'b': current_char = '\b'; break;
case 'f': current_char = '\f'; break;
case 'n': current_char = '\n'; break;
case 'r': current_char = '\r'; break;
case 't': current_char = '\t'; break;
case 'u':
unprocessed_ptr++;
if (!is_utf((const unsigned char*)unprocessed_ptr) ||
sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) {
parson_free(output);
return NULL;
}
if (utf_val < 0x80) {
current_char = utf_val;
} else if (utf_val < 0x800) {
*processed_ptr++ = (utf_val >> 6) | 0xC0;
current_char = ((utf_val | 0x80) & 0xBF);
} else {
*processed_ptr++ = (utf_val >> 12) | 0xE0;
*processed_ptr++ = (((utf_val >> 6) | 0x80) & 0xBF);
current_char = ((utf_val | 0x80) & 0xBF);
}
unprocessed_ptr += 3;
break;
default:
parson_free(output);
return NULL;
break;
}
} else if ((unsigned char)current_char < 0x20) { /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
parson_free(output);
return NULL;
}
*processed_ptr = current_char;
processed_ptr++;
unprocessed_ptr++;
}
*processed_ptr = '\0';
if (try_realloc((void**)&output, strlen(output) + 1) == ERROR)
return NULL;
return output;
}
static JSON_Value * parse_value(const char **string, size_t nesting) {
if (nesting > MAX_NESTING)
return NULL;
skip_whitespaces(string);
switch (**string) {
case '{':
return parse_object_value(string, nesting + 1);
case '[':
return parse_array_value(string, nesting + 1);
case '\"':
return parse_string_value(string);
case 'f': case 't':
return parse_boolean_value(string);
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return parse_number_value(string);
case 'n':
return parse_null_value(string);
default:
return NULL;
}
}
static JSON_Value * parse_object_value(const char **string, size_t nesting) {
JSON_Value *output_value = json_value_init_object(), *new_value = NULL;
JSON_Object *output_object = json_value_get_object(output_value);
const char *new_key = NULL;
if (!output_value)
return NULL;
skip_char(string);
skip_whitespaces(string);
if (**string == '}') { /* empty object */
skip_char(string);
return output_value;
}
while (**string != '\0') {
new_key = get_processed_string(string);
skip_whitespaces(string);
if (!new_key || **string != ':') {
json_value_free(output_value);
return NULL;
}
skip_char(string);
new_value = parse_value(string, nesting);
if (!new_value) {
parson_free(new_key);
json_value_free(output_value);
return NULL;
}
if(!json_object_add(output_object, new_key, new_value)) {
parson_free(new_key);
parson_free(new_value);
json_value_free(output_value);
return NULL;
}
parson_free(new_key);
skip_whitespaces(string);
if (**string != ',')
break;
skip_char(string);
skip_whitespaces(string);
}
skip_whitespaces(string);
if (**string != '}' || /* Trim object after parsing is over */
json_object_resize(output_object, json_object_get_count(output_object)) == ERROR) {
json_value_free(output_value);
return NULL;
}
skip_char(string);
return output_value;
}
static JSON_Value * parse_array_value(const char **string, size_t nesting) {
JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL;
JSON_Array *output_array = json_value_get_array(output_value);
if (!output_value)
return NULL;
skip_char(string);
skip_whitespaces(string);
if (**string == ']') { /* empty array */
skip_char(string);
return output_value;
}
while (**string != '\0') {
new_array_value = parse_value(string, nesting);
if (!new_array_value) {
json_value_free(output_value);
return NULL;
}
if(json_array_add(output_array, new_array_value) == ERROR) {
parson_free(new_array_value);
json_value_free(output_value);
return NULL;
}
skip_whitespaces(string);
if (**string != ',')
break;
skip_char(string);
skip_whitespaces(string);
}
skip_whitespaces(string);
if (**string != ']' || /* Trim array after parsing is over */
json_array_resize(output_array, json_array_get_count(output_array)) == ERROR) {
json_value_free(output_value);
return NULL;
}
skip_char(string);
return output_value;
}
static JSON_Value * parse_string_value(const char **string) {
const char *new_string = get_processed_string(string);
if (!new_string)
return NULL;
return json_value_init_string(new_string);
}
static JSON_Value * parse_boolean_value(const char **string) {
size_t true_token_size = sizeof_token("true");
size_t false_token_size = sizeof_token("false");
if (strncmp("true", *string, true_token_size) == 0) {
*string += true_token_size;
return json_value_init_boolean(1);
} else if (strncmp("false", *string, false_token_size) == 0) {
*string += false_token_size;
return json_value_init_boolean(0);
}
return NULL;
}
static JSON_Value * parse_number_value(const char **string) {
char *end;
double number = strtod(*string, &end);
JSON_Value *output_value;
if (is_decimal(*string, end - *string)) {
*string = end;
output_value = json_value_init_number(number);
} else {
output_value = NULL;
}
return output_value;
}
static JSON_Value * parse_null_value(const char **string) {
size_t token_size = sizeof_token("null");
if (strncmp("null", *string, token_size) == 0) {
*string += token_size;
return json_value_init_null();
}
return NULL;
}
/* Parser API */
JSON_Value * json_parse_file(const char *filename) {
char *file_contents = read_file(filename);
JSON_Value *output_value = NULL;
if (!file_contents)
return NULL;
output_value = json_parse_string(file_contents);
parson_free(file_contents);
return output_value;
}
JSON_Value * json_parse_file_with_comments(const char *filename) {
char *file_contents = read_file(filename);
JSON_Value *output_value = NULL;
if (!file_contents)
return NULL;
output_value = json_parse_string_with_comments(file_contents);
parson_free(file_contents);
return output_value;
}
JSON_Value * json_parse_string(const char *string) {
if (!string || (*string != '{' && *string != '['))
return NULL;
return parse_value((const char**)&string, 0);
}
JSON_Value * json_parse_string_with_comments(const char *string) {
JSON_Value *result = NULL;
char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
string_mutable_copy = parson_strndup(string, strlen(string));
if (!string_mutable_copy)
return NULL;
remove_comments(string_mutable_copy, "/*", "*/");
remove_comments(string_mutable_copy, "//", "\n");
string_mutable_copy_ptr = string_mutable_copy;
skip_whitespaces(&string_mutable_copy_ptr);
if (*string_mutable_copy_ptr != '{' && *string_mutable_copy_ptr != '[') {
parson_free(string_mutable_copy);
return NULL;
}
result = parse_value((const char**)&string_mutable_copy_ptr, 0);
parson_free(string_mutable_copy);
return result;
}
/* JSON Object API */
JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {
return json_object_nget_value(object, name, strlen(name));
}
const char * json_object_get_string(const JSON_Object *object, const char *name) {
return json_value_get_string(json_object_get_value(object, name));
}
double json_object_get_number(const JSON_Object *object, const char *name) {
return json_value_get_number(json_object_get_value(object, name));
}
JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) {
return json_value_get_object(json_object_get_value(object, name));
}
JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) {
return json_value_get_array(json_object_get_value(object, name));
}
int json_object_get_boolean(const JSON_Object *object, const char *name) {
return json_value_get_boolean(json_object_get_value(object, name));
}
JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) {
const char *dot_position = strchr(name, '.');
if (!dot_position)
return json_object_get_value(object, name);
object = json_value_get_object(json_object_nget_value(object, name, dot_position - name));
return json_object_dotget_value(object, dot_position + 1);
}
const char * json_object_dotget_string(const JSON_Object *object, const char *name) {
return json_value_get_string(json_object_dotget_value(object, name));
}
double json_object_dotget_number(const JSON_Object *object, const char *name) {
return json_value_get_number(json_object_dotget_value(object, name));
}
JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) {
return json_value_get_object(json_object_dotget_value(object, name));
}
JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) {
return json_value_get_array(json_object_dotget_value(object, name));
}
int json_object_dotget_boolean(const JSON_Object *object, const char *name) {
return json_value_get_boolean(json_object_dotget_value(object, name));
}
size_t json_object_get_count(const JSON_Object *object) {
return object ? object->count : 0;
}
const char * json_object_get_name(const JSON_Object *object, size_t index) {
if (index >= json_object_get_count(object))
return NULL;
return object->names[index];
}
/* JSON Array API */
JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) {
if (index >= json_array_get_count(array))
return NULL;
return array->items[index];
}
const char * json_array_get_string(const JSON_Array *array, size_t index) {
return json_value_get_string(json_array_get_value(array, index));
}
double json_array_get_number(const JSON_Array *array, size_t index) {
return json_value_get_number(json_array_get_value(array, index));
}
JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) {
return json_value_get_object(json_array_get_value(array, index));
}
JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) {
return json_value_get_array(json_array_get_value(array, index));
}
int json_array_get_boolean(const JSON_Array *array, size_t index) {
return json_value_get_boolean(json_array_get_value(array, index));
}
size_t json_array_get_count(const JSON_Array *array) {
return array ? array->count : 0;
}
/* JSON Value API */
JSON_Value_Type json_value_get_type(const JSON_Value *value) {
return value ? value->type : JSONError;
}
JSON_Object * json_value_get_object(const JSON_Value *value) {
return json_value_get_type(value) == JSONObject ? value->value.object : NULL;
}
JSON_Array * json_value_get_array(const JSON_Value *value) {
return json_value_get_type(value) == JSONArray ? value->value.array : NULL;
}
const char * json_value_get_string(const JSON_Value *value) {
return json_value_get_type(value) == JSONString ? value->value.string : NULL;
}
double json_value_get_number(const JSON_Value *value) {
return json_value_get_type(value) == JSONNumber ? value->value.number : 0;
}
int json_value_get_boolean(const JSON_Value *value) {
return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1;
}
void json_value_free(JSON_Value *value) {
switch (json_value_get_type(value)) {
case JSONObject:
json_object_free(value->value.object);
break;
case JSONString:
if (value->value.string) { parson_free(value->value.string); }
break;
case JSONArray:
json_array_free(value->value.array);
break;
default:
break;
}
parson_free(value);
}
@@ -0,0 +1,144 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
LoRa concentrator : Timer synchronization
Provides synchronization between unix, concentrator and gps clocks
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Michael Coracin
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdio.h> /* printf, fprintf, snprintf, fopen, fputs */
#include <stdint.h> /* C99 types */
#include <pthread.h>
#include "trace.h"
#include "timersync.h"
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */
static pthread_mutex_t mx_timersync = PTHREAD_MUTEX_INITIALIZER; /* control access to timer sync offsets */
static struct timeval offset_unix_concent = {0,0}; /* timer offset between unix host and concentrator */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE SHARED VARIABLES (GLOBAL) ------------------------------------ */
extern bool exit_sig;
extern bool quit_sig;
extern pthread_mutex_t mx_concent;
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int get_concentrator_time(struct timeval *concent_time, struct timeval unix_time) {
struct timeval local_timeval;
if (concent_time == NULL) {
MSG("ERROR: %s invalid parameter\n", __FUNCTION__);
return -1;
}
pthread_mutex_lock(&mx_timersync); /* protect global variable access */
timersub(&unix_time, &offset_unix_concent, &local_timeval);
pthread_mutex_unlock(&mx_timersync);
/* TODO: handle sx1301 coutner wrap-up !! */
concent_time->tv_sec = local_timeval.tv_sec;
concent_time->tv_usec = local_timeval.tv_usec;
MSG_DEBUG(DEBUG_TIMERSYNC, " --> TIME: unix current time is %ld,%ld\n", unix_time.tv_sec, unix_time.tv_usec);
MSG_DEBUG(DEBUG_TIMERSYNC, " offset is %ld,%ld\n", offset_unix_concent.tv_sec, offset_unix_concent.tv_usec);
MSG_DEBUG(DEBUG_TIMERSYNC, " sx1301 current time is %ld,%ld\n", local_timeval.tv_sec, local_timeval.tv_usec);
return 0;
}
/* ---------------------------------------------------------------------------------------------- */
/* --- THREAD 6: REGULARLAY MONITOR THE OFFSET BETWEEN UNIX CLOCK AND CONCENTRATOR CLOCK -------- */
void thread_timersync(void) {
struct timeval unix_timeval;
struct timeval concentrator_timeval;
uint32_t sx1301_timecount = 0;
struct timeval offset_previous = {0,0};
struct timeval offset_drift = {0,0}; /* delta between current and previous offset */
while (!exit_sig && !quit_sig) {
/* Regularly disable GPS mode of concentrator's counter, in order to get
real timer value for synchronizing with host's unix timer */
MSG("\nINFO: Disabling GPS mode for concentrator's counter...\n");
pthread_mutex_lock(&mx_concent); /* TODO: Is it necessary to protect here? */
lgw_reg_w(LGW_GPS_EN, 0);
pthread_mutex_unlock(&mx_concent);
/* Get current unix time */
gettimeofday(&unix_timeval, NULL);
/* Get current concentrator counter value (1MHz) */
lgw_get_trigcnt(&sx1301_timecount);
concentrator_timeval.tv_sec = sx1301_timecount / 1000000UL;
concentrator_timeval.tv_usec = sx1301_timecount - (concentrator_timeval.tv_sec * 1000000UL);
/* Compute offset between unix and concentrator timers, with microsecond precision */
offset_previous.tv_sec = offset_unix_concent.tv_sec;
offset_previous.tv_usec = offset_unix_concent.tv_usec;
/* TODO: handle sx1301 coutner wrap-up */
pthread_mutex_lock(&mx_timersync); /* protect global variable access */
timersub(&unix_timeval, &concentrator_timeval, &offset_unix_concent);
pthread_mutex_unlock(&mx_timersync);
timersub(&offset_unix_concent, &offset_previous, &offset_drift);
MSG_DEBUG(DEBUG_TIMERSYNC, " sx1301 = %u (µs) - timeval (%ld,%ld)\n",
sx1301_timecount,
concentrator_timeval.tv_sec,
concentrator_timeval.tv_usec);
MSG_DEBUG(DEBUG_TIMERSYNC, " unix_timeval = %ld,%ld\n", unix_timeval.tv_sec, unix_timeval.tv_usec);
MSG("INFO: host/sx1301 time offset=(%lds:%ldµs) - drift=%ldµs\n",
offset_unix_concent.tv_sec,
offset_unix_concent.tv_usec,
offset_drift.tv_sec * 1000000UL + offset_drift.tv_usec);
MSG("INFO: Enabling GPS mode for concentrator's counter.\n\n");
pthread_mutex_lock(&mx_concent); /* TODO: Is it necessary to protect here? */
lgw_reg_w(LGW_GPS_EN, 1);
pthread_mutex_unlock(&mx_concent);
/* delay next sync */
/* If we consider a crystal oscillator precision of about 20ppm worst case, and a clock
running at 1MHz, this would mean 1µs drift every 50000µs (10000000/20).
As here the time precision is not critical, we should be able to cope with at least 1ms drift,
which should occur after 50s (50000µs * 1000).
Let's set the thread sleep to 1 minute for now */
wait_ms(60000);
}
}
@@ -0,0 +1,31 @@
#!/bin/sh
# This script is a helper to update the Gateway_ID field of given
# JSON configuration file, as a EUI-64 address generated from the 48-bits MAC
# address of the device it is run from.
#
# Usage examples:
# ./update_gwid.sh ./local_conf.json
iot_sk_update_gwid() {
# get gateway ID from its MAC address to generate an EUI-64 address
GWID_MIDFIX="FFFE"
GWID_BEGIN=$(ip link show eth0 | awk '/ether/ {print $2}' | awk -F\: '{print $1$2$3}')
GWID_END=$(ip link show eth0 | awk '/ether/ {print $2}' | awk -F\: '{print $4$5$6}')
# replace last 8 digits of default gateway ID by actual GWID, in given JSON configuration file
sed -i 's/\(^\s*"gateway_ID":\s*"\).\{16\}"\s*\(,\?\).*$/\1'${GWID_BEGIN}${GWID_MIDFIX}${GWID_END}'"\2/' $1
echo "Gateway_ID set to "$GWID_BEGIN$GWID_MIDFIX$GWID_END" in file "$1
}
if [ $# -ne 1 ]
then
echo "Usage: $0 [filename]"
echo " filename: Path to JSON file containing Gateway_ID for packet forwarder"
exit 1
fi
iot_sk_update_gwid $1
exit 0
@@ -0,0 +1,231 @@
#Attention
This is a fork with the needed modifications to be able to use lora_gateway
with a Multitech MTAC-LORA (MultiConnect mCard) board instead of a
IoT Starter Kit platform.
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Lora network packet forwarder project
======================================
1. Core program: lora_pkt_fwd
-------------------------------
The packet forwarder is a program running on the host of a Lora gateway that
forwards RF packets receive by the concentrator to a server through a IP/UDP
link, and emits RF packets that are sent by the server. It can also emit a
network-wide GPS-synchronous beacon signal used for coordinating all nodes of
the network.
((( Y )))
|
|
+- -|- - - - - - - - - - - - -+ xxxxxxxxxxxx +--------+
|+--+-----------+ +------+| xx x x xxx | |
|| | | || xx Internet xx | |
|| Concentrator |<----+ Host |<------xx or xx-------->| |
|| | SPI | || xx Intranet xx | Server |
|+--------------+ +------+| xxxx x xxxx | |
| ^ ^ | xxxxxxxx | |
| | PPS +-----+ NMEA | | | |
| +------| GPS |-------+ | +--------+
| +-----+ |
| |
| Gateway |
+- - - - - - - - - - - - - - -+
Uplink: radio packets received by the gateway, with metadata added by the
gateway, forwarded to the server. Might also include gateway status.
Downlink: packets generated by the server, with additional metadata, to be
transmitted by the gateway on the radio channel. Might also include
configuration data for the gateway.
2. Helper programs
-------------------
Those programs are included in the project to provide examples on how to
communicate with the packet forwarder, and to help the system builder use it
without having to implement a full Lora network server.
### 3.1. util_sink ###
The packet sink is a simple helper program listening on a single port for UDP
datagrams, and displaying a message each time one is received. The content of
the datagram itself is ignored.
### 3.2. util_ack ###
The packet acknowledger is a simple helper program listening on a single UDP
port and responding to PUSH_DATA datagrams with PUSH_ACK, and to PULL_DATA
datagrams with PULL_ACK.
### 3.3. util_tx_test ###
The network packet sender is a simple helper program used to send packets
through the gateway-to-server downlink route.
4. Helper scripts
-----------------
### 4.1. lora_gateway/reset_lgw.sh
This script, provided with the HAL (lora_gateway), must be launched on IoT Start
Kit platform to reset concentrator chip through GPIO, before starting any
application using the concentrator, like the packet forwarder.
### 4.2. packet_forwarder/lora_pkt_fwd/update_gwid.sh
This script allows automatic update of Gateway_ID with unique MAC address, in
packet forwarder JSON configuration file.
Please refer to the script header for more details.
5. Changelog
-------------
### v3.0.0 - 2016-05-19 ###
* Merged all different flavours of packet forwarder into one unique lora_pkt_fwd
Note: Various flavours can still be achieved using the corresponding
global_conf.json.XXX file provided in lora_pkt_fwd/cfg.
* Added downlink "just-in-time" scheduling to optimize downlink capacity.
* Updated Gateway <-> NetworkServer protocol to describe the new format of
"tx_ack" message.
* Added "Listen-Before-Talk" configuration.
* Splitted reset_pkt_fwd.sh script in 2 different scripts:
- reset_lgw.sh, provided with the HAL (lora_gateway)
- update_gwid.sh, provided with lora_pkt_fwd
WARNING: Gateway <-> Network Server protocol version has changed. Please refer
to PROTOCOL.txt file.
### v2.2.1 - 2016-04-12 ###
* util_tx_test: added FSK support and specific payload for easier PER testing.
* base64: fixed padding check.
* Updated all makefiles to handle the creation of obj directory when necessary.
* [gps/beacon]_pkt_fwd: fixed crash on exit when GPS not enabled.
* [*]_pkt_fwd: added a cfg/ directory containing different flavours or the
global_conf.json file for different boards: Ref Design PCB_E336 (GW1.5-27dBm),
Ref Design PCB_E286 (GW1.0), Ref Design with US902 frequency plan.
### v2.2.0 - 2015-10-08 ###
* Removed FTDI support in makefiles to align with HAL v3.2.0.
* Force IPv4 mode usage on UDP socket, instead of auto. The auto mode was
causing an issue to properly resolve LoRa server hostname given in JSON
configuration file (MariaDB issue: https://mariadb.atlassian.net/browse/MDEV-4356,
https://mariadb.atlassian.net/browse/MDEV-4379).
### v2.1.0 - 2015-06-29 ###
* Added helper script for concentrator reset through GPIO, needed on IoT
Starter Kit (reset_pkt_fwd.sh).
* The same reset_pkt_fwd.sh script also allows to automatically update the
Gateway_ID field in JSON configuration file, with board MAC address.
* Updated JSON configuration file with proper default value for IoT Starter
Kit: server address set to local server, GPS device path set to proper value
(/dev/ttyAMA0).
### v2.0.0 - 2015-04-30 ###
* Changed: Several configuration parameters are now set dynamically from the
JSON configuration file: RSSI offset, concentrator clock source, radio type,
TX gain table, network type. The HAL does not need to be recompiled any more to
update those parameters. An example for IoT Starter Kit platform is provided in
global_conf.json for basic, gps and beacon packet_forwarder.
* Removed: Band frequency JSON configuration file has been removed. An example
for EU 868MHz is provided in global_conf.json for basic, gps and beacon packet
forwarder.
* Changed: Updated makefiles to allow cross compilation from environment
variable (ARCH, CROSS_COMPILE).
** WARNING: **
** Update your JSON configuration file with new dynamic parameters. **
### v1.4.1 - 2015-01-23 ###
* Bugfix: fixed LP-116, fdev parameter parsed incorrectly, making FSK TX fail.
* Bugfix: fixed a platform-dependant minor rounding issue.
* Beta: updated beacon format, partially aligned with latest class B proposal.
### v1.4.0 - 2014-10-16 ###
* Feature: Adding TX FSK support.
* Feature: optional auto-quit if a certain number of PULL_ACK is missed.
* Feature: upstream and downstream ping time is displayed on console.
* Bugfix: some beacons were missed at high beaconing frequency.
* Bugfix: critical snprintf error caused a crash for long payloads.
* FSK bitrate now appears in the upstream JSON.
### v1.3.0 - 2014-03-28 ###
* Feature: adding preliminary beacon support for class B development.
* Solved warnings with 64b integer printf when compiling on x86_64.
* Updated build system for easier deployment on various hardware.
* Changed threads organization in the forwarder programs.
* Removed duplicate protocol documentation.
### v1.2.0 - 2014-02-03 ###
* Feature: added a GPS-enabled packet forwarder, used to timestamp received
packet with a globally-synchronous microsecond-accurate timestamp.
* Feature: GPS packet forwarder sends status report on the uplink, protocol
specification updated accordingly (report include gateway geolocation).
* Feature: packets can be sent without CRC at radio layer.
* Bugfix: no more crash with base64 padded input.
* Bugfix: no more rounding errors on the 'freq' value sent to server.
* A minimum preamble of 6 Lora symbol is enforced for optimum sensitivity.
* Padded Base64 is sent on uplink, downlink accepts padded and unpadded Base64.
* Updated the Parson JSON library to a version that supports comments.
* Added .md (Markdown) extension to readme files for better Github viewing.
### v1.1.0 - 2013-12-09 ###
* Feature: added packet filtering parameters to the JSON configuration files.
* Bugfix: will not send a datagram if all the packets returned by the receive()
function have been filtered out.
* Bugfix: removed leading zeros for the timestamp in the upstream JSON because
it is not compliant with JSON standard (might be interpreted as octal number).
* Removed TXT extension for README files for better Github integration.
* Cleaned-up documentation, moving change log to top README.
* Modified Makefiles to ease cross-compilation.
### v1.0.0 - 2013-11-22 ###
* Initial release of the packet forwarder, protocol specifications and helper
programs.
6. Legal notice
----------------
The information presented in this project documentation does not form part of
any quotation or contract, is believed to be accurate and reliable and may be
changed without notice. No liability will be accepted by the publisher for any
consequence of its use. Publication thereof does not convey nor imply any
license under patent or other industrial or intellectual property rights.
Semtech assumes no responsibility or liability whatsoever for any failure or
unexpected operation resulting from misuse, neglect improper installation,
repair or improper handling or unusual physical or electrical stress
including, but not limited to, exposure to parameters beyond the specified
maximum ratings or operation outside the specified range.
SEMTECH PRODUCTS ARE NOT DESIGNED, INTENDED, AUTHORIZED OR WARRANTED TO BE
SUITABLE FOR USE IN LIFE-SUPPORT APPLICATIONS, DEVICES OR SYSTEMS OR OTHER
CRITICAL APPLICATIONS. INCLUSION OF SEMTECH PRODUCTS IN SUCH APPLICATIONS IS
UNDERSTOOD TO BE UNDERTAKEN SOLELY AT THE CUSTOMERS OWN RISK. Should a
customer purchase or use Semtech products for any such unauthorized
application, the customer shall indemnify and hold Semtech and its officers,
employees, subsidiaries, affiliates, and distributors harmless against all
claims, costs damages and attorney fees which could arise.
*EOF*
@@ -0,0 +1,33 @@
### Application-specific constants
APP_NAME := util_ack
### Constant symbols
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I.
OBJDIR = obj
### General build targets
all: $(APP_NAME)
clean:
rm -f $(OBJDIR)/*.o
rm -f $(APP_NAME)
### Main program compilation and assembly
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c | $(OBJDIR)
$(CC) -c $(CFLAGS) $< -o $@
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o
$(CC) $< -o $@
### EOF
@@ -0,0 +1,65 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Utility: packet acknowledger
=============================
1. Introduction
----------------
The packet acknowledger is a simple helper program listening on a single UDP
port and responding to PUSH_DATA datagrams with PUSH_ACK, and to PULL_DATA
datagrams with PULL_ACK.
Informations about the datagrams received and the answers send are display on
screen to help communication debugging.
Packets not following the protocol detailed in the PROTOCOL.TXT document in the
basic_pkt_fwt directory are ignored.
2. Dependencies
----------------
This program follows the v1.1 version of the gateway-to-server protocol.
3. Usage
---------
Start the program with the port number as first and only argument.
To stop the application, press Ctrl+C.
4. License
-----------
Copyright (C) 2013, 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*
@@ -0,0 +1,193 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Network sink, receives UDP packets and sends an acknowledge
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf, fprintf, sprintf, fopen, fputs */
#include <unistd.h> /* usleep */
#include <string.h> /* memset */
#include <time.h> /* time, clock_gettime, strftime, gmtime, clock_nanosleep*/
#include <stdlib.h> /* atoi, exit */
#include <errno.h> /* error messages */
#include <sys/socket.h> /* socket specific definitions */
#include <netinet/in.h> /* INET constants and stuff */
#include <arpa/inet.h> /* IP address conversion stuff */
#include <netdb.h> /* gai_strerror */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define STRINGIFY(x) #x
#define STR(x) STRINGIFY(x)
#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define PROTOCOL_VERSION 2
#define PKT_PUSH_DATA 0
#define PKT_PUSH_ACK 1
#define PKT_PULL_DATA 2
#define PKT_PULL_RESP 3
#define PKT_PULL_ACK 4
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
int i; /* loop variable and temporary variable for return value */
/* server socket creation */
int sock; /* socket file descriptor */
struct addrinfo hints;
struct addrinfo *result; /* store result of getaddrinfo */
struct addrinfo *q; /* pointer to move into *result data */
char host_name[64];
char port_name[64];
/* variables for receiving and sending packets */
struct sockaddr_storage dist_addr;
socklen_t addr_len = sizeof dist_addr;
uint8_t databuf[4096];
int byte_nb;
/* variables for protocol management */
uint32_t raw_mac_h; /* Most Significant Nibble, network order */
uint32_t raw_mac_l; /* Least Significant Nibble, network order */
uint64_t gw_mac; /* MAC address of the client (gateway) */
uint8_t ack_command;
/* check if port number was passed as parameter */
if (argc != 2) {
MSG("Usage: util_ack <port number>\n");
exit(EXIT_FAILURE);
}
/* prepare hints to open network sockets */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* should handle IP v4 or v6 automatically */
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; /* will assign local IP automatically */
/* look for address */
i = getaddrinfo(NULL, argv[1], &hints, &result);
if (i != 0) {
MSG("ERROR: getaddrinfo returned %s\n", gai_strerror(i));
exit(EXIT_FAILURE);
}
/* try to open socket and bind it */
for (q=result; q!=NULL; q=q->ai_next) {
sock = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
if (sock == -1) {
continue; /* socket failed, try next field */
} else {
i = bind(sock, q->ai_addr, q->ai_addrlen);
if (i == -1) {
shutdown(sock, SHUT_RDWR);
continue; /* bind failed, try next field */
} else {
break; /* success, get out of loop */
}
}
}
if (q == NULL) {
MSG("ERROR: failed to open socket or to bind to it\n");
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("INFO: result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
MSG("INFO: util_ack listening on port %s\n", argv[1]);
freeaddrinfo(result);
while (1) {
/* wait to receive a packet */
byte_nb = recvfrom(sock, databuf, sizeof databuf, 0, (struct sockaddr *)&dist_addr, &addr_len);
if (byte_nb == -1) {
MSG("ERROR: recvfrom returned %s \n", strerror(errno));
exit(EXIT_FAILURE);
}
/* display info about the sender */
i = getnameinfo((struct sockaddr *)&dist_addr, addr_len, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
if (i == -1) {
MSG("ERROR: getnameinfo returned %s \n", gai_strerror(i));
exit(EXIT_FAILURE);
}
printf(" -> pkt in , host %s (port %s), %i bytes", host_name, port_name, byte_nb);
/* check and parse the payload */
if (byte_nb < 12) { /* not enough bytes for packet from gateway */
printf(" (too short for GW <-> MAC protocol)\n");
continue;
}
/* don't touch the token in position 1-2, it will be sent back "as is" for acknowledgement */
if (databuf[0] != PROTOCOL_VERSION) { /* check protocol version number */
printf(", invalid version %u\n", databuf[0]);
continue;
}
raw_mac_h = *((uint32_t *)(databuf+4));
raw_mac_l = *((uint32_t *)(databuf+8));
gw_mac = ((uint64_t)ntohl(raw_mac_h) << 32) + (uint64_t)ntohl(raw_mac_l);
/* interpret gateway command */
switch (databuf[3]) {
case PKT_PUSH_DATA:
printf(", PUSH_DATA from gateway 0x%08X%08X\n", (uint32_t)(gw_mac >> 32), (uint32_t)(gw_mac & 0xFFFFFFFF));
ack_command = PKT_PUSH_ACK;
printf("<- pkt out, PUSH_ACK for host %s (port %s)", host_name, port_name);
break;
case PKT_PULL_DATA:
printf(", PULL_DATA from gateway 0x%08X%08X\n", (uint32_t)(gw_mac >> 32), (uint32_t)(gw_mac & 0xFFFFFFFF));
ack_command = PKT_PULL_ACK;
printf("<- pkt out, PULL_ACK for host %s (port %s)", host_name, port_name);
break;
default:
printf(", unexpected command %u\n", databuf[3]);
continue;
}
/* add some artificial latency */
usleep(30000); /* 30 ms */
/* send acknowledge and check return value */
databuf[3] = ack_command;
byte_nb = sendto(sock, (void *)databuf, 4, 0, (struct sockaddr *)&dist_addr, addr_len);
if (byte_nb == -1) {
printf(", send error:%s\n", strerror(errno));
} else {
printf(", %i bytes sent\n", byte_nb);
}
}
}
Binary file not shown.
@@ -0,0 +1,33 @@
### Application-specific constants
APP_NAME := util_sink
### Constant symbols
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I.
OBJDIR = obj
### General build targets
all: $(APP_NAME)
clean:
rm -f $(OBJDIR)/*.o
rm -f $(APP_NAME)
### Main program compilation and assembly
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c | $(OBJDIR)
$(CC) -c $(CFLAGS) $< -o $@
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o
$(CC) $< -o $@
### EOF
@@ -0,0 +1,62 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Utility: packet sink
=====================
1. Introduction
----------------
The packet sink is a simple helper program listening on a single port for UDP
datagrams, and displaying a message each time one is received. The content of
the datagram itself is ignored.
This allow to test another software (locally or on another computer) that
sends UDP datagrams without having ICMP 'port closed' errors each time.
2. Dependencies
----------------
None.
3. Usage
---------
Start the program with the port number as first and only argument.
To stop the application, press Ctrl+C.
4. License
-----------
Copyright (C) 2013, 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*
@@ -0,0 +1,125 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Network sink, receives UDP packets on certain ports and discards them
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf, fprintf, sprintf, fopen, fputs */
#include <string.h> /* memset */
#include <time.h> /* time, clock_gettime, strftime, gmtime, clock_nanosleep*/
#include <stdlib.h> /* atoi, exit */
#include <errno.h> /* error messages */
#include <sys/socket.h> /* socket specific definitions */
#include <netinet/in.h> /* INET constants and stuff */
#include <arpa/inet.h> /* IP address conversion stuff */
#include <netdb.h> /* gai_strerror */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define STRINGIFY(x) #x
#define STR(x) STRINGIFY(x)
#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
int i; /* loop variable and temporary variable for return value */
/* server socket creation */
int sock; /* socket file descriptor */
struct addrinfo hints;
struct addrinfo *result; /* store result of getaddrinfo */
struct addrinfo *q; /* pointer to move into *result data */
char host_name[64];
char port_name[64];
/* variables for receiving packets */
struct sockaddr_storage dist_addr;
socklen_t addr_len = sizeof dist_addr;
uint8_t databuf[4096];
int byte_nb;
/* check if port number was passed as parameter */
if (argc != 2) {
MSG("Usage: util_sink <port number>\n");
exit(EXIT_FAILURE);
}
/* prepare hints to open network sockets */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* should handle IP v4 or v6 automatically */
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; /* will assign local IP automatically */
/* look for address */
i = getaddrinfo(NULL, argv[1], &hints, &result);
if (i != 0) {
MSG("ERROR: getaddrinfo returned %s\n", gai_strerror(i));
exit(EXIT_FAILURE);
}
/* try to open socket and bind it */
for (q=result; q!=NULL; q=q->ai_next) {
sock = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
if (sock == -1) {
continue; /* socket failed, try next field */
} else {
i = bind(sock, q->ai_addr, q->ai_addrlen);
if (i == -1) {
shutdown(sock, SHUT_RDWR);
continue; /* bind failed, try next field */
} else {
break; /* success, get out of loop */
}
}
}
if (q == NULL) {
MSG("ERROR: failed to open socket or to bind to it\n");
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
MSG("INFO: util_sink listening on port %s\n", argv[1]);
freeaddrinfo(result);
while (1) {
byte_nb = recvfrom(sock, databuf, sizeof databuf, 0, (struct sockaddr *)&dist_addr, &addr_len);
if (byte_nb == -1) {
MSG("ERROR: recvfrom returned %s \n", strerror(errno));
exit(EXIT_FAILURE);
}
getnameinfo((struct sockaddr *)&dist_addr, addr_len, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
printf("Got packet from host %s port %s, %i bytes long\n", host_name, port_name, byte_nb);
}
}
@@ -0,0 +1,36 @@
### Application-specific constants
APP_NAME := util_tx_test
### Constant symbols
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I.
OBJDIR = obj
INCLUDES = $(wildcard inc/*.h)
### General build targets
all: $(APP_NAME)
clean:
rm -f $(OBJDIR)/*.o
rm -f $(APP_NAME)
### Sub-modules compilation
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR)
$(CC) -c $(CFLAGS) $< -o $@
### Main program assembly
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(OBJDIR)/base64.o
$(CC) $< $(OBJDIR)/base64.o -o $@
### EOF
@@ -0,0 +1,62 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Base64 encoding & decoding library
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
#ifndef _BASE64_H
#define _BASE64_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Encode binary data in Base64 string (no padding)
@param in pointer to a table of binary data
@param size number of bytes to be encoded to base64
@param out pointer to a string where the function will output encoded data
@param max_len max length of the out string (including null char)
@return >=0 length of the resulting string (w/o null char), -1 for error
*/
int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len);
/**
@brief Decode Base64 string to binary data (no padding)
@param in string containing only base64 valid characters
@param size number of characters to be decoded from base64 (w/o null char)
@param out pointer to a data buffer where the function will output decoded data
@param out_max_len usable size of the output data buffer
@return >=0 number of bytes written to the data buffer, -1 for error
*/
int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len);
/* === derivative functions === */
/**
@brief Encode binary data in Base64 string (with added padding)
*/
int bin_to_b64(const uint8_t * in, int size, char * out, int max_len);
/**
@brief Decode Base64 string to binary data (remove padding if necessary)
*/
int b64_to_bin(const char * in, int size, uint8_t * out, int max_len);
#endif
/* --- EOF ------------------------------------------------------------------ */
@@ -0,0 +1,75 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Utility: network packet sender
===============================
1. Introduction
----------------
The network packet sender is a simple helper program used to send packets
through the gateway-to-server downlink route.
The program start by waiting for a gateway to send it a PULL_DATA datagram.
After that, it will send back to the gateway a specified amount of PULL_RESP
datagrams, each containing a packet to be sent immediately and a variable
payload.
2. Dependencies
----------------
This program follows the v1.1 version of the gateway-to-server protocol.
3. Usage
---------
The application runs until the specified number of packets have been sent.
Press Ctrl+C to stop the application before that.
Use the -h option to get help and details about available options.
The packets are [9-n] bytes long, and have following payload content:
+----------+---------------+---------------+---------------+---------------+---+---+---+---+---+---+---+---+
| Id | PktCnt[31:24] | PktCnt[23:16] | PktCnt[15:8] | PktCnt[7:0] | P | E | R |FCS| 0 | 1 |...| n |
+----------+---------------+---------------+---------------+---------------+---+---+---+---+---+---+---+---+
Id : User defined ID to differentiate sender at receiver side. (8 bits)
PktCnt : Packet counter incremented at each transmission. (32 bits)
P, E, R : ASCII values for characters 'P', 'E' and 'R'.
FCS : Checksum: 8-bits sum of Id, PktCnt[31 :24] , PktCnt[23 :16] , PktCnt[15 :8] , PktCnt[7:0], P,E,R
0,1, ..., n : Padding bytes up until user specified payload length.
4. License
-----------
Copyright (C) 2013, 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*
@@ -0,0 +1,308 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Base64 encoding & decoding library
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "base64.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE)
//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */
#define DEBUG(args...)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */
static char code_62 = '+'; /* RFC 1421 standard character for code 62 */
static char code_63 = '/'; /* RFC 1421 standard character for code 63 */
static char code_pad = '='; /* RFC 1421 padding character if padding */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
/**
@brief Convert a code in the range 0-63 to an ASCII character
*/
char code_to_char(uint8_t x);
/**
@brief Convert an ASCII character to a code in the range 0-63
*/
uint8_t char_to_code(char x);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
char code_to_char(uint8_t x) {
if (x <= 25) {
return 'A' + x;
} else if ((x >= 26) && (x <= 51)) {
return 'a' + (x-26);
} else if ((x >= 52) && (x <= 61)) {
return '0' + (x-52);
} else if (x == 62) {
return code_62;
} else if (x == 63) {
return code_63;
} else {
DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x);
exit(EXIT_FAILURE);
} //TODO: improve error management
}
uint8_t char_to_code(char x) {
if ((x >= 'A') && (x <= 'Z')) {
return (uint8_t)x - (uint8_t)'A';
} else if ((x >= 'a') && (x <= 'z')) {
return (uint8_t)x - (uint8_t)'a' + 26;
} else if ((x >= '0') && (x <= '9')) {
return (uint8_t)x - (uint8_t)'0' + 52;
} else if (x == code_62) {
return 62;
} else if (x == code_63) {
return 63;
} else {
DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x);
exit(EXIT_FAILURE);
} //TODO: improve error management
}
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) {
int i;
int result_len; /* size of the result */
int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
int last_bytes; /* number of unsigned chars <3 in the last block */
int last_chars; /* number of characters <4 in the last block */
uint32_t b;
/* check input values */
if ((out == NULL) || (in == NULL)) {
DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n");
return -1;
}
if (size == 0) {
*out = 0; /* null string */
return 0;
}
/* calculate the number of base64 'blocks' */
full_blocks = size / 3;
last_bytes = size % 3;
switch (last_bytes) {
case 0: /* no byte left to encode */
last_chars = 0;
break;
case 1: /* 1 byte left to encode -> +2 chars */
last_chars = 2;
break;
case 2: /* 2 bytes left to encode -> +3 chars */
last_chars = 3;
break;
default:
CRIT("switch default that should not be possible");
}
/* check if output buffer is big enough */
result_len = (4*full_blocks) + last_chars;
if (max_len < (result_len + 1)) { /* 1 char added for string terminator */
DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n");
return -1;
}
/* process all the full blocks */
for (i=0; i < full_blocks; ++i) {
b = (0xFF & in[3*i] ) << 16;
b |= (0xFF & in[3*i + 1]) << 8;
b |= 0xFF & in[3*i + 2];
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
out[4*i + 3] = code_to_char( b & 0x3F);
}
/* process the last 'partial' block and terminate string */
i = full_blocks;
if (last_chars == 0) {
out[4*i] = 0; /* null character to terminate string */
} else if (last_chars == 2) {
b = (0xFF & in[3*i] ) << 16;
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = 0; /* null character to terminate string */
} else if (last_chars == 3) {
b = (0xFF & in[3*i] ) << 16;
b |= (0xFF & in[3*i + 1]) << 8;
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
out[4*i + 3] = 0; /* null character to terminate string */
}
return result_len;
}
int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) {
int i;
int result_len; /* size of the result */
int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
int last_chars; /* number of characters <4 in the last block */
int last_bytes; /* number of unsigned chars <3 in the last block */
uint32_t b;
;
/* check input values */
if ((out == NULL) || (in == NULL)) {
DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
return -1;
}
if (size == 0) {
return 0;
}
/* calculate the number of base64 'blocks' */
full_blocks = size / 4;
last_chars = size % 4;
switch (last_chars) {
case 0: /* no char left to decode */
last_bytes = 0;
break;
case 1: /* only 1 char left is an error */
DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n");
return -1;
case 2: /* 2 chars left to decode -> +1 byte */
last_bytes = 1;
break;
case 3: /* 3 chars left to decode -> +2 bytes */
last_bytes = 2;
break;
default:
CRIT("switch default that should not be possible");
}
/* check if output buffer is big enough */
result_len = (3*full_blocks) + last_bytes;
if (max_len < result_len) {
DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n");
return -1;
}
/* process all the full blocks */
for (i=0; i < full_blocks; ++i) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
b |= 0x3F & char_to_code(in[4*i + 3]);
out[3*i + 0] = (b >> 16) & 0xFF;
out[3*i + 1] = (b >> 8 ) & 0xFF;
out[3*i + 2] = b & 0xFF;
}
/* process the last 'partial' block */
i = full_blocks;
if (last_bytes == 1) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
out[3*i + 0] = (b >> 16) & 0xFF;
if (((b >> 12) & 0x0F) != 0) {
DEBUG("WARNING: last character contains unusable bits\n");
}
} else if (last_bytes == 2) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
out[3*i + 0] = (b >> 16) & 0xFF;
out[3*i + 1] = (b >> 8 ) & 0xFF;
if (((b >> 6) & 0x03) != 0) {
DEBUG("WARNING: last character contains unusable bits\n");
}
}
return result_len;
}
int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) {
int ret;
ret = bin_to_b64_nopad(in, size, out, max_len);
if (ret == -1) {
return -1;
}
switch (ret%4) {
case 0: /* nothing to do */
return ret;
case 1:
DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n");
return -1;
case 2: /* 2 chars in last block, must add 2 padding char */
if (max_len >= (ret + 2 + 1)) {
out[ret] = code_pad;
out[ret+1] = code_pad;
out[ret+2] = 0;
return ret+2;
} else {
DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
return -1;
}
case 3: /* 3 chars in last block, must add 1 padding char */
if (max_len >= (ret + 1 + 1)) {
out[ret] = code_pad;
out[ret+1] = 0;
return ret+1;
} else {
DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
return -1;
}
default:
CRIT("switch default that should not be possible");
}
}
int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) {
if (in == NULL) {
DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
return -1;
}
if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */
if (in[size-2] == code_pad) { /* 2 padding char to ignore */
return b64_to_bin_nopad(in, size-2, out, max_len);
} else if (in[size-1] == code_pad) { /* 1 padding char to ignore */
return b64_to_bin_nopad(in, size-1, out, max_len);
} else { /* no padding to ignore */
return b64_to_bin_nopad(in, size, out, max_len);
}
} else { /* treat as unpadded Base64 */
return b64_to_bin_nopad(in, size, out, max_len);
}
}
/* --- EOF ------------------------------------------------------------------ */
@@ -0,0 +1,500 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2013 Semtech-Cycleo
Description:
Ask a gateway to emit packets using GW <-> server protocol
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <stdio.h> /* printf fprintf sprintf fopen fputs */
#include <unistd.h> /* getopt access usleep */
#include <string.h> /* memset */
#include <signal.h> /* sigaction */
#include <stdlib.h> /* exit codes */
#include <errno.h> /* error messages */
#include <sys/socket.h> /* socket specific definitions */
#include <netinet/in.h> /* INET constants and stuff */
#include <arpa/inet.h> /* IP address conversion stuff */
#include <netdb.h> /* gai_strerror */
#include "base64.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define PROTOCOL_VERSION 2
#define PKT_PUSH_DATA 0
#define PKT_PUSH_ACK 1
#define PKT_PULL_DATA 2
#define PKT_PULL_RESP 3
#define PKT_PULL_ACK 4
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */
/* signal handling variables */
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
static void sig_handler(int sigio);
void usage (void);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;;
} else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
/* describe command line options */
void usage(void) {
MSG("Usage: util_tx_test {options}\n");
MSG("Available options:\n");
MSG(" -h print this help\n");
MSG(" -n <int or service> port number for gateway link\n");
MSG(" -f <float> target frequency in MHz\n");
MSG(" -m <str> Modulation type ['LORA, 'FSK']\n");
MSG(" -s <int> Spreading Factor [7:12]\n");
MSG(" -b <int> Modulation bandwidth in kHz [125,250,500]\n");
MSG(" -d <uint> FSK frequency deviation in kHz [1:250]\n");
MSG(" -r <float> FSK bitrate in kbps [0.5:250]\n");
MSG(" -p <int> RF power (dBm)\n");
MSG(" -z <uint> Payload size in bytes [9:255]\n");
MSG(" -t <int> pause between packets (ms)\n");
MSG(" -x <int> numbers of times the sequence is repeated\n");
MSG(" -v <uint> test ID, inserted in payload for PER test [0:255]\n");
MSG(" -i send packet using inverted modulation polarity \n");
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
int i, j, x;
unsigned int xu;
char arg_s[64];
/* application parameters */
char mod[64] = "LORA"; /* LoRa modulation by default */
float f_target = 866.0; /* target frequency */
int sf = 10; /* SF10 by default */
int bw = 125; /* 125kHz bandwidth by default */
int pow = 14; /* 14 dBm by default */
int delay = 1000; /* 1 second between packets by default */
int repeat = 1; /* sweep only once by default */
bool invert = false;
float br_kbps = 50; /* 50 kbps by default */
uint8_t fdev_khz = 25; /* 25 khz by default */
/* packet payload variables */
int payload_size = 9; /* minimum size for PER frame */
uint8_t payload_bin[255];
char payload_b64[341];
int payload_index;
/* PER payload variables */
uint8_t id = 0;
/* server socket creation */
int sock; /* socket file descriptor */
struct addrinfo hints;
struct addrinfo *result; /* store result of getaddrinfo */
struct addrinfo *q; /* pointer to move into *result data */
char serv_port[8] = "1680";
char host_name[64];
char port_name[64];
/* variables for receiving and sending packets */
struct sockaddr_storage dist_addr;
socklen_t addr_len = sizeof dist_addr;
uint8_t databuf[500];
int buff_index;
int byte_nb;
/* variables for gateway identification */
uint32_t raw_mac_h; /* Most Significant Nibble, network order */
uint32_t raw_mac_l; /* Least Significant Nibble, network order */
uint64_t gw_mac; /* MAC address of the client (gateway) */
/* prepare hints to open network sockets */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* should handle IP v4 or v6 automatically */
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; /* will assign local IP automatically */
/* parse command line options */
while ((i = getopt (argc, argv, "hn:f:m:s:b:d:r:p:z:t:x:v:i")) != -1) {
switch (i) {
case 'h':
usage();
return EXIT_FAILURE;
break;
case 'n': /* -n <int or service> port number for gateway link */
strncpy(serv_port, optarg, sizeof serv_port);
break;
case 'f': /* -f <float> target frequency in MHz */
i = sscanf(optarg, "%f", &f_target);
if ((i != 1) || (f_target < 30.0) || (f_target > 3000.0)) {
MSG("ERROR: invalid TX frequency\n");
return EXIT_FAILURE;
}
break;
case 'm': /* -m <str> Modulation type */
i = sscanf(optarg, "%s", arg_s);
if ((i != 1) || ((strcmp(arg_s, "LORA") != 0) && (strcmp(arg_s, "FSK")))) {
MSG("ERROR: invalid modulation type\n");
usage();
return EXIT_FAILURE;
} else {
sprintf(mod, "%s", arg_s);
}
break;
case 's': /* -s <int> Spreading Factor */
i = sscanf(optarg, "%i", &sf);
if ((i != 1) || (sf < 7) || (sf > 12)) {
MSG("ERROR: invalid spreading factor\n");
return EXIT_FAILURE;
}
break;
case 'b': /* -b <int> Modulation bandwidth in kHz */
i = sscanf(optarg, "%i", &bw);
if ((i != 1) || ((bw != 125) && (bw != 250) && (bw != 500))) {
MSG("ERROR: invalid LORA bandwidth\n");
return EXIT_FAILURE;
}
break;
case 'd': /* -d <uint> FSK frequency deviation */
i = sscanf(optarg, "%u", &xu);
if ((i != 1) || (xu < 1) || (xu > 250)) {
MSG("ERROR: invalid FSK frequency deviation\n");
usage();
return EXIT_FAILURE;
} else {
fdev_khz = (uint8_t)xu;
}
break;
case 'r': /* -q <float> FSK bitrate */
i = sscanf(optarg, "%f", &br_kbps);
if ((i != 1) || (br_kbps < 0.5) || (br_kbps > 250)) {
MSG("ERROR: invalid FSK bitrate\n");
usage();
return EXIT_FAILURE;
}
break;
case 'p': /* -p <int> RF power */
i = sscanf(optarg, "%i", &pow);
if ((i != 1) || (pow < 0) || (pow > 30)) {
MSG("ERROR: invalid RF power\n");
return EXIT_FAILURE;
}
break;
case 'z': /* -z <uint> Payload size */
i = sscanf(optarg, "%i", &payload_size);
if ((i != 1) || (payload_size < 9) || (payload_size > 255)) {
MSG("ERROR: invalid payload size\n");
usage();
return EXIT_FAILURE;
}
break;
case 't': /* -t <int> pause between RF packets (ms) */
i = sscanf(optarg, "%i", &delay);
if ((i != 1) || (delay < 0)) {
MSG("ERROR: invalid time between RF packets\n");
return EXIT_FAILURE;
}
break;
case 'x': /* -x <int> numbers of times the sequence is repeated */
i = sscanf(optarg, "%u", &repeat);
if ((i != 1) || (repeat < 1)) {
MSG("ERROR: invalid number of repeats\n");
return EXIT_FAILURE;
}
break;
case 'v': /* -v <uint> test Id */
i = sscanf(optarg, "%u", &xu);
if ((i != 1) || ((xu < 1) && (xu > 255))) {
MSG("ERROR: invalid Id\n");
return EXIT_FAILURE;
} else {
id = (uint8_t)xu;
}
break;
case 'i': /* -i send packet using inverted modulation polarity */
invert = true;
break;
default:
MSG("ERROR: argument parsing failure, use -h option for help\n");
usage();
return EXIT_FAILURE;
}
}
/* compose local address (auto-complete a structure for socket) */
i = getaddrinfo(NULL, serv_port, &hints, &result);
if (i != 0) {
MSG("ERROR: getaddrinfo returned %s\n", gai_strerror(i));
exit(EXIT_FAILURE);
}
/* try to open socket and bind to it */
for (q=result; q!=NULL; q=q->ai_next) {
sock = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
if (sock == -1) {
continue; /* socket failed, try next field */
} else {
i = bind(sock, q->ai_addr, q->ai_addrlen);
if (i == -1) {
shutdown(sock, SHUT_RDWR);
continue; /* bind failed, try next field */
} else {
break; /* success, get out of loop */
}
}
}
if (q == NULL) {
MSG("ERROR: failed to open socket or to bind to it\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
/* configure signal handling */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
/* display setup summary */
if (strcmp(mod, "FSK") == 0) {
MSG("INFO: %i FSK pkts @%f MHz (FDev %u kHz, Bitrate %.2f kbps, %uB payload) %i dBm, %i ms between each\n", repeat, f_target, fdev_khz, br_kbps, payload_size, pow, delay);
} else {
MSG("INFO: %i LoRa pkts @%f MHz (BW %u kHz, SF%i, %uB payload) %i dBm, %i ms between each\n", repeat, f_target, bw, sf, payload_size, pow, delay);
}
/* wait to receive a PULL_DATA request packet */
MSG("INFO: waiting to receive a PULL_DATA request on port %s\n", serv_port);
while (1) {
byte_nb = recvfrom(sock, databuf, sizeof databuf, 0, (struct sockaddr *)&dist_addr, &addr_len);
if ((quit_sig == 1) || (exit_sig == 1)) {
exit(EXIT_SUCCESS);
} else if (byte_nb < 0) {
MSG("WARNING: recvfrom returned an error\n");
} else if ((byte_nb < 12) || (databuf[0] != PROTOCOL_VERSION) || (databuf[3] != PKT_PULL_DATA)) {
MSG("INFO: packet received, not PULL_DATA request\n");
} else {
break; /* success! */
}
}
/* retrieve gateway MAC from the request */
raw_mac_h = *((uint32_t *)(databuf+4));
raw_mac_l = *((uint32_t *)(databuf+8));
gw_mac = ((uint64_t)ntohl(raw_mac_h) << 32) + (uint64_t)ntohl(raw_mac_l);
/* display info about the sender */
i = getnameinfo((struct sockaddr *)&dist_addr, addr_len, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
if (i == -1) {
MSG("ERROR: getnameinfo returned %s \n", gai_strerror(i));
exit(EXIT_FAILURE);
}
MSG("INFO: PULL_DATA request received from gateway 0x%08X%08X (host %s, port %s)\n", (uint32_t)(gw_mac >> 32), (uint32_t)(gw_mac & 0xFFFFFFFF), host_name, port_name);
/* PKT_PULL_RESP datagrams header */
databuf[0] = PROTOCOL_VERSION;
databuf[1] = 0; /* no token */
databuf[2] = 0; /* no token */
databuf[3] = PKT_PULL_RESP;
buff_index = 4;
/* start of JSON structure */
memcpy((void *)(databuf + buff_index), (void *)"{\"txpk\":{\"imme\":true", 20);
buff_index += 20;
/* TX frequency */
i = snprintf((char *)(databuf + buff_index), 20, ",\"freq\":%.6f", f_target);
if ((i>=0) && (i < 20)) {
buff_index += i;
} else {
MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* RF channel */
memcpy((void *)(databuf + buff_index), (void *)",\"rfch\":0", 9);
buff_index += 9;
/* TX power */
i = snprintf((char *)(databuf + buff_index), 12, ",\"powe\":%i", pow);
if ((i>=0) && (i < 12)) {
buff_index += i;
} else {
MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* modulation type and parameters */
if (strcmp(mod, "FSK") == 0) {
i = snprintf((char *)(databuf + buff_index), 50, ",\"modu\":\"FSK\",\"datr\":%u,\"fdev\":%u", (unsigned int)(br_kbps*1e3), (unsigned int)(fdev_khz*1e3));
if ((i>=0) && (i < 50)) {
buff_index += i;
} else {
MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
} else {
i = snprintf((char *)(databuf + buff_index), 50, ",\"modu\":\"LORA\",\"datr\":\"SF%iBW%i\",\"codr\":\"4/6\"", sf, bw);
if ((i>=0) && (i < 50)) {
buff_index += i;
} else {
MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
}
/* signal polarity */
if (invert) {
memcpy((void *)(databuf + buff_index), (void *)",\"ipol\":true", 12);
buff_index += 12;
} else {
memcpy((void *)(databuf + buff_index), (void *)",\"ipol\":false", 13);
buff_index += 13;
}
/* Preamble size */
memcpy((void *)(databuf + buff_index), (void *)",\"prea\":8", 9);
buff_index += 9;
/* payload size */
i = snprintf((char *)(databuf + buff_index), 12, ",\"size\":%i", payload_size);
if ((i>=0) && (i < 12)) {
buff_index += i;
} else {
MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* payload JSON object */
memcpy((void *)(databuf + buff_index), (void *)",\"data\":\"", 9);
buff_index += 9;
payload_index = buff_index; /* keep the value where the payload content start */
/* payload place-holder & end of JSON structure */
x = bin_to_b64(payload_bin, payload_size, payload_b64, sizeof payload_b64); /* dummy conversion to get exact size */
if (x >= 0) {
buff_index += x;
} else {
MSG("ERROR: bin_to_b64 failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* Close JSON structure */
memcpy((void *)(databuf + buff_index), (void *)"\"}}", 3);
buff_index += 3; /* ends up being the total length of payload */
/* main loop */
for (i = 0; i < repeat; ++i) {
/* fill payload */
payload_bin[0] = id;
payload_bin[1] = (uint8_t)(i >> 24);
payload_bin[2] = (uint8_t)(i >> 16);
payload_bin[3] = (uint8_t)(i >> 8);
payload_bin[4] = (uint8_t)(i);
payload_bin[5] = 'P';
payload_bin[6] = 'E';
payload_bin[7] = 'R';
payload_bin[8] = (uint8_t)(payload_bin[0] + payload_bin[1] + payload_bin[2] + payload_bin[3] + payload_bin[4] + payload_bin[5] + payload_bin[6] + payload_bin[7]);
for (j = 0; j < (payload_size - 9); j++) {
payload_bin[9+j] = j;
}
#if 0
for (j = 0; j < payload_size; j++ ) {
printf("0x%02X ", payload_bin[j]);
}
printf("\n");
#endif
/* encode the payload in Base64 */
x = bin_to_b64(payload_bin, payload_size, payload_b64, sizeof payload_b64);
if (x >= 0) {
memcpy((void *)(databuf + payload_index), (void *)payload_b64, x);
} else {
MSG("ERROR: bin_to_b64 failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* send packet to the gateway */
byte_nb = sendto(sock, (void *)databuf, buff_index, 0, (struct sockaddr *)&dist_addr, addr_len);
if (byte_nb == -1) {
MSG("WARNING: sendto returned an error %s\n", strerror(errno));
} else {
MSG("INFO: packet #%i sent successfully\n", i);
}
/* wait inter-packet delay */
usleep(delay * 1000);
/* exit loop on user signals */
if ((quit_sig == 1) || (exit_sig == 1)) {
break;
}
}
exit(EXIT_SUCCESS);
}
/* --- EOF ------------------------------------------------------------------ */