diff options
author | Harsh Sharma <92harshsharma@gmail.com> | 2018-06-13 13:26:38 -0500 |
---|---|---|
committer | Harsh Sharma <92harshsharma@gmail.com> | 2018-06-13 13:26:38 -0500 |
commit | 2a02a3a7dfda2679ebda86fa830023fe996a06c9 (patch) | |
tree | 96aee456765756759d04e59361ae07726b053e24 /util_tx_test | |
download | packet_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/Makefile | 36 | ||||
-rw-r--r-- | util_tx_test/inc/base64.h | 62 | ||||
-rw-r--r-- | util_tx_test/readme.md | 77 | ||||
-rw-r--r-- | util_tx_test/src/base64.c | 308 | ||||
-rw-r--r-- | util_tx_test/src/util_tx_test.c | 502 |
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 ------------------------------------------------------------------ */ |