summaryrefslogtreecommitdiff
path: root/util_pkt_logger
diff options
context:
space:
mode:
authorHarsh Sharma <92harshsharma@gmail.com>2018-06-13 13:24:54 -0500
committerHarsh Sharma <92harshsharma@gmail.com>2018-06-13 13:24:54 -0500
commit7c383be1542368f2601015d9fc2a417197677677 (patch)
treebc06453f879cbadf65fd88123c506956403c5684 /util_pkt_logger
downloadlora_gateway_mtac_full-7c383be1542368f2601015d9fc2a417197677677.tar.gz
lora_gateway_mtac_full-7c383be1542368f2601015d9fc2a417197677677.tar.bz2
lora_gateway_mtac_full-7c383be1542368f2601015d9fc2a417197677677.zip
Initial Commit
Diffstat (limited to 'util_pkt_logger')
-rw-r--r--util_pkt_logger/Makefile70
-rw-r--r--util_pkt_logger/global_conf.json87
-rw-r--r--util_pkt_logger/inc/parson.h222
-rw-r--r--util_pkt_logger/local_conf.json5
-rw-r--r--util_pkt_logger/readme.md143
-rw-r--r--util_pkt_logger/src/parson.c1765
-rw-r--r--util_pkt_logger/src/util_pkt_logger.c626
7 files changed, 2918 insertions, 0 deletions
diff --git a/util_pkt_logger/Makefile b/util_pkt_logger/Makefile
new file mode 100644
index 0000000..264ccc1
--- /dev/null
+++ b/util_pkt_logger/Makefile
@@ -0,0 +1,70 @@
+### Application-specific constants
+
+APP_NAME := util_pkt_logger
+
+### Environment constants
+
+LGW_PATH ?= ../libloragw
+ARCH ?=
+CROSS_COMPILE ?=
+
+### External constant definitions
+
+include $(LGW_PATH)/library.cfg
+
+### Constant symbols
+
+CC := $(CROSS_COMPILE)gcc
+AR := $(CROSS_COMPILE)ar
+
+CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I.
+
+OBJDIR = obj
+
+### Constants for LoRa concentrator HAL library
+# List the library sub-modules that are used by the application
+
+LGW_INC = $(LGW_PATH)/inc/config.h
+LGW_INC += $(LGW_PATH)/inc/loragw_hal.h
+
+### Linking options
+
+LIBS := -lloragw -lrt -lm
+
+### General build targets
+
+all: $(APP_NAME)
+
+clean:
+ rm -f $(OBJDIR)/*.o
+ rm -f $(APP_NAME)
+
+### HAL library (do no force multiple library rebuild even with 'make -B')
+
+$(LGW_PATH)/inc/config.h:
+ @if test ! -f $@; then \
+ $(MAKE) all -C $(LGW_PATH); \
+ fi
+
+$(LGW_PATH)/libloragw.a: $(LGW_INC)
+ @if test ! -f $@; then \
+ $(MAKE) all -C $(LGW_PATH); \
+ fi
+
+### Sub-modules compilation
+
+$(OBJDIR):
+ mkdir -p $(OBJDIR)
+
+$(OBJDIR)/parson.o: src/parson.c inc/parson.h | $(OBJDIR)
+ $(CC) -c $(CFLAGS) $< -o $@
+
+### Main program compilation and assembly
+
+$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) inc/parson.h | $(OBJDIR)
+ $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@
+
+$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a $(OBJDIR)/parson.o
+ $(CC) -L$(LGW_PATH) $< $(OBJDIR)/parson.o -o $@ $(LIBS)
+
+### EOF
diff --git a/util_pkt_logger/global_conf.json b/util_pkt_logger/global_conf.json
new file mode 100644
index 0000000..a3f327a
--- /dev/null
+++ b/util_pkt_logger/global_conf.json
@@ -0,0 +1,87 @@
+{
+ "SX1301_conf": {
+ "lorawan_public": true,
+ "clksrc": 1, /* radio_1 provides clock to concentrator */
+ "radio_0": {
+ "enable": true,
+ "type": "SX1257",
+ "freq": 867500000,
+ "rssi_offset": -166.0,
+ "tx_enable": false
+ },
+ "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
+ }
+ },
+ "gateway_conf": {
+ "gateway_ID": "AA555A0000000000"
+ }
+}
diff --git a/util_pkt_logger/inc/parson.h b/util_pkt_logger/inc/parson.h
new file mode 100644
index 0000000..2669a18
--- /dev/null
+++ b/util_pkt_logger/inc/parson.h
@@ -0,0 +1,222 @@
+/*
+ Parson ( http://kgabis.github.com/parson/ )
+ Copyright (c) 2012 - 2016 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 */
+
+/* Types and enums */
+typedef struct json_object_t JSON_Object;
+typedef struct json_array_t JSON_Array;
+typedef struct json_value_t JSON_Value;
+
+enum json_value_type {
+ JSONError = -1,
+ JSONNull = 1,
+ JSONString = 2,
+ JSONNumber = 3,
+ JSONObject = 4,
+ JSONArray = 5,
+ JSONBoolean = 6
+};
+typedef int JSON_Value_Type;
+
+enum json_result_t {
+ JSONSuccess = 0,
+ JSONFailure = -1
+};
+typedef int JSON_Status;
+
+typedef void * (*JSON_Malloc_Function)(size_t);
+typedef void (*JSON_Free_Function)(void *);
+
+/* Call only once, before calling any other function from parson API. If not called, malloc and free
+ from stdlib will be used for all allocations */
+void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
+
+/* 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);
+
+/* Serialization */
+size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
+JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
+JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
+char * json_serialize_to_string(const JSON_Value *value);
+
+/* Pretty serialization */
+size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
+JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
+JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
+char * json_serialize_to_string_pretty(const JSON_Value *value);
+
+void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
+
+/* Comparing */
+int json_value_equals(const JSON_Value *a, const JSON_Value *b);
+
+/* Validation
+ This is *NOT* JSON Schema. It validates json by checking if object have identically
+ named fields with matching types.
+ For example schema {"name":"", "age":0} will validate
+ {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
+ but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
+ In case of arrays, only first value in schema is checked against all values in tested array.
+ Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
+ null validates values of every type.
+ */
+JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
+
+/*
+ * 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); /* returns 0 on fail */
+int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
+
+/* 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); /* returns 0 on fail */
+int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
+
+/* 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);
+
+/* Creates new name-value pair or frees and replaces old value with a new one.
+ * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
+JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
+JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
+JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
+JSON_Status json_object_set_null(JSON_Object *object, const char *name);
+
+/* Works like dotget functions, but creates whole hierarchy if necessary.
+ * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
+JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
+JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
+JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
+JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
+
+/* Frees and removes name-value pair */
+JSON_Status json_object_remove(JSON_Object *object, const char *name);
+
+/* Works like dotget function, but removes name-value pair only on exact match. */
+JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
+
+/* Removes all name-value pairs in object */
+JSON_Status json_object_clear(JSON_Object *object);
+
+/*
+ *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); /* returns 0 on fail */
+int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
+size_t json_array_get_count (const JSON_Array *array);
+
+/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
+ * Order of values in array may change during execution. */
+JSON_Status json_array_remove(JSON_Array *array, size_t i);
+
+/* Frees and removes from array value at given index and replaces it with given one.
+ * Does nothing and returns JSONFailure if index doesn't exist.
+ * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
+JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
+JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
+JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
+JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
+
+/* Frees and removes all values from array */
+JSON_Status json_array_clear(JSON_Array *array);
+
+/* Appends new value at the end of array.
+ * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
+JSON_Status json_array_append_string(JSON_Array *array, const char *string);
+JSON_Status json_array_append_number(JSON_Array *array, double number);
+JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
+JSON_Status json_array_append_null(JSON_Array *array);
+
+/*
+ *JSON Value
+ */
+JSON_Value * json_value_init_object (void);
+JSON_Value * json_value_init_array (void);
+JSON_Value * json_value_init_string (const char *string); /* copies passed string */
+JSON_Value * json_value_init_number (double number);
+JSON_Value * json_value_init_boolean(int boolean);
+JSON_Value * json_value_init_null (void);
+JSON_Value * json_value_deep_copy (const JSON_Value *value);
+void json_value_free (JSON_Value *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);
+
+/* Same as above, but shorter */
+JSON_Value_Type json_type (const JSON_Value *value);
+JSON_Object * json_object (const JSON_Value *value);
+JSON_Array * json_array (const JSON_Value *value);
+const char * json_string (const JSON_Value *value);
+double json_number (const JSON_Value *value);
+int json_boolean(const JSON_Value *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/util_pkt_logger/local_conf.json b/util_pkt_logger/local_conf.json
new file mode 100644
index 0000000..51fc7c2
--- /dev/null
+++ b/util_pkt_logger/local_conf.json
@@ -0,0 +1,5 @@
+{
+ "gateway_conf": {
+ "gateway_ID": "AA555A0000000101"
+ }
+}
diff --git a/util_pkt_logger/readme.md b/util_pkt_logger/readme.md
new file mode 100644
index 0000000..a980bd4
--- /dev/null
+++ b/util_pkt_logger/readme.md
@@ -0,0 +1,143 @@
+ / _____) _ | |
+ ( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+ (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech-Cycleo
+
+LoRa packet logger
+===================
+
+1. Introduction
+----------------
+
+This software is used to set up a LoRa concentrator using a JSON configuration
+file and then record all the packets received in a log file, indefinitely, until
+the user stops the application.
+No filtering is done and all packets that are LoRa packets with the correct RF
+parameters (frequency, datarate, bandwidth) should appear in the log.
+
+2. 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 a typical example of LoRa concentrator HAL usage for receiving
+packets.
+
+Only high-level functions are used (the ones contained in loragw_hal) so there
+is no hardware dependencies assuming the HAL is matched with the proper version
+of the hardware.
+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.
+
+It was tested with v1.3.0 of the libloragw library, and should be compatible
+with any later version of the library assuming the API is downward-compatible.
+
+3. Usage
+---------
+
+To stop the application, press Ctrl+C.
+
+The only optional parameter when launching the application is the log rotation
+time (in seconds).
+
+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 and 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).
+
+If you have build the libloragw library for a specific radio band (eg. ETSI
+868 MHz band) a ready-to-use global_conf.json file is generated by the Makefile
+with a set of channels typical for a 'LoRa MAC' network application.
+If you don't specify a radio band, an empty global_conf.json is generated and
+must be filled with the settings you need.
+
+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 LoRa MAC controller, network authentication parameters, etc).
+
+To learn more about the JSON configuration format, read the provided JSON files
+and the API documentation. A dedicated document will be available later on.
+
+The received packets are put in a CSV file whose name include the MAC address of
+the gateway in hexadecimal format and a UTC timestamp of log starting time in
+ISO 8601 recommended compact format:
+yyyymmddThhmmssZ (eg. 20131009T172345Z for October 9th, 2013 at 5:23:45PM UTC)
+
+To able continuous monitoring, the current log file is closed is closed and a
+new one is opened every hour (by default, rotation interval is settable by the
+user using -r command line option).
+No packet is lost during that rotation of log file.
+Every log file but the current one can then be modified, uploaded and/or deleted
+without any consequence for the program execution.
+
+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.
+
+5. 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* \ No newline at end of file
diff --git a/util_pkt_logger/src/parson.c b/util_pkt_logger/src/parson.c
new file mode 100644
index 0000000..16bb158
--- /dev/null
+++ b/util_pkt_logger/src/parson.c
@@ -0,0 +1,1765 @@
+/*
+ Parson ( http://kgabis.github.com/parson/ )
+ Copyright (c) 2012 - 2016 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.
+*/
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "parson.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#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 DOUBLE_SERIALIZATION_FORMAT "%f"
+
+#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))
+
+#undef malloc
+#undef free
+
+static JSON_Malloc_Function parson_malloc = malloc;
+static JSON_Free_Function parson_free = free;
+
+#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
+
+/* Type definitions */
+typedef union json_value_value {
+ 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 {
+ 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 char * parson_strndup(const char *string, size_t n);
+static char * parson_strdup(const char *string);
+static int is_utf16_hex(const unsigned char *string);
+static int num_bytes_in_utf8_sequence(unsigned char c);
+static int verify_utf8_sequence(const unsigned char *string, int *len);
+static int is_valid_utf8(const char *string, size_t string_len);
+static int is_decimal(const char *string, size_t length);
+
+/* JSON Object */
+static JSON_Object * json_object_init(void);
+static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value);
+static JSON_Status json_object_resize(JSON_Object *object, size_t new_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 JSON_Status json_array_add(JSON_Array *array, JSON_Value *value);
+static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity);
+static void json_array_free(JSON_Array *array);
+
+/* JSON Value */
+static JSON_Value * json_value_init_string_no_copy(char *string);
+
+/* Parser */
+static void skip_quotes(const char **string);
+static int parse_utf_16(const char **unprocessed, char **processed);
+static char * process_string(const char *input, size_t len);
+static char * get_quoted_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);
+
+/* Serialization */
+static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf);
+static int json_serialize_string(const char *string, char *buf);
+static int append_indent(char *buf, int level);
+static int append_string(char *buf, const char *string);
+
+/* Various */
+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 char * parson_strdup(const char *string) {
+ return parson_strndup(string, strlen(string));
+}
+
+static int is_utf16_hex(const unsigned char *s) {
+ return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]);
+}
+
+static int num_bytes_in_utf8_sequence(unsigned char c) {
+ if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {
+ return 0;
+ } else if ((c & 0x80) == 0) { /* 0xxxxxxx */
+ return 1;
+ } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
+ return 2;
+ } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
+ return 3;
+ } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
+ return 4;
+ }
+ return 0; /* won't happen */
+}
+
+static int verify_utf8_sequence(const unsigned char *string, int *len) {
+ unsigned int cp = 0;
+ *len = num_bytes_in_utf8_sequence(string[0]);
+
+ if (*len == 1) {
+ cp = string[0];
+ } else if (*len == 2 && IS_CONT(string[1])) {
+ cp = string[0] & 0x1F;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
+ cp = ((unsigned char)string[0]) & 0xF;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
+ cp = string[0] & 0x7;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ cp = (cp << 6) | (string[3] & 0x3F);
+ } else {
+ return 0;
+ }
+
+ /* overlong encodings */
+ if ((cp < 0x80 && *len > 1) ||
+ (cp < 0x800 && *len > 2) ||
+ (cp < 0x10000 && *len > 3)) {
+ return 0;
+ }
+
+ /* invalid unicode */
+ if (cp > 0x10FFFF) {
+ return 0;
+ }
+
+ /* surrogate halves */
+ if (cp >= 0xD800 && cp <= 0xDFFF) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_valid_utf8(const char *string, size_t string_len) {
+ int len = 0;
+ const char *string_end = string + string_len;
+ while (string < string_end) {
+ if (!verify_utf8_sequence((const unsigned char*)string, &len)) {
+ return 0;
+ }
+ string += len;
+ }
+ return 1;
+}
+
+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;
+ long pos;
+ char *file_contents;
+ if (!fp)
+ return NULL;
+ fseek(fp, 0L, SEEK_END);
+ pos = ftell(fp);
+ if (pos < 0) {
+ fclose(fp);
+ return NULL;
+ }
+ file_size = pos;
+ 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 = (char**)NULL;
+ new_obj->values = (JSON_Value**)NULL;
+ new_obj->capacity = 0;
+ new_obj->count = 0;
+ return new_obj;
+}
+
+static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value) {
+ size_t index = 0;
+ if (object == NULL || name == NULL || value == NULL) {
+ return JSONFailure;
+ }
+ if (object->count >= object->capacity) {
+ size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY);
+ if (new_capacity > OBJECT_MAX_CAPACITY)
+ return JSONFailure;
+ if (json_object_resize(object, new_capacity) == JSONFailure)
+ return JSONFailure;
+ }
+ if (json_object_get_value(object, name) != NULL)
+ return JSONFailure;
+ index = object->count;
+ object->names[index] = parson_strdup(name);
+ if (object->names[index] == NULL)
+ return JSONFailure;
+ object->values[index] = value;
+ object->count++;
+ return JSONSuccess;
+}
+
+static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity) {
+ char **temp_names = NULL;
+ JSON_Value **temp_values = NULL;
+
+ if ((object->names == NULL && object->values != NULL) ||
+ (object->names != NULL && object->values == NULL) ||
+ new_capacity == 0) {
+ return JSONFailure; /* Shouldn't happen */
+ }
+
+ temp_names = (char**)parson_malloc(new_capacity * sizeof(char*));
+ if (temp_names == NULL)
+ return JSONFailure;
+
+ temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*));
+ if (temp_values == NULL) {
+ parson_free(temp_names);
+ return JSONFailure;
+ }
+
+ if (object->names != NULL && object->values != NULL && object->count > 0) {
+ memcpy(temp_names, object->names, object->count * sizeof(char*));
+ memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*));
+ }
+ parson_free(object->names);
+ parson_free(object->values);
+ object->names = temp_names;
+ object->values = temp_values;
+ object->capacity = new_capacity;
+ return JSONSuccess;
+}
+
+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 JSON_Status 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 JSONFailure;
+ if (json_array_resize(array, new_capacity) == JSONFailure)
+ return JSONFailure;
+ }
+ array->items[array->count] = value;
+ array->count++;
+ return JSONSuccess;
+}
+
+static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) {
+ JSON_Value **new_items = NULL;
+ if (new_capacity == 0) {
+ return JSONFailure;
+ }
+ new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*));
+ if (new_items == NULL) {
+ return JSONFailure;
+ }
+ if (array->items != NULL && array->count > 0) {
+ memcpy(new_items, array->items, array->count * sizeof(JSON_Value*));
+ }
+ parson_free(array->items);
+ array->items = new_items;
+ array->capacity = new_capacity;
+ return JSONSuccess;
+}
+
+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_string_no_copy(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;
+}
+
+/* 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);
+}
+
+static int parse_utf_16(const char **unprocessed, char **processed) {
+ unsigned int cp, lead, trail;
+ char *processed_ptr = *processed;
+ const char *unprocessed_ptr = *unprocessed;
+ unprocessed_ptr++; /* skips u */
+ if (!is_utf16_hex((const unsigned char*)unprocessed_ptr) || sscanf(unprocessed_ptr, "%4x", &cp) == EOF)
+ return JSONFailure;
+ if (cp < 0x80) {
+ *processed_ptr = cp; /* 0xxxxxxx */
+ } else if (cp < 0x800) {
+ *processed_ptr++ = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */
+ *processed_ptr = ((cp ) & 0x3F) | 0x80; /* 10xxxxxx */
+ } else if (cp < 0xD800 || cp > 0xDFFF) {
+ *processed_ptr++ = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */
+ *processed_ptr++ = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */
+ *processed_ptr = ((cp ) & 0x3F) | 0x80; /* 10xxxxxx */
+ } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */
+ lead = cp;
+ unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */
+ if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u' || /* starts with \u? */
+ !is_utf16_hex((const unsigned char*)unprocessed_ptr) ||
+ sscanf(unprocessed_ptr, "%4x", &trail) == EOF ||
+ trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */
+ return JSONFailure;
+ }
+ cp = ((((lead-0xD800)&0x3FF)<<10)|((trail-0xDC00)&0x3FF))+0x010000;
+ *processed_ptr++ = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */
+ *processed_ptr++ = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */
+ *processed_ptr++ = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */
+ *processed_ptr = (((cp ) & 0x3F) | 0x80); /* 10xxxxxx */
+ } else { /* trail surrogate before lead surrogate */
+ return JSONFailure;
+ }
+ unprocessed_ptr += 3;
+ *processed = processed_ptr;
+ *unprocessed = unprocessed_ptr;
+ return JSONSuccess;
+}
+
+
+/* Copies and processes passed string up to supplied length.
+Example: "\u006Corem ipsum" -> lorem ipsum */
+static char* process_string(const char *input, size_t len) {
+ const char *input_ptr = input;
+ size_t initial_size = (len + 1) * sizeof(char);
+ size_t final_size = 0;
+ char *output = (char*)parson_malloc(initial_size);
+ char *output_ptr = output;
+ char *resized_output = NULL;
+ while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < len) {
+ if (*input_ptr == '\\') {
+ input_ptr++;
+ switch (*input_ptr) {
+ case '\"': *output_ptr = '\"'; break;
+ case '\\': *output_ptr = '\\'; break;
+ case '/': *output_ptr = '/'; break;
+ case 'b': *output_ptr = '\b'; break;
+ case 'f': *output_ptr = '\f'; break;
+ case 'n': *output_ptr = '\n'; break;
+ case 'r': *output_ptr = '\r'; break;
+ case 't': *output_ptr = '\t'; break;
+ case 'u':
+ if (parse_utf_16(&input_ptr, &output_ptr) == JSONFailure)
+ goto error;
+ break;
+ default:
+ goto error;
+ }
+ } else if ((unsigned char)*input_ptr < 0x20) {
+ goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
+ } else {
+ *output_ptr = *input_ptr;
+ }
+ output_ptr++;
+ input_ptr++;
+ }
+ *output_ptr = '\0';
+ /* resize to new length */
+ final_size = (size_t)(output_ptr-output) + 1;
+ resized_output = (char*)parson_malloc(final_size);
+ if (resized_output == NULL)
+ goto error;
+ memcpy(resized_output, output, final_size);
+ parson_free(output);
+ return resized_output;
+error:
+ parson_free(output);
+ return NULL;
+}
+
+/* Return processed contents of a string between quotes and
+ skips passed argument to a matching quote. */
+static char * get_quoted_string(const char **string) {
+ const char *string_start = *string;
+ size_t string_len = 0;
+ skip_quotes(string);
+ if (**string == '\0')
+ return NULL;
+ string_len = *string - string_start - 2; /* length without quotes */
+ return process_string(string_start + 1, string_len);
+}
+
+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);
+ char *new_key = NULL;
+ if (output_value == NULL)
+ return NULL;
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == '}') { /* empty object */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ new_key = get_quoted_string(string);
+ SKIP_WHITESPACES(string);
+ if (new_key == NULL || **string != ':') {
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ new_value = parse_value(string, nesting);
+ if (new_value == NULL) {
+ parson_free(new_key);
+ json_value_free(output_value);
+ return NULL;
+ }
+ if(json_object_add(output_object, new_key, new_value) == JSONFailure) {
+ 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)) == JSONFailure) {
+ 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) == JSONFailure) {
+ 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)) == JSONFailure) {
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_Value * parse_string_value(const char **string) {
+ JSON_Value *value = NULL;
+ char *new_string = get_quoted_string(string);
+ if (new_string == NULL)
+ return NULL;
+ value = json_value_init_string_no_copy(new_string);
+ if (value == NULL) {
+ parson_free(new_string);
+ return NULL;
+ }
+ return value;
+}
+
+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;
+}
+
+/* Serialization */
+#define APPEND_STRING(str) do { written = append_string(buf, (str)); \
+ if (written < 0) { return -1; } \
+ if (buf != NULL) { buf += written; } \
+ written_total += written; } while(0)
+
+#define APPEND_INDENT(level) do { written = append_indent(buf, (level)); \
+ if (written < 0) { return -1; } \
+ if (buf != NULL) { buf += written; } \
+ written_total += written; } while(0)
+
+static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf)
+{
+ const char *key = NULL, *string = NULL;
+ JSON_Value *temp_value = NULL;
+ JSON_Array *array = NULL;
+ JSON_Object *object = NULL;
+ size_t i = 0, count = 0;
+ double num = 0.0;
+ int written = -1, written_total = 0;
+
+ switch (json_value_get_type(value)) {
+ case JSONArray:
+ array = json_value_get_array(value);
+ count = json_array_get_count(array);
+ APPEND_STRING("[");
+ if (count > 0 && is_pretty)
+ APPEND_STRING("\n");
+ for (i = 0; i < count; i++) {
+ if (is_pretty)
+ APPEND_INDENT(level+1);
+ temp_value = json_array_get_value(array, i);
+ written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);
+ if (written < 0)
+ return -1;
+ if (buf != NULL)
+ buf += written;
+ written_total += written;
+ if (i < (count - 1))
+ APPEND_STRING(",");
+ if (is_pretty)
+ APPEND_STRING("\n");
+ }
+ if (count > 0 && is_pretty)
+ APPEND_INDENT(level);
+ APPEND_STRING("]");
+ return written_total;
+ case JSONObject:
+ object = json_value_get_object(value);
+ count = json_object_get_count(object);
+ APPEND_STRING("{");
+ if (count > 0 && is_pretty)
+ APPEND_STRING("\n");
+ for (i = 0; i < count; i++) {
+ key = json_object_get_name(object, i);
+ if (is_pretty)
+ APPEND_INDENT(level+1);
+ written = json_serialize_string(key, buf);
+ if (written < 0)
+ return -1;
+ if (buf != NULL)
+ buf += written;
+ written_total += written;
+ APPEND_STRING(":");
+ if (is_pretty)
+ APPEND_STRING(" ");
+ temp_value = json_object_get_value(object, key);
+ written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);
+ if (written < 0)
+ return -1;
+ if (buf != NULL)
+ buf += written;
+ written_total += written;
+ if (i < (count - 1))
+ APPEND_STRING(",");
+ if (is_pretty)
+ APPEND_STRING("\n");
+ }
+ if (count > 0 && is_pretty)
+ APPEND_INDENT(level);
+ APPEND_STRING("}");
+ return written_total;
+ case JSONString:
+ string = json_value_get_string(value);
+ written = json_serialize_string(string, buf);
+ if (written < 0)
+ return -1;
+ if (buf != NULL)
+ buf += written;
+ written_total += written;
+ return written_total;
+ case JSONBoolean:
+ if (json_value_get_boolean(value))
+ APPEND_STRING("true");
+ else
+ APPEND_STRING("false");
+ return written_total;
+ case JSONNumber:
+ num = json_value_get_number(value);
+ if (buf != NULL)
+ num_buf = buf;
+ if (num == ((double)(int)num)) /* check if num is integer */
+ written = sprintf(num_buf, "%d", (int)num);
+ else
+ written = sprintf(num_buf, DOUBLE_SERIALIZATION_FORMAT, num);
+ if (written < 0)
+ return -1;
+ if (buf != NULL)
+ buf += written;
+ written_total += written;
+ return written_total;
+ case JSONNull:
+ APPEND_STRING("null");
+ return written_total;
+ case JSONError:
+ return -1;
+ default:
+ return -1;
+ }
+}
+
+static int json_serialize_string(const char *string, char *buf) {
+ size_t i = 0, len = strlen(string);
+ char c = '\0';
+ int written = -1, written_total = 0;
+ APPEND_STRING("\"");
+ for (i = 0; i < len; i++) {
+ c = string[i];
+ switch (c) {
+ case '\"': APPEND_STRING("\\\""); break;
+ case '\\': APPEND_STRING("\\\\"); break;
+ case '/': APPEND_STRING("\\/"); break; /* to make json embeddable in xml\/html */
+ case '\b': APPEND_STRING("\\b"); break;
+ case '\f': APPEND_STRING("\\f"); break;
+ case '\n': APPEND_STRING("\\n"); break;
+ case '\r': APPEND_STRING("\\r"); break;
+ case '\t': APPEND_STRING("\\t"); break;
+ default:
+ if (buf != NULL) {
+ buf[0] = c;
+ buf += 1;
+ }
+ written_total += 1;
+ break;
+ }
+ }
+ APPEND_STRING("\"");
+ return written_total;
+}
+
+static int append_indent(char *buf, int level) {
+ int i;
+ int written = -1, written_total = 0;
+ for (i = 0; i < level; i++) {
+ APPEND_STRING(" ");
+ }
+ return written_total;
+}
+
+static int append_string(char *buf, const char *string) {
+ if (buf == NULL) {
+ return (int)strlen(string);
+ }
+ return sprintf(buf, "%s", string);
+}
+
+#undef APPEND_STRING
+#undef APPEND_INDENT
+
+/* Parser API */
+JSON_Value * json_parse_file(const char *filename) {
+ char *file_contents = read_file(filename);
+ JSON_Value *output_value = NULL;
+ if (file_contents == NULL)
+ 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 == NULL)
+ 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 == NULL)
+ return NULL;
+ SKIP_WHITESPACES(&string);
+ if (*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_strdup(string);
+ if (string_mutable_copy == NULL)
+ 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) {
+ if (object == NULL || name == NULL)
+ return NULL;
+ 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);
+}
+
+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;
+}
+
+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;
+}
+
+JSON_Value * json_value_init_string(const char *string) {
+ char *copy = NULL;
+ JSON_Value *value;
+ size_t string_len = 0;
+ if (string == NULL)
+ return NULL;
+ string_len = strlen(string);
+ if (!is_valid_utf8(string, string_len))
+ return NULL;
+ copy = parson_strndup(string, string_len);
+ if (copy == NULL)
+ return NULL;
+ value = json_value_init_string_no_copy(copy);
+ if (value == NULL)
+ parson_free(copy);
+ return value;
+}
+
+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;
+}
+
+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 ? 1 : 0;
+ return new_value;
+}
+
+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;
+}
+
+JSON_Value * json_value_deep_copy(const JSON_Value *value) {
+ size_t i = 0;
+ JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;
+ const char *temp_string = NULL, *temp_key = NULL;
+ char *temp_string_copy = NULL;
+ JSON_Array *temp_array = NULL, *temp_array_copy = NULL;
+ JSON_Object *temp_object = NULL, *temp_object_copy = NULL;
+
+ switch (json_value_get_type(value)) {
+ case JSONArray:
+ temp_array = json_value_get_array(value);
+ return_value = json_value_init_array();
+ if (return_value == NULL)
+ return NULL;
+ temp_array_copy = json_value_get_array(return_value);
+ for (i = 0; i < json_array_get_count(temp_array); i++) {
+ temp_value = json_array_get_value(temp_array, i);
+ temp_value_copy = json_value_deep_copy(temp_value);
+ if (temp_value_copy == NULL) {
+ json_value_free(return_value);
+ return NULL;
+ }
+ if (json_array_add(temp_array_copy, temp_value_copy) == JSONFailure) {
+ json_value_free(return_value);
+ json_value_free(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSONObject:
+ temp_object = json_value_get_object(value);
+ return_value = json_value_init_object();
+ if (return_value == NULL)
+ return NULL;
+ temp_object_copy = json_value_get_object(return_value);
+ for (i = 0; i < json_object_get_count(temp_object); i++) {
+ temp_key = json_object_get_name(temp_object, i);
+ temp_value = json_object_get_value(temp_object, temp_key);
+ temp_value_copy = json_value_deep_copy(temp_value);
+ if (temp_value_copy == NULL) {
+ json_value_free(return_value);
+ return NULL;
+ }
+ if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSONFailure) {
+ json_value_free(return_value);
+ json_value_free(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSONBoolean:
+ return json_value_init_boolean(json_value_get_boolean(value));
+ case JSONNumber:
+ return json_value_init_number(json_value_get_number(value));
+ case JSONString:
+ temp_string = json_value_get_string(value);
+ temp_string_copy = parson_strdup(temp_string);
+ if (temp_string_copy == NULL)
+ return NULL;
+ return_value = json_value_init_string_no_copy(temp_string_copy);
+ if (return_value == NULL)
+ parson_free(temp_string_copy);
+ return return_value;
+ case JSONNull:
+ return json_value_init_null();
+ case JSONError:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+size_t json_serialization_size(const JSON_Value *value) {
+ char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf);
+ return res < 0 ? 0 : (size_t)(res + 1);
+}
+
+JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {
+ int written = -1;
+ size_t needed_size_in_bytes = json_serialization_size(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSONFailure;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL);
+ if (written < 0)
+ return JSONFailure;
+ return JSONSuccess;
+}
+
+JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) {
+ JSON_Status return_code = JSONSuccess;
+ FILE *fp = NULL;
+ char *serialized_string = json_serialize_to_string(value);
+ if (serialized_string == NULL) {
+ return JSONFailure;
+ }
+ fp = fopen (filename, "w");
+ if (fp != NULL) {
+ if (fputs (serialized_string, fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ if (fclose (fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ }
+ json_free_serialized_string(serialized_string);
+ return return_code;
+}
+
+char * json_serialize_to_string(const JSON_Value *value) {
+ JSON_Status serialization_result = JSONFailure;
+ size_t buf_size_bytes = json_serialization_size(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char*)parson_malloc(buf_size_bytes);
+ if (buf == NULL)
+ return NULL;
+ serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes);
+ if (serialization_result == JSONFailure) {
+ json_free_serialized_string(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+size_t json_serialization_size_pretty(const JSON_Value *value) {
+ char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf);
+ return res < 0 ? 0 : (size_t)(res + 1);
+}
+
+JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {
+ int written = -1;
+ size_t needed_size_in_bytes = json_serialization_size_pretty(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes)
+ return JSONFailure;
+ written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL);
+ if (written < 0)
+ return JSONFailure;
+ return JSONSuccess;
+}
+
+JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) {
+ JSON_Status return_code = JSONSuccess;
+ FILE *fp = NULL;
+ char *serialized_string = json_serialize_to_string_pretty(value);
+ if (serialized_string == NULL) {
+ return JSONFailure;
+ }
+ fp = fopen (filename, "w");
+ if (fp != NULL) {
+ if (fputs (serialized_string, fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ if (fclose (fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ }
+ json_free_serialized_string(serialized_string);
+ return return_code;
+}
+
+char * json_serialize_to_string_pretty(const JSON_Value *value) {
+ JSON_Status serialization_result = JSONFailure;
+ size_t buf_size_bytes = json_serialization_size_pretty(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char*)parson_malloc(buf_size_bytes);
+ if (buf == NULL)
+ return NULL;
+ serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes);
+ if (serialization_result == JSONFailure) {
+ json_free_serialized_string(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+void json_free_serialized_string(char *string) {
+ parson_free(string);
+}
+
+JSON_Status json_array_remove(JSON_Array *array, size_t ix) {
+ JSON_Value *temp_value = NULL;
+ size_t last_element_ix = 0;
+ if (array == NULL || ix >= json_array_get_count(array)) {
+ return JSONFailure;
+ }
+ last_element_ix = json_array_get_count(array) - 1;
+ json_value_free(json_array_get_value(array, ix));
+ if (ix != last_element_ix) { /* Replace value with one from the end of array */
+ temp_value = json_array_get_value(array, last_element_ix);
+ if (temp_value == NULL) {
+ return JSONFailure;
+ }
+ array->items[ix] = temp_value;
+ }
+ array->count -= 1;
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) {
+ if (array == NULL || value == NULL || ix >= json_array_get_count(array)) {
+ return JSONFailure;
+ }
+ json_value_free(json_array_get_value(array, ix));
+ array->items[ix] = value;
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) {
+ JSON_Value *value = json_value_init_string(string);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_replace_value(array, i, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) {
+ JSON_Value *value = json_value_init_number(number);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_replace_value(array, i, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) {
+ JSON_Value *value = json_value_init_boolean(boolean);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_replace_value(array, i, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_null(JSON_Array *array, size_t i) {
+ JSON_Value *value = json_value_init_null();
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_replace_value(array, i, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_clear(JSON_Array *array) {
+ size_t i = 0;
+ if (array == NULL)
+ return JSONFailure;
+ for (i = 0; i < json_array_get_count(array); i++) {
+ json_value_free(json_array_get_value(array, i));
+ }
+ array->count = 0;
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) {
+ if (array == NULL || value == NULL)
+ return JSONFailure;
+ return json_array_add(array, value);
+}
+
+JSON_Status json_array_append_string(JSON_Array *array, const char *string) {
+ JSON_Value *value = json_value_init_string(string);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_append_value(array, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_number(JSON_Array *array, double number) {
+ JSON_Value *value = json_value_init_number(number);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_append_value(array, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) {
+ JSON_Value *value = json_value_init_boolean(boolean);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_append_value(array, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_null(JSON_Array *array) {
+ JSON_Value *value = json_value_init_null();
+ if (value == NULL)
+ return JSONFailure;
+ if (json_array_append_value(array, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) {
+ size_t i = 0;
+ JSON_Value *old_value;
+ if (object == NULL || name == NULL || value == NULL)
+ return JSONFailure;
+ old_value = json_object_get_value(object, name);
+ if (old_value != NULL) { /* free and overwrite old value */
+ json_value_free(old_value);
+ for (i = 0; i < json_object_get_count(object); i++) {
+ if (strcmp(object->names[i], name) == 0) {
+ object->values[i] = value;
+ return JSONSuccess;
+ }
+ }
+ }
+ /* add new key value pair */
+ return json_object_add(object, name, value);
+}
+
+JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) {
+ return json_object_set_value(object, name, json_value_init_string(string));
+}
+
+JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) {
+ return json_object_set_value(object, name, json_value_init_number(number));
+}
+
+JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) {
+ return json_object_set_value(object, name, json_value_init_boolean(boolean));
+}
+
+JSON_Status json_object_set_null(JSON_Object *object, const char *name) {
+ return json_object_set_value(object, name, json_value_init_null());
+}
+
+JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) {
+ const char *dot_pos = NULL;
+ char *current_name = NULL;
+ JSON_Object *temp_obj = NULL;
+ JSON_Value *new_value = NULL;
+ if (value == NULL || name == NULL || value == NULL)
+ return JSONFailure;
+ dot_pos = strchr(name, '.');
+ if (dot_pos == NULL) {
+ return json_object_set_value(object, name, value);
+ } else {
+ current_name = parson_strndup(name, dot_pos - name);
+ temp_obj = json_object_get_object(object, current_name);
+ if (temp_obj == NULL) {
+ new_value = json_value_init_object();
+ if (new_value == NULL) {
+ parson_free(current_name);
+ return JSONFailure;
+ }
+ if (json_object_add(object, current_name, new_value) == JSONFailure) {
+ json_value_free(new_value);
+ parson_free(current_name);
+ return JSONFailure;
+ }
+ temp_obj = json_object_get_object(object, current_name);
+ }
+ parson_free(current_name);
+ return json_object_dotset_value(temp_obj, dot_pos + 1, value);
+ }
+}
+
+JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) {
+ JSON_Value *value = json_value_init_string(string);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_object_dotset_value(object, name, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) {
+ JSON_Value *value = json_value_init_number(number);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_object_dotset_value(object, name, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) {
+ JSON_Value *value = json_value_init_boolean(boolean);
+ if (value == NULL)
+ return JSONFailure;
+ if (json_object_dotset_value(object, name, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) {
+ JSON_Value *value = json_value_init_null();
+ if (value == NULL)
+ return JSONFailure;
+ if (json_object_dotset_value(object, name, value) == JSONFailure) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_remove(JSON_Object *object, const char *name) {
+ size_t i = 0, last_item_index = 0;
+ if (object == NULL || json_object_get_value(object, name) == NULL)
+ return JSONFailure;
+ last_item_index = json_object_get_count(object) - 1;
+ for (i = 0; i < json_object_get_count(object); i++) {
+ if (strcmp(object->names[i], name) == 0) {
+ parson_free(object->names[i]);
+ json_value_free(object->values[i]);
+ if (i != last_item_index) { /* Replace key value pair with one from the end */
+ object->names[i] = object->names[last_item_index];
+ object->values[i] = object->values[last_item_index];
+ }
+ object->count -= 1;
+ return JSONSuccess;
+ }
+ }
+ return JSONFailure; /* No execution path should end here */
+}
+
+JSON_Status json_object_dotremove(JSON_Object *object, const char *name) {
+ const char *dot_pos = strchr(name, '.');
+ char *current_name = NULL;
+ JSON_Object *temp_obj = NULL;
+ if (dot_pos == NULL) {
+ return json_object_remove(object, name);
+ } else {
+ current_name = parson_strndup(name, dot_pos - name);
+ temp_obj = json_object_get_object(object, current_name);
+ if (temp_obj == NULL) {
+ parson_free(current_name);
+ return JSONFailure;
+ }
+ parson_free(current_name);
+ return json_object_dotremove(temp_obj, dot_pos + 1);
+ }
+}
+
+JSON_Status json_object_clear(JSON_Object *object) {
+ size_t i = 0;
+ if (object == NULL) {
+ return JSONFailure;
+ }
+ for (i = 0; i < json_object_get_count(object); i++) {
+ parson_free(object->names[i]);
+ json_value_free(object->values[i]);
+ }
+ object->count = 0;
+ return JSONSuccess;
+}
+
+JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) {
+ JSON_Value *temp_schema_value = NULL, *temp_value = NULL;
+ JSON_Array *schema_array = NULL, *value_array = NULL;
+ JSON_Object *schema_object = NULL, *value_object = NULL;
+ JSON_Value_Type schema_type = JSONError, value_type = JSONError;
+ const char *key = NULL;
+ size_t i = 0, count = 0;
+ if (schema == NULL || value == NULL)
+ return JSONFailure;
+ schema_type = json_value_get_type(schema);
+ value_type = json_value_get_type(value);
+ if (schema_type != value_type && schema_type != JSONNull) /* null represents all values */
+ return JSONFailure;
+ switch (schema_type) {
+ case JSONArray:
+ schema_array = json_value_get_array(schema);
+ value_array = json_value_get_array(value);
+ count = json_array_get_count(schema_array);
+ if (count == 0)
+ return JSONSuccess; /* Empty array allows all types */
+ /* Get first value from array, rest is ignored */
+ temp_schema_value = json_array_get_value(schema_array, 0);
+ for (i = 0; i < json_array_get_count(value_array); i++) {
+ temp_value = json_array_get_value(value_array, i);
+ if (json_validate(temp_schema_value, temp_value) == 0) {
+ return JSONFailure;
+ }
+ }
+ return JSONSuccess;
+ case JSONObject:
+ schema_object = json_value_get_object(schema);
+ value_object = json_value_get_object(value);
+ count = json_object_get_count(schema_object);
+ if (count == 0)
+ return JSONSuccess; /* Empty object allows all objects */
+ else if (json_object_get_count(value_object) < count)
+ return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */
+ for (i = 0; i < count; i++) {
+ key = json_object_get_name(schema_object, i);
+ temp_schema_value = json_object_get_value(schema_object, key);
+ temp_value = json_object_get_value(value_object, key);
+ if (temp_value == NULL)
+ return JSONFailure;
+ if (json_validate(temp_schema_value, temp_value) == JSONFailure)
+ return JSONFailure;
+ }
+ return JSONSuccess;
+ case JSONString: case JSONNumber: case JSONBoolean: case JSONNull:
+ return JSONSuccess; /* equality already tested before switch */
+ case JSONError: default:
+ return JSONFailure;
+ }
+}
+
+JSON_Status json_value_equals(const JSON_Value *a, const JSON_Value *b) {
+ JSON_Object *a_object = NULL, *b_object = NULL;
+ JSON_Array *a_array = NULL, *b_array = NULL;
+ const char *a_string = NULL, *b_string = NULL;
+ const char *key = NULL;
+ size_t a_count = 0, b_count = 0, i = 0;
+ JSON_Value_Type a_type, b_type;
+ a_type = json_value_get_type(a);
+ b_type = json_value_get_type(b);
+ if (a_type != b_type) {
+ return 0;
+ }
+ switch (a_type) {
+ case JSONArray:
+ a_array = json_value_get_array(a);
+ b_array = json_value_get_array(b);
+ a_count = json_array_get_count(a_array);
+ b_count = json_array_get_count(b_array);
+ if (a_count != b_count) {
+ return 0;
+ }
+ for (i = 0; i < a_count; i++) {
+ if (!json_value_equals(json_array_get_value(a_array, i),
+ json_array_get_value(b_array, i))) {
+ return 0;
+ }
+ }
+ return 1;
+ case JSONObject:
+ a_object = json_value_get_object(a);
+ b_object = json_value_get_object(b);
+ a_count = json_object_get_count(a_object);
+ b_count = json_object_get_count(b_object);
+ if (a_count != b_count) {
+ return 0;
+ }
+ for (i = 0; i < a_count; i++) {
+ key = json_object_get_name(a_object, i);
+ if (!json_value_equals(json_object_get_value(a_object, key),
+ json_object_get_value(b_object, key))) {
+ return 0;
+ }
+ }
+ return 1;
+ case JSONString:
+ a_string = json_value_get_string(a);
+ b_string = json_value_get_string(b);
+ return strcmp(a_string, b_string) == 0;
+ case JSONBoolean:
+ return json_value_get_boolean(a) == json_value_get_boolean(b);
+ case JSONNumber:
+ return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */
+ case JSONError:
+ return 1;
+ case JSONNull:
+ return 1;
+ default:
+ return 1;
+ }
+}
+
+JSON_Value_Type json_type(const JSON_Value *value) {
+ return json_value_get_type(value);
+}
+
+JSON_Object * json_object (const JSON_Value *value) {
+ return json_value_get_object(value);
+}
+
+JSON_Array * json_array (const JSON_Value *value) {
+ return json_value_get_array(value);
+}
+
+const char * json_string (const JSON_Value *value) {
+ return json_value_get_string(value);
+}
+
+double json_number (const JSON_Value *value) {
+ return json_value_get_number(value);
+}
+
+int json_boolean(const JSON_Value *value) {
+ return json_value_get_boolean(value);
+}
+
+void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) {
+ parson_malloc = malloc_fun;
+ parson_free = free_fun;
+}
diff --git a/util_pkt_logger/src/util_pkt_logger.c b/util_pkt_logger/src/util_pkt_logger.c
new file mode 100644
index 0000000..38a50f7
--- /dev/null
+++ b/util_pkt_logger/src/util_pkt_logger.c
@@ -0,0 +1,626 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech-Cycleo
+
+Description:
+ Configure LoRa concentrator and record received packets in a log file
+
+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 <string.h> /* memset */
+#include <signal.h> /* sigaction */
+#include <time.h> /* time clock_gettime strftime gmtime clock_nanosleep*/
+#include <unistd.h> /* getopt access */
+#include <stdlib.h> /* atoi */
+
+#include "parson.h"
+#include "loragw_hal.h"
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define MSG(args...) fprintf(stderr,"loragw_pkt_logger: " args) /* message that is destined to the user */
+
+/* -------------------------------------------------------------------------- */
+/* --- 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 */
+
+/* configuration variables needed by the application */
+uint64_t lgwm = 0; /* LoRa gateway MAC address */
+char lgwm_str[17];
+
+/* clock and log file management */
+time_t now_time;
+time_t log_start_time;
+FILE * log_file = NULL;
+char log_file_name[64];
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
+
+static void sig_handler(int sigio);
+
+int parse_SX1301_configuration(const char * conf_file);
+
+int parse_gateway_configuration(const char * conf_file);
+
+void open_log(void);
+
+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;
+ }
+}
+
+int parse_SX1301_configuration(const char * conf_file) {
+ int i;
+ const char conf_obj[] = "SX1301_conf";
+ char param_name[32]; /* used to generate variable parameter names */
+ const char *str; /* used to store string value from JSON object */
+ struct lgw_conf_board_s boardconf;
+ struct lgw_conf_rxrf_s rfconf;
+ struct lgw_conf_rxif_s ifconf;
+ JSON_Value *root_val;
+ JSON_Object *root = NULL;
+ JSON_Object *conf = NULL;
+ JSON_Value *val;
+ uint32_t sf, bw;
+
+ /* try to parse JSON */
+ root_val = json_parse_file_with_comments(conf_file);
+ root = json_value_get_object(root_val);
+ if (root == NULL) {
+ MSG("ERROR: %s id not a valid JSON file\n", conf_file);
+ exit(EXIT_FAILURE);
+ }
+ conf = json_object_get_object(root, conf_obj);
+ if (conf == NULL) {
+ MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj);
+ return -1;
+ } else {
+ MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 parameters\n", conf_file, conf_obj);
+ }
+
+ /* set board configuration */
+ memset(&boardconf, 0, sizeof boardconf); /* initialize configuration structure */
+ val = json_object_get_value(conf, "lorawan_public"); /* fetch value (if possible) */
+ if (json_value_get_type(val) == JSONBoolean) {
+ boardconf.lorawan_public = (bool)json_value_get_boolean(val);
+ } else {
+ MSG("WARNING: Data type for lorawan_public seems wrong, please check\n");
+ boardconf.lorawan_public = false;
+ }
+ val = json_object_get_value(conf, "clksrc"); /* fetch value (if possible) */
+ if (json_value_get_type(val) == JSONNumber) {
+ boardconf.clksrc = (uint8_t)json_value_get_number(val);
+ } else {
+ MSG("WARNING: Data type for clksrc seems wrong, please check\n");
+ boardconf.clksrc = 0;
+ }
+ MSG("INFO: lorawan_public %d, clksrc %d\n", boardconf.lorawan_public, boardconf.clksrc);
+ /* all parameters parsed, submitting configuration to the HAL */
+ if (lgw_board_setconf(boardconf) != LGW_HAL_SUCCESS) {
+ MSG("ERROR: Failed to configure board\n");
+ return -1;
+ }
+
+ /* set configuration for RF chains */
+ for (i = 0; i < LGW_RF_CHAIN_NB; ++i) {
+ memset(&rfconf, 0, sizeof(rfconf)); /* initialize configuration structure */
+ sprintf(param_name, "radio_%i", i); /* compose parameter path inside JSON structure */
+ val = json_object_get_value(conf, param_name); /* fetch value (if possible) */
+ if (json_value_get_type(val) != JSONObject) {
+ MSG("INFO: no configuration for radio %i\n", i);
+ continue;
+ }
+ /* there is an object to configure that radio, let's parse it */
+ sprintf(param_name, "radio_%i.enable", i);
+ val = json_object_dotget_value(conf, param_name);
+ if (json_value_get_type(val) == JSONBoolean) {
+ rfconf.enable = (bool)json_value_get_boolean(val);
+ } else {
+ rfconf.enable = false;
+ }
+ if (rfconf.enable == false) { /* radio disabled, nothing else to parse */
+ MSG("INFO: radio %i disabled\n", i);
+ } else { /* radio enabled, will parse the other parameters */
+ snprintf(param_name, sizeof param_name, "radio_%i.freq", i);
+ rfconf.freq_hz = (uint32_t)json_object_dotget_number(conf, param_name);
+ snprintf(param_name, sizeof param_name, "radio_%i.rssi_offset", i);
+ rfconf.rssi_offset = (float)json_object_dotget_number(conf, param_name);
+ snprintf(param_name, sizeof param_name, "radio_%i.type", i);
+ str = json_object_dotget_string(conf, param_name);
+ if (!strncmp(str, "SX1255", 6)) {
+ rfconf.type = LGW_RADIO_TYPE_SX1255;
+ } else if (!strncmp(str, "SX1257", 6)) {
+ rfconf.type = LGW_RADIO_TYPE_SX1257;
+ } else {
+ MSG("WARNING: invalid radio type: %s (should be SX1255 or SX1257)\n", str);
+ }
+ snprintf(param_name, sizeof param_name, "radio_%i.tx_enable", i);
+ val = json_object_dotget_value(conf, param_name);
+ if (json_value_get_type(val) == JSONBoolean) {
+ rfconf.tx_enable = (bool)json_value_get_boolean(val);
+ if (rfconf.tx_enable == true) {
+ /* tx notch filter frequency to be set */
+ snprintf(param_name, sizeof param_name, "radio_%i.tx_notch_freq", i);
+ rfconf.tx_notch_freq = (uint32_t)json_object_dotget_number(conf, param_name);
+ }
+ } else {
+ rfconf.tx_enable = false;
+ }
+ MSG("INFO: radio %i enabled (type %s), center frequency %u, RSSI offset %f, tx enabled %d, tx_notch_freq %u\n", i, str, rfconf.freq_hz, rfconf.rssi_offset, rfconf.tx_enable, rfconf.tx_notch_freq);
+ }
+ /* all parameters parsed, submitting configuration to the HAL */
+ if (lgw_rxrf_setconf(i, rfconf) != LGW_HAL_SUCCESS) {
+ MSG("ERROR: invalid configuration for radio %i\n", i);
+ return -1;
+ }
+ }
+
+ /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */
+ for (i = 0; i < LGW_MULTI_NB; ++i) {
+ memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */
+ sprintf(param_name, "chan_multiSF_%i", i); /* compose parameter path inside JSON structure */
+ val = json_object_get_value(conf, param_name); /* fetch value (if possible) */
+ if (json_value_get_type(val) != JSONObject) {
+ MSG("INFO: no configuration for LoRa multi-SF channel %i\n", i);
+ continue;
+ }
+ /* there is an object to configure that LoRa multi-SF channel, let's parse it */
+ sprintf(param_name, "chan_multiSF_%i.enable", i);
+ val = json_object_dotget_value(conf, param_name);
+ if (json_value_get_type(val) == JSONBoolean) {
+ ifconf.enable = (bool)json_value_get_boolean(val);
+ } else {
+ ifconf.enable = false;
+ }
+ if (ifconf.enable == false) { /* LoRa multi-SF channel disabled, nothing else to parse */
+ MSG("INFO: LoRa multi-SF channel %i disabled\n", i);
+ } else { /* LoRa multi-SF channel enabled, will parse the other parameters */
+ sprintf(param_name, "chan_multiSF_%i.radio", i);
+ ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, param_name);
+ sprintf(param_name, "chan_multiSF_%i.if", i);
+ ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, param_name);
+ // TODO: handle individual SF enabling and disabling (spread_factor)
+ MSG("INFO: LoRa multi-SF channel %i enabled, radio %i selected, IF %i Hz, 125 kHz bandwidth, SF 7 to 12\n", i, ifconf.rf_chain, ifconf.freq_hz);
+ }
+ /* all parameters parsed, submitting configuration to the HAL */
+ if (lgw_rxif_setconf(i, ifconf) != LGW_HAL_SUCCESS) {
+ MSG("ERROR: invalid configuration for Lora multi-SF channel %i\n", i);
+ return -1;
+ }
+ }
+
+ /* set configuration for LoRa standard channel */
+ memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */
+ val = json_object_get_value(conf, "chan_Lora_std"); /* fetch value (if possible) */
+ if (json_value_get_type(val) != JSONObject) {
+ MSG("INFO: no configuration for LoRa standard channel\n");
+ } else {
+ val = json_object_dotget_value(conf, "chan_Lora_std.enable");
+ if (json_value_get_type(val) == JSONBoolean) {
+ ifconf.enable = (bool)json_value_get_boolean(val);
+ } else {
+ ifconf.enable = false;
+ }
+ if (ifconf.enable == false) {
+ MSG("INFO: LoRa standard channel %i disabled\n", i);
+ } else {
+ ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.radio");
+ ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_Lora_std.if");
+ bw = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.bandwidth");
+ switch(bw) {
+ case 500000: ifconf.bandwidth = BW_500KHZ; break;
+ case 250000: ifconf.bandwidth = BW_250KHZ; break;
+ case 125000: ifconf.bandwidth = BW_125KHZ; break;
+ default: ifconf.bandwidth = BW_UNDEFINED;
+ }
+ sf = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.spread_factor");
+ switch(sf) {
+ case 7: ifconf.datarate = DR_LORA_SF7; break;
+ case 8: ifconf.datarate = DR_LORA_SF8; break;
+ case 9: ifconf.datarate = DR_LORA_SF9; break;
+ case 10: ifconf.datarate = DR_LORA_SF10; break;
+ case 11: ifconf.datarate = DR_LORA_SF11; break;
+ case 12: ifconf.datarate = DR_LORA_SF12; break;
+ default: ifconf.datarate = DR_UNDEFINED;
+ }
+ MSG("INFO: LoRa standard channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, SF %u\n", ifconf.rf_chain, ifconf.freq_hz, bw, sf);
+ }
+ if (lgw_rxif_setconf(8, ifconf) != LGW_HAL_SUCCESS) {
+ MSG("ERROR: invalid configuration for Lora standard channel\n");
+ return -1;
+ }
+ }
+
+ /* set configuration for FSK channel */
+ memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */
+ val = json_object_get_value(conf, "chan_FSK"); /* fetch value (if possible) */
+ if (json_value_get_type(val) != JSONObject) {
+ MSG("INFO: no configuration for FSK channel\n");
+ } else {
+ val = json_object_dotget_value(conf, "chan_FSK.enable");
+ if (json_value_get_type(val) == JSONBoolean) {
+ ifconf.enable = (bool)json_value_get_boolean(val);
+ } else {
+ ifconf.enable = false;
+ }
+ if (ifconf.enable == false) {
+ MSG("INFO: FSK channel %i disabled\n", i);
+ } else {
+ ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_FSK.radio");
+ ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_FSK.if");
+ bw = (uint32_t)json_object_dotget_number(conf, "chan_FSK.bandwidth");
+ if (bw <= 7800) ifconf.bandwidth = BW_7K8HZ;
+ else if (bw <= 15600) ifconf.bandwidth = BW_15K6HZ;
+ else if (bw <= 31200) ifconf.bandwidth = BW_31K2HZ;
+ else if (bw <= 62500) ifconf.bandwidth = BW_62K5HZ;
+ else if (bw <= 125000) ifconf.bandwidth = BW_125KHZ;
+ else if (bw <= 250000) ifconf.bandwidth = BW_250KHZ;
+ else if (bw <= 500000) ifconf.bandwidth = BW_500KHZ;
+ else ifconf.bandwidth = BW_UNDEFINED;
+ ifconf.datarate = (uint32_t)json_object_dotget_number(conf, "chan_FSK.datarate");
+ MSG("INFO: FSK channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, %u bps datarate\n", ifconf.rf_chain, ifconf.freq_hz, bw, ifconf.datarate);
+ }
+ if (lgw_rxif_setconf(9, ifconf) != LGW_HAL_SUCCESS) {
+ MSG("ERROR: invalid configuration for FSK channel\n");
+ return -1;
+ }
+ }
+ json_value_free(root_val);
+ return 0;
+}
+
+int parse_gateway_configuration(const char * conf_file) {
+ const char conf_obj[] = "gateway_conf";
+ JSON_Value *root_val;
+ JSON_Object *root = NULL;
+ JSON_Object *conf = NULL;
+ const char *str; /* pointer to sub-strings in the JSON data */
+ unsigned long long ull = 0;
+
+ /* try to parse JSON */
+ root_val = json_parse_file_with_comments(conf_file);
+ root = json_value_get_object(root_val);
+ if (root == NULL) {
+ MSG("ERROR: %s id not a valid JSON file\n", conf_file);
+ exit(EXIT_FAILURE);
+ }
+ conf = json_object_get_object(root, conf_obj);
+ if (conf == NULL) {
+ MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj);
+ return -1;
+ } else {
+ MSG("INFO: %s does contain a JSON object named %s, parsing gateway parameters\n", conf_file, conf_obj);
+ }
+
+ /* getting network parameters (only those necessary for the packet logger) */
+ str = json_object_get_string(conf, "gateway_ID");
+ if (str != NULL) {
+ sscanf(str, "%llx", &ull);
+ lgwm = ull;
+ MSG("INFO: gateway MAC address is configured to %016llX\n", ull);
+ }
+
+ json_value_free(root_val);
+ return 0;
+}
+
+void open_log(void) {
+ int i;
+ char iso_date[20];
+
+ strftime(iso_date,ARRAY_SIZE(iso_date),"%Y%m%dT%H%M%SZ",gmtime(&now_time)); /* format yyyymmddThhmmssZ */
+ log_start_time = now_time; /* keep track of when the log was started, for log rotation */
+
+ sprintf(log_file_name, "pktlog_%s_%s.csv", lgwm_str, iso_date);
+ log_file = fopen(log_file_name, "a"); /* create log file, append if file already exist */
+ if (log_file == NULL) {
+ MSG("ERROR: impossible to create log file %s\n", log_file_name);
+ exit(EXIT_FAILURE);
+ }
+
+ i = fprintf(log_file, "\"gateway ID\",\"node MAC\",\"UTC timestamp\",\"us count\",\"frequency\",\"RF chain\",\"RX chain\",\"status\",\"size\",\"modulation\",\"bandwidth\",\"datarate\",\"coderate\",\"RSSI\",\"SNR\",\"payload\"\n");
+ if (i < 0) {
+ MSG("ERROR: impossible to write to log file %s\n", log_file_name);
+ exit(EXIT_FAILURE);
+ }
+
+ MSG("INFO: Now writing to log file %s\n", log_file_name);
+ return;
+}
+
+/* describe command line options */
+void usage(void) {
+ printf("*** Library version information ***\n%s\n\n", lgw_version_info());
+ printf( "Available options:\n");
+ printf( " -h print this help\n");
+ printf( " -r <int> rotate log file every N seconds (-1 disable log rotation)\n");
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- MAIN FUNCTION -------------------------------------------------------- */
+
+int main(int argc, char **argv)
+{
+ int i, j; /* loop and temporary variables */
+ struct timespec sleep_time = {0, 3000000}; /* 3 ms */
+
+ /* clock and log rotation management */
+ int log_rotate_interval = 3600; /* by default, rotation every hour */
+ int time_check = 0; /* variable used to limit the number of calls to time() function */
+ unsigned long pkt_in_log = 0; /* count the number of packet written in each log file */
+
+ /* configuration file related */
+ const char global_conf_fname[] = "global_conf.json"; /* contain global (typ. network-wide) configuration */
+ const char local_conf_fname[] = "local_conf.json"; /* contain node specific configuration, overwrite global parameters for parameters that are defined in both */
+ const char debug_conf_fname[] = "debug_conf.json"; /* if present, all other configuration files are ignored */
+
+ /* allocate memory for packet fetching and processing */
+ struct lgw_pkt_rx_s rxpkt[16]; /* array containing up to 16 inbound packets metadata */
+ struct lgw_pkt_rx_s *p; /* pointer on a RX packet */
+ int nb_pkt;
+
+ /* local timestamp variables until we get accurate GPS time */
+ struct timespec fetch_time;
+ char fetch_timestamp[30];
+ struct tm * x;
+
+ /* parse command line options */
+ while ((i = getopt (argc, argv, "hr:")) != -1) {
+ switch (i) {
+ case 'h':
+ usage();
+ return EXIT_FAILURE;
+ break;
+
+ case 'r':
+ log_rotate_interval = atoi(optarg);
+ if ((log_rotate_interval == 0) || (log_rotate_interval < -1)) {
+ MSG( "ERROR: Invalid argument for -r option\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ default:
+ MSG("ERROR: argument parsing use -h option for help\n");
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* 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);
+
+ /* configuration files management */
+ if (access(debug_conf_fname, R_OK) == 0) {
+ /* if there is a debug conf, parse only the debug conf */
+ MSG("INFO: found debug configuration file %s, other configuration files will be ignored\n", debug_conf_fname);
+ parse_SX1301_configuration(debug_conf_fname);
+ parse_gateway_configuration(debug_conf_fname);
+ } else if (access(global_conf_fname, R_OK) == 0) {
+ /* if there is a global conf, parse it and then try to parse local conf */
+ MSG("INFO: found global configuration file %s, trying to parse it\n", global_conf_fname);
+ parse_SX1301_configuration(global_conf_fname);
+ parse_gateway_configuration(global_conf_fname);
+ if (access(local_conf_fname, R_OK) == 0) {
+ MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname);
+ parse_SX1301_configuration(local_conf_fname);
+ parse_gateway_configuration(local_conf_fname);
+ }
+ } else if (access(local_conf_fname, R_OK) == 0) {
+ /* if there is only a local conf, parse it and that's all */
+ MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname);
+ parse_SX1301_configuration(local_conf_fname);
+ parse_gateway_configuration(local_conf_fname);
+ } else {
+ MSG("ERROR: failed to find any configuration file named %s, %s or %s\n", global_conf_fname, local_conf_fname, debug_conf_fname);
+ return EXIT_FAILURE;
+ }
+
+ /* starting the concentrator */
+ i = lgw_start();
+ if (i == LGW_HAL_SUCCESS) {
+ MSG("INFO: concentrator started, packet can now be received\n");
+ } else {
+ MSG("ERROR: failed to start the concentrator\n");
+ return EXIT_FAILURE;
+ }
+
+ /* transform the MAC address into a string */
+ sprintf(lgwm_str, "%08X%08X", (uint32_t)(lgwm >> 32), (uint32_t)(lgwm & 0xFFFFFFFF));
+
+ /* opening log file and writing CSV header*/
+ time(&now_time);
+ open_log();
+
+ /* main loop */
+ while ((quit_sig != 1) && (exit_sig != 1)) {
+ /* fetch packets */
+ nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt);
+ if (nb_pkt == LGW_HAL_ERROR) {
+ MSG("ERROR: failed packet fetch, exiting\n");
+ return EXIT_FAILURE;
+ } else if (nb_pkt == 0) {
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, NULL); /* wait a short time if no packets */
+ } else {
+ /* local timestamp generation until we get accurate GPS time */
+ clock_gettime(CLOCK_REALTIME, &fetch_time);
+ x = gmtime(&(fetch_time.tv_sec));
+ sprintf(fetch_timestamp,"%04i-%02i-%02i %02i:%02i:%02i.%03liZ",(x->tm_year)+1900,(x->tm_mon)+1,x->tm_mday,x->tm_hour,x->tm_min,x->tm_sec,(fetch_time.tv_nsec)/1000000); /* ISO 8601 format */
+ }
+
+ /* log packets */
+ for (i=0; i < nb_pkt; ++i) {
+ p = &rxpkt[i];
+
+ /* writing gateway ID */
+ fprintf(log_file, "\"%08X%08X\",", (uint32_t)(lgwm >> 32), (uint32_t)(lgwm & 0xFFFFFFFF));
+
+ /* writing node MAC address */
+ fputs("\"\",", log_file); // TODO: need to parse payload
+
+ /* writing UTC timestamp*/
+ fprintf(log_file, "\"%s\",", fetch_timestamp);
+ // TODO: replace with GPS time when available
+
+ /* writing internal clock */
+ fprintf(log_file, "%10u,", p->count_us);
+
+ /* writing RX frequency */
+ fprintf(log_file, "%10u,", p->freq_hz);
+
+ /* writing RF chain */
+ fprintf(log_file, "%u,", p->rf_chain);
+
+ /* writing RX modem/IF chain */
+ fprintf(log_file, "%2d,", p->if_chain);
+
+ /* writing status */
+ switch(p->status) {
+ case STAT_CRC_OK: fputs("\"CRC_OK\" ,", log_file); break;
+ case STAT_CRC_BAD: fputs("\"CRC_BAD\",", log_file); break;
+ case STAT_NO_CRC: fputs("\"NO_CRC\" ,", log_file); break;
+ case STAT_UNDEFINED: fputs("\"UNDEF\" ,", log_file); break;
+ default: fputs("\"ERR\" ,", log_file);
+ }
+
+ /* writing payload size */
+ fprintf(log_file, "%3u,", p->size);
+
+ /* writing modulation */
+ switch(p->modulation) {
+ case MOD_LORA: fputs("\"LORA\",", log_file); break;
+ case MOD_FSK: fputs("\"FSK\" ,", log_file); break;
+ default: fputs("\"ERR\" ,", log_file);
+ }
+
+ /* writing bandwidth */
+ switch(p->bandwidth) {
+ case BW_500KHZ: fputs("500000,", log_file); break;
+ case BW_250KHZ: fputs("250000,", log_file); break;
+ case BW_125KHZ: fputs("125000,", log_file); break;
+ case BW_62K5HZ: fputs("62500 ,", log_file); break;
+ case BW_31K2HZ: fputs("31200 ,", log_file); break;
+ case BW_15K6HZ: fputs("15600 ,", log_file); break;
+ case BW_7K8HZ: fputs("7800 ,", log_file); break;
+ case BW_UNDEFINED: fputs("0 ,", log_file); break;
+ default: fputs("-1 ,", log_file);
+ }
+
+ /* writing datarate */
+ if (p->modulation == MOD_LORA) {
+ switch (p->datarate) {
+ case DR_LORA_SF7: fputs("\"SF7\" ,", log_file); break;
+ case DR_LORA_SF8: fputs("\"SF8\" ,", log_file); break;
+ case DR_LORA_SF9: fputs("\"SF9\" ,", log_file); break;
+ case DR_LORA_SF10: fputs("\"SF10\" ,", log_file); break;
+ case DR_LORA_SF11: fputs("\"SF11\" ,", log_file); break;
+ case DR_LORA_SF12: fputs("\"SF12\" ,", log_file); break;
+ default: fputs("\"ERR\" ,", log_file);
+ }
+ } else if (p->modulation == MOD_FSK) {
+ fprintf(log_file, "\"%6u\",", p->datarate);
+ } else {
+ fputs("\"ERR\" ,", log_file);
+ }
+
+ /* writing coderate */
+ switch (p->coderate) {
+ case CR_LORA_4_5: fputs("\"4/5\",", log_file); break;
+ case CR_LORA_4_6: fputs("\"2/3\",", log_file); break;
+ case CR_LORA_4_7: fputs("\"4/7\",", log_file); break;
+ case CR_LORA_4_8: fputs("\"1/2\",", log_file); break;
+ case CR_UNDEFINED: fputs("\"\" ,", log_file); break;
+ default: fputs("\"ERR\",", log_file);
+ }
+
+ /* writing packet RSSI */
+ fprintf(log_file, "%+.0f,", p->rssi);
+
+ /* writing packet average SNR */
+ fprintf(log_file, "%+5.1f,", p->snr);
+
+ /* writing hex-encoded payload (bundled in 32-bit words) */
+ fputs("\"", log_file);
+ for (j = 0; j < p->size; ++j) {
+ if ((j > 0) && (j%4 == 0)) fputs("-", log_file);
+ fprintf(log_file, "%02X", p->payload[j]);
+ }
+
+ /* end of log file line */
+ fputs("\"\n", log_file);
+ fflush(log_file);
+ ++pkt_in_log;
+ }
+
+ /* check time and rotate log file if necessary */
+ ++time_check;
+ if (time_check >= 8) {
+ time_check = 0;
+ time(&now_time);
+ if (difftime(now_time, log_start_time) > log_rotate_interval) {
+ fclose(log_file);
+ MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log);
+ pkt_in_log = 0;
+ open_log();
+ }
+ }
+ }
+
+ if (exit_sig == 1) {
+ /* clean up before leaving */
+ i = lgw_stop();
+ if (i == LGW_HAL_SUCCESS) {
+ MSG("INFO: concentrator stopped successfully\n");
+ } else {
+ MSG("WARNING: failed to stop concentrator successfully\n");
+ }
+ fclose(log_file);
+ MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log);
+ }
+
+ MSG("INFO: Exiting packet logger program\n");
+ return EXIT_SUCCESS;
+}
+
+/* --- EOF ------------------------------------------------------------------ */