summaryrefslogtreecommitdiff
path: root/util_tx_test
diff options
context:
space:
mode:
authorHarsh Sharma <92harshsharma@gmail.com>2018-06-13 13:26:38 -0500
committerHarsh Sharma <92harshsharma@gmail.com>2018-06-13 13:26:38 -0500
commit2a02a3a7dfda2679ebda86fa830023fe996a06c9 (patch)
tree96aee456765756759d04e59361ae07726b053e24 /util_tx_test
downloadpacket_forwarder_mtac_full-2a02a3a7dfda2679ebda86fa830023fe996a06c9.tar.gz
packet_forwarder_mtac_full-2a02a3a7dfda2679ebda86fa830023fe996a06c9.tar.bz2
packet_forwarder_mtac_full-2a02a3a7dfda2679ebda86fa830023fe996a06c9.zip
Initial commit
Diffstat (limited to 'util_tx_test')
-rw-r--r--util_tx_test/Makefile36
-rw-r--r--util_tx_test/inc/base64.h62
-rw-r--r--util_tx_test/readme.md77
-rw-r--r--util_tx_test/src/base64.c308
-rw-r--r--util_tx_test/src/util_tx_test.c502
5 files changed, 985 insertions, 0 deletions
diff --git a/util_tx_test/Makefile b/util_tx_test/Makefile
new file mode 100644
index 0000000..880b10d
--- /dev/null
+++ b/util_tx_test/Makefile
@@ -0,0 +1,36 @@
+### Application-specific constants
+
+APP_NAME := util_tx_test
+
+### Constant symbols
+
+CC := $(CROSS_COMPILE)gcc
+AR := $(CROSS_COMPILE)ar
+
+CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I.
+
+OBJDIR = obj
+INCLUDES = $(wildcard inc/*.h)
+
+### General build targets
+
+all: $(APP_NAME)
+
+clean:
+ rm -f $(OBJDIR)/*.o
+ rm -f $(APP_NAME)
+
+### Sub-modules compilation
+
+$(OBJDIR):
+ mkdir -p $(OBJDIR)
+
+$(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR)
+ $(CC) -c $(CFLAGS) $< -o $@
+
+### Main program assembly
+
+$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(OBJDIR)/base64.o
+ $(CC) $< $(OBJDIR)/base64.o -o $@
+
+### EOF
diff --git a/util_tx_test/inc/base64.h b/util_tx_test/inc/base64.h
new file mode 100644
index 0000000..e57eb47
--- /dev/null
+++ b/util_tx_test/inc/base64.h
@@ -0,0 +1,62 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech-Cycleo
+
+Description:
+ Base64 encoding & decoding library
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+Maintainer: Sylvain Miermont
+*/
+
+
+#ifndef _BASE64_H
+#define _BASE64_H
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <stdint.h> /* C99 types */
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+
+/**
+@brief Encode binary data in Base64 string (no padding)
+@param in pointer to a table of binary data
+@param size number of bytes to be encoded to base64
+@param out pointer to a string where the function will output encoded data
+@param max_len max length of the out string (including null char)
+@return >=0 length of the resulting string (w/o null char), -1 for error
+*/
+int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len);
+
+/**
+@brief Decode Base64 string to binary data (no padding)
+@param in string containing only base64 valid characters
+@param size number of characters to be decoded from base64 (w/o null char)
+@param out pointer to a data buffer where the function will output decoded data
+@param out_max_len usable size of the output data buffer
+@return >=0 number of bytes written to the data buffer, -1 for error
+*/
+int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len);
+
+/* === derivative functions === */
+
+/**
+@brief Encode binary data in Base64 string (with added padding)
+*/
+int bin_to_b64(const uint8_t * in, int size, char * out, int max_len);
+
+/**
+@brief Decode Base64 string to binary data (remove padding if necessary)
+*/
+int b64_to_bin(const char * in, int size, uint8_t * out, int max_len);
+
+#endif
+
+/* --- EOF ------------------------------------------------------------------ */
diff --git a/util_tx_test/readme.md b/util_tx_test/readme.md
new file mode 100644
index 0000000..f307292
--- /dev/null
+++ b/util_tx_test/readme.md
@@ -0,0 +1,77 @@
+ / _____) _ | |
+ ( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+ (______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech-Cycleo
+
+Utility: network packet sender
+===============================
+
+1. Introduction
+----------------
+
+The network packet sender is a simple helper program used to send packets
+through the gateway-to-server downlink route.
+
+The program start by waiting for a gateway to send it a PULL_DATA datagram.
+After that, it will send back to the gateway a specified amount of PULL_RESP
+datagrams, each containing a packet to be sent immediately and a variable
+payload.
+
+2. Dependencies
+----------------
+
+This program follows the v1.1 version of the gateway-to-server protocol.
+
+3. Usage
+---------
+
+The application runs until the specified number of packets have been sent.
+Press Ctrl+C to stop the application before that.
+
+Use the -h option to get help and details about available options.
+
+The packets are [9-n] bytes long, and have following payload content:
+```
++----------+---------------+---------------+---------------+---------------+---+---+---+---+---+---+---+---+
+| Id | PktCnt[31:24] | PktCnt[23:16] | PktCnt[15:8] | PktCnt[7:0] | P | E | R |FCS| 0 | 1 |...| n |
++----------+---------------+---------------+---------------+---------------+---+---+---+---+---+---+---+---+
+
+Id : User defined ID to differentiate sender at receiver side. (8 bits)
+PktCnt : Packet counter incremented at each transmission. (32 bits)
+‘P’, ‘E’, ‘R’ : ASCII values for characters 'P', 'E' and 'R'.
+FCS : Checksum: 8-bits sum of Id, PktCnt[31 :24] , PktCnt[23 :16] , PktCnt[15 :8] , PktCnt[7:0], ‘P’,’E’,’R’
+0,1, ..., n : Padding bytes up until user specified payload length.
+```
+
+4. License
+-----------
+
+Copyright (C) 2013, SEMTECH S.A.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of the Semtech corporation nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*EOF*
diff --git a/util_tx_test/src/base64.c b/util_tx_test/src/base64.c
new file mode 100644
index 0000000..8ba908e
--- /dev/null
+++ b/util_tx_test/src/base64.c
@@ -0,0 +1,308 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech-Cycleo
+
+Description:
+ Base64 encoding & decoding library
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+Maintainer: Sylvain Miermont
+*/
+
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "base64.h"
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE)
+
+//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */
+#define DEBUG(args...)
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */
+
+static char code_62 = '+'; /* RFC 1421 standard character for code 62 */
+static char code_63 = '/'; /* RFC 1421 standard character for code 63 */
+static char code_pad = '='; /* RFC 1421 padding character if padding */
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
+
+/**
+@brief Convert a code in the range 0-63 to an ASCII character
+*/
+char code_to_char(uint8_t x);
+
+/**
+@brief Convert an ASCII character to a code in the range 0-63
+*/
+uint8_t char_to_code(char x);
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
+
+char code_to_char(uint8_t x) {
+ if (x <= 25) {
+ return 'A' + x;
+ } else if ((x >= 26) && (x <= 51)) {
+ return 'a' + (x-26);
+ } else if ((x >= 52) && (x <= 61)) {
+ return '0' + (x-52);
+ } else if (x == 62) {
+ return code_62;
+ } else if (x == 63) {
+ return code_63;
+ } else {
+ DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x);
+ exit(EXIT_FAILURE);
+ } //TODO: improve error management
+}
+
+uint8_t char_to_code(char x) {
+ if ((x >= 'A') && (x <= 'Z')) {
+ return (uint8_t)x - (uint8_t)'A';
+ } else if ((x >= 'a') && (x <= 'z')) {
+ return (uint8_t)x - (uint8_t)'a' + 26;
+ } else if ((x >= '0') && (x <= '9')) {
+ return (uint8_t)x - (uint8_t)'0' + 52;
+ } else if (x == code_62) {
+ return 62;
+ } else if (x == code_63) {
+ return 63;
+ } else {
+ DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x);
+ exit(EXIT_FAILURE);
+ } //TODO: improve error management
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) {
+ int i;
+ int result_len; /* size of the result */
+ int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
+ int last_bytes; /* number of unsigned chars <3 in the last block */
+ int last_chars; /* number of characters <4 in the last block */
+ uint32_t b;
+
+ /* check input values */
+ if ((out == NULL) || (in == NULL)) {
+ DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n");
+ return -1;
+ }
+ if (size == 0) {
+ *out = 0; /* null string */
+ return 0;
+ }
+
+ /* calculate the number of base64 'blocks' */
+ full_blocks = size / 3;
+ last_bytes = size % 3;
+ switch (last_bytes) {
+ case 0: /* no byte left to encode */
+ last_chars = 0;
+ break;
+ case 1: /* 1 byte left to encode -> +2 chars */
+ last_chars = 2;
+ break;
+ case 2: /* 2 bytes left to encode -> +3 chars */
+ last_chars = 3;
+ break;
+ default:
+ CRIT("switch default that should not be possible");
+ }
+
+ /* check if output buffer is big enough */
+ result_len = (4*full_blocks) + last_chars;
+ if (max_len < (result_len + 1)) { /* 1 char added for string terminator */
+ DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n");
+ return -1;
+ }
+
+ /* process all the full blocks */
+ for (i=0; i < full_blocks; ++i) {
+ b = (0xFF & in[3*i] ) << 16;
+ b |= (0xFF & in[3*i + 1]) << 8;
+ b |= 0xFF & in[3*i + 2];
+ out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
+ out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
+ out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
+ out[4*i + 3] = code_to_char( b & 0x3F);
+ }
+
+ /* process the last 'partial' block and terminate string */
+ i = full_blocks;
+ if (last_chars == 0) {
+ out[4*i] = 0; /* null character to terminate string */
+ } else if (last_chars == 2) {
+ b = (0xFF & in[3*i] ) << 16;
+ out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
+ out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
+ out[4*i + 2] = 0; /* null character to terminate string */
+ } else if (last_chars == 3) {
+ b = (0xFF & in[3*i] ) << 16;
+ b |= (0xFF & in[3*i + 1]) << 8;
+ out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
+ out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
+ out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
+ out[4*i + 3] = 0; /* null character to terminate string */
+ }
+
+ return result_len;
+}
+
+int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) {
+ int i;
+ int result_len; /* size of the result */
+ int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
+ int last_chars; /* number of characters <4 in the last block */
+ int last_bytes; /* number of unsigned chars <3 in the last block */
+ uint32_t b;
+ ;
+
+ /* check input values */
+ if ((out == NULL) || (in == NULL)) {
+ DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
+ return -1;
+ }
+ if (size == 0) {
+ return 0;
+ }
+
+ /* calculate the number of base64 'blocks' */
+ full_blocks = size / 4;
+ last_chars = size % 4;
+ switch (last_chars) {
+ case 0: /* no char left to decode */
+ last_bytes = 0;
+ break;
+ case 1: /* only 1 char left is an error */
+ DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n");
+ return -1;
+ case 2: /* 2 chars left to decode -> +1 byte */
+ last_bytes = 1;
+ break;
+ case 3: /* 3 chars left to decode -> +2 bytes */
+ last_bytes = 2;
+ break;
+ default:
+ CRIT("switch default that should not be possible");
+ }
+
+ /* check if output buffer is big enough */
+ result_len = (3*full_blocks) + last_bytes;
+ if (max_len < result_len) {
+ DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n");
+ return -1;
+ }
+
+ /* process all the full blocks */
+ for (i=0; i < full_blocks; ++i) {
+ b = (0x3F & char_to_code(in[4*i] )) << 18;
+ b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
+ b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
+ b |= 0x3F & char_to_code(in[4*i + 3]);
+ out[3*i + 0] = (b >> 16) & 0xFF;
+ out[3*i + 1] = (b >> 8 ) & 0xFF;
+ out[3*i + 2] = b & 0xFF;
+ }
+
+ /* process the last 'partial' block */
+ i = full_blocks;
+ if (last_bytes == 1) {
+ b = (0x3F & char_to_code(in[4*i] )) << 18;
+ b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
+ out[3*i + 0] = (b >> 16) & 0xFF;
+ if (((b >> 12) & 0x0F) != 0) {
+ DEBUG("WARNING: last character contains unusable bits\n");
+ }
+ } else if (last_bytes == 2) {
+ b = (0x3F & char_to_code(in[4*i] )) << 18;
+ b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
+ b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
+ out[3*i + 0] = (b >> 16) & 0xFF;
+ out[3*i + 1] = (b >> 8 ) & 0xFF;
+ if (((b >> 6) & 0x03) != 0) {
+ DEBUG("WARNING: last character contains unusable bits\n");
+ }
+ }
+
+ return result_len;
+}
+
+int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) {
+ int ret;
+
+ ret = bin_to_b64_nopad(in, size, out, max_len);
+
+ if (ret == -1) {
+ return -1;
+ }
+ switch (ret%4) {
+ case 0: /* nothing to do */
+ return ret;
+ case 1:
+ DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n");
+ return -1;
+ case 2: /* 2 chars in last block, must add 2 padding char */
+ if (max_len >= (ret + 2 + 1)) {
+ out[ret] = code_pad;
+ out[ret+1] = code_pad;
+ out[ret+2] = 0;
+ return ret+2;
+ } else {
+ DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
+ return -1;
+ }
+ case 3: /* 3 chars in last block, must add 1 padding char */
+ if (max_len >= (ret + 1 + 1)) {
+ out[ret] = code_pad;
+ out[ret+1] = 0;
+ return ret+1;
+ } else {
+ DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
+ return -1;
+ }
+ default:
+ CRIT("switch default that should not be possible");
+ }
+}
+
+int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) {
+ if (in == NULL) {
+ DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
+ return -1;
+ }
+ if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */
+ if (in[size-2] == code_pad) { /* 2 padding char to ignore */
+ return b64_to_bin_nopad(in, size-2, out, max_len);
+ } else if (in[size-1] == code_pad) { /* 1 padding char to ignore */
+ return b64_to_bin_nopad(in, size-1, out, max_len);
+ } else { /* no padding to ignore */
+ return b64_to_bin_nopad(in, size, out, max_len);
+ }
+ } else { /* treat as unpadded Base64 */
+ return b64_to_bin_nopad(in, size, out, max_len);
+ }
+}
+
+
+/* --- EOF ------------------------------------------------------------------ */
diff --git a/util_tx_test/src/util_tx_test.c b/util_tx_test/src/util_tx_test.c
new file mode 100644
index 0000000..00b0c03
--- /dev/null
+++ b/util_tx_test/src/util_tx_test.c
@@ -0,0 +1,502 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ (C)2013 Semtech-Cycleo
+
+Description:
+ Ask a gateway to emit packets using GW <-> server protocol
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+Maintainer: Sylvain Miermont
+*/
+
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+/* fix an issue between POSIX and C99 */
+#if __STDC_VERSION__ >= 199901L
+ #define _XOPEN_SOURCE 600
+#else
+ #define _XOPEN_SOURCE 500
+#endif
+
+#include <stdint.h> /* C99 types */
+#include <stdbool.h> /* bool type */
+#include <stdio.h> /* printf fprintf sprintf fopen fputs */
+#include <unistd.h> /* getopt access usleep */
+
+#include <string.h> /* memset */
+#include <signal.h> /* sigaction */
+#include <stdlib.h> /* exit codes */
+#include <errno.h> /* error messages */
+
+#include <sys/socket.h> /* socket specific definitions */
+#include <netinet/in.h> /* INET constants and stuff */
+#include <arpa/inet.h> /* IP address conversion stuff */
+#include <netdb.h> /* gai_strerror */
+
+#include "base64.h"
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+#define PROTOCOL_VERSION 2
+
+#define PKT_PUSH_DATA 0
+#define PKT_PUSH_ACK 1
+#define PKT_PULL_DATA 2
+#define PKT_PULL_RESP 3
+#define PKT_PULL_ACK 4
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */
+
+/* signal handling variables */
+struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
+static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
+static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
+
+static void sig_handler(int sigio);
+
+void usage (void);
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
+
+static void sig_handler(int sigio) {
+ if (sigio == SIGQUIT) {
+ quit_sig = 1;;
+ } else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
+ exit_sig = 1;
+ }
+}
+
+/* describe command line options */
+void usage(void) {
+ MSG("Usage: util_tx_test {options}\n");
+ MSG("Available options:\n");
+ MSG(" -h print this help\n");
+ MSG(" -n <int or service> port number for gateway link\n");
+ MSG(" -f <float> target frequency in MHz\n");
+ MSG(" -m <str> Modulation type ['LORA, 'FSK']\n");
+ MSG(" -s <int> Spreading Factor [7:12]\n");
+ MSG(" -b <int> Modulation bandwidth in kHz [125,250,500]\n");
+ MSG(" -d <uint> FSK frequency deviation in kHz [1:250]\n");
+ MSG(" -r <float> FSK bitrate in kbps [0.5:250]\n");
+ MSG(" -p <int> RF power (dBm)\n");
+ MSG(" -z <uint> Payload size in bytes [9:255]\n");
+ MSG(" -t <int> pause between packets (ms)\n");
+ MSG(" -x <int> numbers of times the sequence is repeated\n");
+ MSG(" -v <uint> test ID, inserted in payload for PER test [0:255]\n");
+ MSG(" -i send packet using inverted modulation polarity \n");
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- MAIN FUNCTION -------------------------------------------------------- */
+
+int main(int argc, char **argv)
+{
+ int i, j, x;
+ unsigned int xu;
+ char arg_s[64];
+
+ /* application parameters */
+ char mod[64] = "LORA"; /* LoRa modulation by default */
+ float f_target = 866.0; /* target frequency */
+ int sf = 10; /* SF10 by default */
+ int bw = 125; /* 125kHz bandwidth by default */
+ int pow = 14; /* 14 dBm by default */
+ int delay = 1000; /* 1 second between packets by default */
+ int repeat = 1; /* sweep only once by default */
+ bool invert = false;
+ float br_kbps = 50; /* 50 kbps by default */
+ uint8_t fdev_khz = 25; /* 25 khz by default */
+
+ /* packet payload variables */
+ int payload_size = 9; /* minimum size for PER frame */
+ uint8_t payload_bin[255];
+ char payload_b64[341];
+ int payload_index;
+
+ /* PER payload variables */
+ uint8_t id = 0;
+
+ /* server socket creation */
+ int sock; /* socket file descriptor */
+ struct addrinfo hints;
+ struct addrinfo *result; /* store result of getaddrinfo */
+ struct addrinfo *q; /* pointer to move into *result data */
+ char serv_port[8] = "1680";
+ char host_name[64];
+ char port_name[64];
+
+ /* variables for receiving and sending packets */
+ struct sockaddr_storage dist_addr;
+ socklen_t addr_len = sizeof dist_addr;
+ uint8_t databuf[500];
+ int buff_index;
+ int byte_nb;
+
+ /* variables for gateway identification */
+ uint32_t raw_mac_h; /* Most Significant Nibble, network order */
+ uint32_t raw_mac_l; /* Least Significant Nibble, network order */
+ uint64_t gw_mac; /* MAC address of the client (gateway) */
+
+ /* prepare hints to open network sockets */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC; /* should handle IP v4 or v6 automatically */
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE; /* will assign local IP automatically */
+
+ /* parse command line options */
+ while ((i = getopt (argc, argv, "hn:f:m:s:b:d:r:p:z:t:x:v:i")) != -1) {
+ switch (i) {
+ case 'h':
+ usage();
+ return EXIT_FAILURE;
+ break;
+
+ case 'n': /* -n <int or service> port number for gateway link */
+ strncpy(serv_port, optarg, sizeof serv_port);
+ break;
+
+ case 'f': /* -f <float> target frequency in MHz */
+ i = sscanf(optarg, "%f", &f_target);
+ if ((i != 1) || (f_target < 30.0) || (f_target > 3000.0)) {
+ MSG("ERROR: invalid TX frequency\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'm': /* -m <str> Modulation type */
+ i = sscanf(optarg, "%s", arg_s);
+ if ((i != 1) || ((strcmp(arg_s, "LORA") != 0) && (strcmp(arg_s, "FSK")))) {
+ MSG("ERROR: invalid modulation type\n");
+ usage();
+ return EXIT_FAILURE;
+ } else {
+ sprintf(mod, "%s", arg_s);
+ }
+ break;
+
+ case 's': /* -s <int> Spreading Factor */
+ i = sscanf(optarg, "%i", &sf);
+ if ((i != 1) || (sf < 7) || (sf > 12)) {
+ MSG("ERROR: invalid spreading factor\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'b': /* -b <int> Modulation bandwidth in kHz */
+ i = sscanf(optarg, "%i", &bw);
+ if ((i != 1) || ((bw != 125) && (bw != 250) && (bw != 500))) {
+ MSG("ERROR: invalid LORA bandwidth\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'd': /* -d <uint> FSK frequency deviation */
+ i = sscanf(optarg, "%u", &xu);
+ if ((i != 1) || (xu < 1) || (xu > 250)) {
+ MSG("ERROR: invalid FSK frequency deviation\n");
+ usage();
+ return EXIT_FAILURE;
+ } else {
+ fdev_khz = (uint8_t)xu;
+ }
+ break;
+
+ case 'r': /* -q <float> FSK bitrate */
+ i = sscanf(optarg, "%f", &br_kbps);
+ if ((i != 1) || (br_kbps < 0.5) || (br_kbps > 250)) {
+ MSG("ERROR: invalid FSK bitrate\n");
+ usage();
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'p': /* -p <int> RF power */
+ i = sscanf(optarg, "%i", &pow);
+ if ((i != 1) || (pow < 0) || (pow > 30)) {
+ MSG("ERROR: invalid RF power\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'z': /* -z <uint> Payload size */
+ i = sscanf(optarg, "%i", &payload_size);
+ if ((i != 1) || (payload_size < 9) || (payload_size > 255)) {
+ MSG("ERROR: invalid payload size\n");
+ usage();
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 't': /* -t <int> pause between RF packets (ms) */
+ i = sscanf(optarg, "%i", &delay);
+ if ((i != 1) || (delay < 0)) {
+ MSG("ERROR: invalid time between RF packets\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'x': /* -x <int> numbers of times the sequence is repeated */
+ i = sscanf(optarg, "%u", &repeat);
+ if ((i != 1) || (repeat < 1)) {
+ MSG("ERROR: invalid number of repeats\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'v': /* -v <uint> test Id */
+ i = sscanf(optarg, "%u", &xu);
+ if ((i != 1) || ((xu < 1) && (xu > 255))) {
+ MSG("ERROR: invalid Id\n");
+ return EXIT_FAILURE;
+ } else {
+ id = (uint8_t)xu;
+ }
+ break;
+
+ case 'i': /* -i send packet using inverted modulation polarity */
+ invert = true;
+ break;
+
+ default:
+ MSG("ERROR: argument parsing failure, use -h option for help\n");
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* compose local address (auto-complete a structure for socket) */
+ i = getaddrinfo(NULL, serv_port, &hints, &result);
+ if (i != 0) {
+ MSG("ERROR: getaddrinfo returned %s\n", gai_strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ /* try to open socket and bind to it */
+ for (q=result; q!=NULL; q=q->ai_next) {
+ sock = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
+ if (sock == -1) {
+ continue; /* socket failed, try next field */
+ } else {
+ i = bind(sock, q->ai_addr, q->ai_addrlen);
+ if (i == -1) {
+ shutdown(sock, SHUT_RDWR);
+ continue; /* bind failed, try next field */
+ } else {
+ break; /* success, get out of loop */
+ }
+ }
+ }
+ if (q == NULL) {
+ MSG("ERROR: failed to open socket or to bind to it\n");
+ exit(EXIT_FAILURE);
+ }
+ freeaddrinfo(result);
+
+ /* configure signal handling */
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigact.sa_handler = sig_handler;
+ sigaction(SIGQUIT, &sigact, NULL);
+ sigaction(SIGINT, &sigact, NULL);
+ sigaction(SIGTERM, &sigact, NULL);
+
+ /* display setup summary */
+ if (strcmp(mod, "FSK") == 0) {
+ MSG("INFO: %i FSK pkts @%f MHz (FDev %u kHz, Bitrate %.2f kbps, %uB payload) %i dBm, %i ms between each\n", repeat, f_target, fdev_khz, br_kbps, payload_size, pow, delay);
+ } else {
+ MSG("INFO: %i LoRa pkts @%f MHz (BW %u kHz, SF%i, %uB payload) %i dBm, %i ms between each\n", repeat, f_target, bw, sf, payload_size, pow, delay);
+ }
+
+ /* wait to receive a PULL_DATA request packet */
+ MSG("INFO: waiting to receive a PULL_DATA request on port %s\n", serv_port);
+ while (1) {
+ byte_nb = recvfrom(sock, databuf, sizeof databuf, 0, (struct sockaddr *)&dist_addr, &addr_len);
+ if ((quit_sig == 1) || (exit_sig == 1)) {
+ exit(EXIT_SUCCESS);
+ } else if (byte_nb < 0) {
+ MSG("WARNING: recvfrom returned an error\n");
+ } else if ((byte_nb < 12) || (databuf[0] != PROTOCOL_VERSION) || (databuf[3] != PKT_PULL_DATA)) {
+ MSG("INFO: packet received, not PULL_DATA request\n");
+ } else {
+ break; /* success! */
+ }
+ }
+
+ /* retrieve gateway MAC from the request */
+ raw_mac_h = *((uint32_t *)(databuf+4));
+ raw_mac_l = *((uint32_t *)(databuf+8));
+ gw_mac = ((uint64_t)ntohl(raw_mac_h) << 32) + (uint64_t)ntohl(raw_mac_l);
+
+ /* display info about the sender */
+ i = getnameinfo((struct sockaddr *)&dist_addr, addr_len, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
+ if (i == -1) {
+ MSG("ERROR: getnameinfo returned %s \n", gai_strerror(i));
+ exit(EXIT_FAILURE);
+ }
+ MSG("INFO: PULL_DATA request received from gateway 0x%08X%08X (host %s, port %s)\n", (uint32_t)(gw_mac >> 32), (uint32_t)(gw_mac & 0xFFFFFFFF), host_name, port_name);
+
+ /* PKT_PULL_RESP datagrams header */
+ databuf[0] = PROTOCOL_VERSION;
+ databuf[1] = 0; /* no token */
+ databuf[2] = 0; /* no token */
+ databuf[3] = PKT_PULL_RESP;
+ buff_index = 4;
+
+ /* start of JSON structure */
+ memcpy((void *)(databuf + buff_index), (void *)"{\"txpk\":{\"imme\":true", 20);
+ buff_index += 20;
+
+ /* TX frequency */
+ i = snprintf((char *)(databuf + buff_index), 20, ",\"freq\":%.6f", f_target);
+ if ((i>=0) && (i < 20)) {
+ buff_index += i;
+ } else {
+ MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+
+ /* RF channel */
+ memcpy((void *)(databuf + buff_index), (void *)",\"rfch\":0", 9);
+ buff_index += 9;
+
+ /* TX power */
+ i = snprintf((char *)(databuf + buff_index), 12, ",\"powe\":%i", pow);
+ if ((i>=0) && (i < 12)) {
+ buff_index += i;
+ } else {
+ MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+
+ /* modulation type and parameters */
+ if (strcmp(mod, "FSK") == 0) {
+ i = snprintf((char *)(databuf + buff_index), 50, ",\"modu\":\"FSK\",\"datr\":%u,\"fdev\":%u", (unsigned int)(br_kbps*1e3), (unsigned int)(fdev_khz*1e3));
+ if ((i>=0) && (i < 50)) {
+ buff_index += i;
+ } else {
+ MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ i = snprintf((char *)(databuf + buff_index), 50, ",\"modu\":\"LORA\",\"datr\":\"SF%iBW%i\",\"codr\":\"4/6\"", sf, bw);
+ if ((i>=0) && (i < 50)) {
+ buff_index += i;
+ } else {
+ MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* signal polarity */
+ if (invert) {
+ memcpy((void *)(databuf + buff_index), (void *)",\"ipol\":true", 12);
+ buff_index += 12;
+ } else {
+ memcpy((void *)(databuf + buff_index), (void *)",\"ipol\":false", 13);
+ buff_index += 13;
+ }
+
+ /* Preamble size */
+ if (strcmp(mod, "LORA") == 0) {
+ memcpy((void *)(databuf + buff_index), (void *)",\"prea\":8", 9);
+ buff_index += 9;
+ }
+
+ /* payload size */
+ i = snprintf((char *)(databuf + buff_index), 12, ",\"size\":%i", payload_size);
+ if ((i>=0) && (i < 12)) {
+ buff_index += i;
+ } else {
+ MSG("ERROR: snprintf failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+
+ /* payload JSON object */
+ memcpy((void *)(databuf + buff_index), (void *)",\"data\":\"", 9);
+ buff_index += 9;
+ payload_index = buff_index; /* keep the value where the payload content start */
+
+ /* payload place-holder & end of JSON structure */
+ x = bin_to_b64(payload_bin, payload_size, payload_b64, sizeof payload_b64); /* dummy conversion to get exact size */
+ if (x >= 0) {
+ buff_index += x;
+ } else {
+ MSG("ERROR: bin_to_b64 failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Close JSON structure */
+ memcpy((void *)(databuf + buff_index), (void *)"\"}}", 3);
+ buff_index += 3; /* ends up being the total length of payload */
+
+ /* main loop */
+ for (i = 0; i < repeat; ++i) {
+ /* fill payload */
+ payload_bin[0] = id;
+ payload_bin[1] = (uint8_t)(i >> 24);
+ payload_bin[2] = (uint8_t)(i >> 16);
+ payload_bin[3] = (uint8_t)(i >> 8);
+ payload_bin[4] = (uint8_t)(i);
+ payload_bin[5] = 'P';
+ payload_bin[6] = 'E';
+ payload_bin[7] = 'R';
+ payload_bin[8] = (uint8_t)(payload_bin[0] + payload_bin[1] + payload_bin[2] + payload_bin[3] + payload_bin[4] + payload_bin[5] + payload_bin[6] + payload_bin[7]);
+ for (j = 0; j < (payload_size - 9); j++) {
+ payload_bin[9+j] = j;
+ }
+
+#if 0
+ for (j = 0; j < payload_size; j++ ) {
+ printf("0x%02X ", payload_bin[j]);
+ }
+ printf("\n");
+#endif
+
+ /* encode the payload in Base64 */
+ x = bin_to_b64(payload_bin, payload_size, payload_b64, sizeof payload_b64);
+ if (x >= 0) {
+ memcpy((void *)(databuf + payload_index), (void *)payload_b64, x);
+ } else {
+ MSG("ERROR: bin_to_b64 failed line %u\n", (__LINE__ - 4));
+ exit(EXIT_FAILURE);
+ }
+
+ /* send packet to the gateway */
+ byte_nb = sendto(sock, (void *)databuf, buff_index, 0, (struct sockaddr *)&dist_addr, addr_len);
+ if (byte_nb == -1) {
+ MSG("WARNING: sendto returned an error %s\n", strerror(errno));
+ } else {
+ MSG("INFO: packet #%i sent successfully\n", i);
+ }
+
+ /* wait inter-packet delay */
+ usleep(delay * 1000);
+
+ /* exit loop on user signals */
+ if ((quit_sig == 1) || (exit_sig == 1)) {
+ break;
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+/* --- EOF ------------------------------------------------------------------ */