From 9dfc0590ba694e5e879d7c2ee4cf6264016c5859 Mon Sep 17 00:00:00 2001 From: Dave Akerman Date: Sun, 4 Sep 2016 10:50:39 +0000 Subject: [PATCH 1/2] V1.8.1 - Caption added M0RJX, Ignore CR in gateway.txt, auto trailing slash on config filepaths --- gateway.c | 39 +- keep/README.md | 321 +++++ keep/base64.c | 119 ++ keep/base64.h | 8 + keep/ftp.c | 95 ++ keep/ftp.h | 1 + keep/gateway-sample.txt | 58 + keep/gateway.c | 2458 +++++++++++++++++++++++++++++++++++++++ keep/gateway.h | 14 + keep/global.h | 103 ++ keep/habitat.c | 229 ++++ keep/habitat.h | 1 + keep/network.c | 120 ++ keep/network.h | 1 + keep/server.c | 137 +++ keep/server.h | 2 + keep/sha256.c | 170 +++ keep/sha256.h | 10 + keep/ssdv.c | 209 ++++ keep/ssdv.h | 2 + keep/ssdv_resend.py | 103 ++ keep/urlencode.c | 71 ++ keep/urlencode.h | 2 + 23 files changed, 4258 insertions(+), 15 deletions(-) create mode 100644 keep/README.md create mode 100644 keep/base64.c create mode 100644 keep/base64.h create mode 100644 keep/ftp.c create mode 100644 keep/ftp.h create mode 100644 keep/gateway-sample.txt create mode 100644 keep/gateway.c create mode 100644 keep/gateway.h create mode 100644 keep/global.h create mode 100644 keep/habitat.c create mode 100644 keep/habitat.h create mode 100644 keep/network.c create mode 100644 keep/network.h create mode 100644 keep/server.c create mode 100644 keep/server.h create mode 100644 keep/sha256.c create mode 100644 keep/sha256.h create mode 100644 keep/ssdv.c create mode 100644 keep/ssdv.h create mode 100644 keep/ssdv_resend.py create mode 100644 keep/urlencode.c create mode 100644 keep/urlencode.h diff --git a/gateway.c b/gateway.c index e279686..b20fbab 100644 --- a/gateway.c +++ b/gateway.c @@ -33,7 +33,7 @@ #include "server.h" #include "gateway.h" -#define VERSION "V1.8.0" +#define VERSION "V1.8.1" bool run = TRUE; // RFM98 @@ -1072,12 +1072,10 @@ TestMessageForSMSAcknowledgement( int Channel, char *Message ) FileNumber = atoi( value1 ); if ( FileNumber > 0 ) { - sprintf( OldFileName, "%s%d.sms", Config.SMSFolder, - FileNumber ); + sprintf( OldFileName, "%s/%d.sms", Config.SMSFolder, FileNumber ); if ( FileExists( OldFileName ) ) { - sprintf( NewFileName, "%s%d.ack", Config.SMSFolder, - FileNumber ); + sprintf( NewFileName, "%s/%d.ack", Config.SMSFolder, FileNumber ); if ( FileExists( NewFileName ) ) { remove( NewFileName ); @@ -1344,9 +1342,7 @@ receiveMessage( int Channel, char *message ) return Bytes; } -void -ReadString( FILE * fp, char *keyword, char *Result, int Length, - int NeedValue ) +void ReadString( FILE * fp, char *keyword, char *Result, int Length, int NeedValue ) { char line[100], *token, *value; @@ -1355,6 +1351,8 @@ ReadString( FILE * fp, char *keyword, char *Result, int Length, while ( fgets( line, sizeof( line ), fp ) != NULL ) { + line[strcspn(line, "\r")] = '\0'; // Get rid of CR if there is one + token = strtok( line, "=" ); if ( strcasecmp( keyword, token ) == 0 ) { @@ -1366,8 +1364,7 @@ ReadString( FILE * fp, char *keyword, char *Result, int Length, if ( NeedValue ) { - LogMessage( "Missing value for '%s' in configuration file\n", - keyword ); + LogMessage( "Missing value for '%s' in configuration file\n", keyword ); exit( 1 ); } } @@ -1418,6 +1415,19 @@ ReadBoolean( FILE * fp, char *keyword, int NeedValue, int *Result ) return *Temp; } +void RemoveTrailingSlash(char *Value) +{ + int Len; + + if ((Len = strlen(Value)) > 0) + { + if ((Value[Len-1] == '/') || (Value[Len-1] == '\\')) + { + Value[Len-1] = '\0'; + } + } +} + void LoadConfigFile(void) { FILE *fp; @@ -1486,8 +1496,7 @@ void LoadConfigFile(void) Config.ServerPort = ReadInteger( fp, "ServerPort", 0, -1 ); // SSDV Settings - ReadString( fp, "jpgFolder", Config.SSDVJpegFolder, - sizeof( Config.SSDVJpegFolder ), 0 ); + ReadString( fp, "jpgFolder", Config.SSDVJpegFolder, sizeof( Config.SSDVJpegFolder ), 0 ); if ( Config.SSDVJpegFolder[0] ) { // Create SSDV Folders @@ -1521,8 +1530,8 @@ void LoadConfigFile(void) ReadString(fp, "SMSFolder", Config.SMSFolder, sizeof( Config.SMSFolder ), 0); if ( Config.SMSFolder[0] ) { - LogMessage( "Folder %s will be scanned for messages to upload\n", - Config.SMSFolder ); + RemoveTrailingSlash(Config.SMSFolder); + LogMessage( "Folder %s will be scanned for messages to upload\n", Config.SMSFolder ); } for ( Channel = 0; Channel <= 1; Channel++ ) @@ -1769,7 +1778,7 @@ InitDisplay( void ) char title[80]; - sprintf( title, "LoRa Habitat and SSDV Gateway by M0RPI - " VERSION); + sprintf( title, "LoRa Habitat and SSDV Gateway by M0RPI, M0RJX - " VERSION); // Title bar mvaddstr( 0, ( 80 - strlen( title ) ) / 2, title ); diff --git a/keep/README.md b/keep/README.md new file mode 100644 index 0000000..790b2e2 --- /dev/null +++ b/keep/README.md @@ -0,0 +1,321 @@ +Part of the LoRa Balloon Tracking System + +Runs on a Raspberry Pi with 1 or 2 RFM98HW modules attached to the SPI port. +Also works with other compatible HopeRF and Semtec LoRa devices. + +Connections +=========== + +If you're making your own board for the Pi, connect the LoRa module(s) like so: + + LORA PI + ---- -- + 3.3V 3.3V Power + GND Ground + MOSI MOSI (pin 19) + MISO MISO (pin 21) + NSS CE0 (pin 24) (CE1 (pin 26) for 2nd module) + SCK SLCK + DIO0 Wiring Pi 31 (Pin 28) (Wiring Pi 6 (pin 22) for 2nd module) + DIO5 Wiring Pi 26 (Pin 32) (Wiring Pi 5 (pin 18) for 2nd module) + + +Installation +============ + +Enable SPI in raspi-config. + +Install WiringPi: + + 1. cd ~ + 2. git clone git://git.drogon.net/wiringPi + 3. cd wiringPi + 4. ./build + +Install the curl library: + + sudo apt-get install libcurl4-openssl-dev + +Install the ncurses library + + sudo apt-get install libncurses5-dev + +Install the LoRa gateway + + 1. cd ~ + 2. git clone https://github.com/PiInTheSky/lora-gateway.git + 3. cd lora-gateway + 4. make + 5. cp gateway-sample.txt gateway.txt + + + +Configuration +============= + +The configuration is in the file gateway.txt. Example: + + tracker=M0RPI + EnableHabitat=N + EnableSSDV=Y + LogTelemetry=Y + LogPackets=Y + CallingTimeout=60 + JPGFolder=ssdv + CallingTimeout=60 + ServerPort=6004 + Latitude=51.95023 + Longitude=-2.5445 + Antenna=868MHz Yagi + + frequency_0=434.347 + mode_0=1 + DIO0_0=31 + DIO5_0=26 + AFC_0=N + + frequency_1=434.475 + mode_1=5 + DIO0_1=6 + DIO5_1=5 + AFC_1=Y + +The global options are: + + tracker=. This is whatever callsign you want to appear as on the tracking map and/or SSDV page. + + EnableHabitat=. Enables uploading of telemetry packets to Habitat. + + EnableSSDV=. Enables uploading of SSDV image packets to the SSDV server. + + JPGFolder=. Tells the gateway where to save local JPEG files built from incoming SSDV packets. + + LogTelemetry=. Enables logging of telemetry packets (ASCII only at present) to telemetry.txt. + + LogPackets=. Enables logging of packet information (SNR, RSSI, length, type) to packets.txt. + + SMSFolder=. Tells the gateway to check for incoming SMS messages or tweets that should be sent to the tracker via the uplink. + + CallingTimeout=. Sets a timeout for returning to calling mode after a period with no received packets. + + ServerPort=. Opens a server socket which can have 1 client connected. Sends JSON telemetry and status information to that client. + + Latitude= + Longitude=. These let you tell the gateway your position, for uploading to habitat, so your listener icon appears on the map in the correct position. + Antenna=. Lets you specify your antenna make/model or type. This appears on the map if your listener icon is clicked on. + + NetworkLED= + InternetLED= + ActivityLED_0= + ActivityLED_1=. These are used for LED status indicators. Useful for packaged gateways that don't have a monitor attached. + + +and the channel-specific options are: + + frequency_=. This sets the frequency for LoRa module (0 for first, 1 for second). e.g. frequency_0=434.450 + + AFC_=. Enables or disables automatic frequency control (retunes by the frequency error of last received packet). + + mode_=. Sets the "mode" for the selected LoRa module. This offers a simple way of setting the various + LoRa parameters (SF etc.) in one go. The modes are: + + 0 = (normal for telemetry) Explicit mode, Error coding 4:8, Bandwidth 20.8kHz, SF 11, Low data rate optimize on + 1 = (normal for SSDV) Implicit mode, Error coding 4:5, Bandwidth 20.8kHz, SF 6, Low data rate optimize off + 2 = (normal for repeater) Explicit mode, Error coding 4:8, Bandwidth 62.5kHz, SF 8, Low data rate optimize off + 3 = (normal for fast SSDV) Explicit mode, Error coding 4:6, Bandwidth 250kHz, SF 7, Low data rate optimize off + 4 = Test mode not for normal use. + 5 = (normal for calling mode) Explicit mode, Error coding 4:8, Bandwidth 41.7kHz, SF 11, Low data rate optimize off + + SF_= e.g. SF_0=7 + + Bandwidth_=. e.g. Bandwidth_0=41K7. Options are 7K8, 10K4, 15K6, 20K8, 31K25, 41K7, 62K5, 125K, 250K, 500K + + Implicit_=. e.g. Implicit_0=Y + + Coding_=. e.g. Coding_0=5 (4:5) + + lowopt_=. Enables or disables low data rate optimization. + + power_=. This is the power setting used for uplinks. Refer to the LoRa manual for details on setting this. ** Only set values that are legal in your location (for EU see IR2030) ** + + UplinkTime_0=. When to send any uplink messages, measured as seconds into each cycle. + + UplinkCycle_0=. Cycle time for uplinks. First cycle starts at 00:00:00. So for uplink time=2 and cycle=30, any transmissions will start at 2 and 32 seconds after each minute. + +Lines are commented out with "#" at the start. + +If the frequency_n line is commented out, then that channel is disabled. + + +Uplinks +======= + +The gateway can uplink messages to the tracker. Currently this is restricted to time-based uplink slots using "UplinkTime" and "UplinkCycle". + +The code uses Linux system time, so the gateway should ideally be using a GPS receiver the GPSD daemon. NTP may prove sufficient however. + +For uplinks to work, both UplinkTime and UplinkCycle have to be set for the appropriate channel. + +There are currently two types of uplink supported: + + - Uplink of messages from the "SMSFolder" folder. For this to work, "SMSFolder" has to be defined and present. The gateway will then check for "*.sms" files in that folder. + - Uplink of SSD packet re-send requests. The gateway looks for an "uplink.txt" file in the gateway folder. The file is created by an external Python script (supplied) which interrogates the SSDV server. + + +Calling Mode +============ + +It is possible for trackers to send out messages on a special "calling channel" as well as telemetry on their main frequency. The calling channel messages state the main frequency and LoRa modes. + +This allows for gateways tp be normally left on the calling channel, so they then switch to each tracker as it comes within range. + +There's nothing special about "calling mode" except that after a period (CallingTimeout seconds) of time without packets, the gateway returns to its default settings. + +There is no current standard claling channel. + + +Use +=== + +Run with: + + sudo ./gateway + + +Display +======= + +The display has a title bar at the top, scrolling log at the bottom, and 2 channel panels in the middle. Each panel shows something like: + + Channel 0 869.8500MHz + Explicit, 250k, SF7, EC4:6 + Telemetry 74 bytes + 51.95028, -2.54443, 00138 + Habitat SSDV 0000 + 0s since last packet + Telem Packets = 37 + Image Packets = 0 + Bad CRC = 0 Bad Type = 0 + Packet SNR = 10, RSSI = -67 + Freq. Error = 1.0kHz + Current RSSI = -64 + +The "Habitat" text appears during uploads to habitat. Normally it will flash up then disappear quickly; if it stays on (not flickering) then the upload is slow. + +The "SSDV 0000" text shows the current state of the SSDV upload buffers. There are 4 upload threads and each can handle up to 16 (0-9-A-F) packets in its queue. +Normally, even with fast SSDV, uplinks should happen quickly enough for there to be no more than 1 or 2 active threads each with 1 packet being uploaded. + + +Interactive Features +==================== + +The following key presses are available. Where appropriate unshifted keys affect Channel 0 and shifted keys affect Channel 1. +Many thanks to David Brooke for coding this feature and the AFC. + + q quit + + a increase frequency by 100kHz + z decrease frequency by 100kHz + s increase frequency by 10kHz + x decrease frequency by 10kHz + d increase frequency by 1kHz + c decrease frequency by 1kHz + + f toggle AFC + +Change History +============== + +03/09/2016 - V1.8 +----------------- + + Add configuration of uplink frequency, mode and power + LoRa modes now in array so easier to add new ones + Added LoRa mode for uplink + Re-instated logging to telemetry.txt + Fixed pipe errors which happened if packets arrived during program exit + Merged in changes to JSON format + Sends data both LoRa channels in JSON + Config disables CE0 by default (most cards have CE1 only) + Fixed typos in gateway-sample.txt + Accept new SSDV types + + +25/08/2016 - V1.7 +----------------- + + Robert Harrison (RJH) has made numerous changes. + + Highlights include :- + + Changed makefile to include -Wall and fixed all warnings generated + Added pipes for Inter-Process Communication + Moved none thread safe curl funtions from threads and into main() + Added reporting of curl errors to habitat and ssdv threads + Changed color to green but requires 256 color support in your terminal + + For putty users please set your terminal as shown + +![Alt text](http://i.imgur.com/B81bvEQ.png "Putty config") + + when you are connected to your pi + + # echo $TERM # should show something with 256 in + + or + + # tpu colors # Should show 256 + + +27/06/2016 - V1.6 +----------------- + + Single SSDV upload thread using new API to upload multiple packets at once + Fixed 100% CPU (SSDV thread not sleeping) + + +23/05/2016 - V1.5 +----------------- + + Better status screen + +13/05/2016 +---------- + + Bug fux to local conversion of large SSDV images + Added packet logging + + +04/04/2016 +---------- + + SSDV 8 buffers + JSON feed instead of old "transition" method + + +19/02/2016 +---------- + + Fixed listener_information JSON + Added antenna setting to gateway.txt + + +16/02/2016 +---------- + + JSON telemetry feed via a server port + JPEGFolder setting + Uplink of text messages to tracker (e.g. accepted from Twitter by an external script) + Uplink of SSDV re-send requests. These requests are built by an external Python script. + Separate thread for uploading latest telemetry to habitat + 4 separate threads for uploading SSDV packets to the SSDV server + Slightly different display layout, with extra information + + +07/10/2015 +---------- + + fsphil: Tidied up compiler warnings, makefile, file permissions + + + diff --git a/keep/base64.c b/keep/base64.c new file mode 100644 index 0000000..5383f30 --- /dev/null +++ b/keep/base64.c @@ -0,0 +1,119 @@ +#include +#include + + +static char encoding_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static char *decoding_table = NULL; +static int mod_table[] = { 0, 2, 1 }; + + +char * +base64_encode( const char *data, + size_t input_length, + size_t * output_length, char *encoded_data ) +{ + + int i, j; + + *output_length = 4 * ( ( input_length + 2 ) / 3 ); + + // char *encoded_data = malloc(*output_length); + // if (encoded_data == NULL) return NULL; + + for ( i = 0, j = 0; i < input_length; ) + { + uint32_t octet_a = i < input_length ? ( unsigned char ) data[i++] : 0; + uint32_t octet_b = i < input_length ? ( unsigned char ) data[i++] : 0; + uint32_t octet_c = i < input_length ? ( unsigned char ) data[i++] : 0; + + uint32_t triple = ( octet_a << 0x10 ) + ( octet_b << 0x08 ) + octet_c; + + encoded_data[j++] = encoding_table[( triple >> 3 * 6 ) & 0x3F]; + encoded_data[j++] = encoding_table[( triple >> 2 * 6 ) & 0x3F]; + encoded_data[j++] = encoding_table[( triple >> 1 * 6 ) & 0x3F]; + encoded_data[j++] = encoding_table[( triple >> 0 * 6 ) & 0x3F]; + } + + for ( i = 0; i < mod_table[input_length % 3]; i++ ) + encoded_data[*output_length - 1 - i] = '='; + + return encoded_data; +} + + +void +build_decoding_table( ) +{ + + int i; + + decoding_table = malloc( 256 ); + + for ( i = 0; i < 64; i++ ) + decoding_table[( unsigned char ) encoding_table[i]] = i; +} + +char * +base64_decode( const char *data, size_t input_length, size_t * output_length ) +{ + int i, j; + + if ( decoding_table == NULL ) + build_decoding_table( ); + + if ( input_length % 4 != 0 ) + return NULL; + + *output_length = input_length / 4 * 3; + if ( data[input_length - 1] == '=' ) + ( *output_length )--; + if ( data[input_length - 2] == '=' ) + ( *output_length )--; + + char *decoded_data = malloc( *output_length ); + if ( decoded_data == NULL ) + return NULL; + + for ( i = 0, j = 0; i < input_length; ) + { + + uint32_t sextet_a = + data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; + uint32_t sextet_b = + data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; + uint32_t sextet_c = + data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; + uint32_t sextet_d = + data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; + + uint32_t triple = ( sextet_a << 3 * 6 ) + + ( sextet_b << 2 * 6 ) + + ( sextet_c << 1 * 6 ) + ( sextet_d << 0 * 6 ); + + if ( j < *output_length ) + decoded_data[j++] = ( triple >> 2 * 8 ) & 0xFF; + if ( j < *output_length ) + decoded_data[j++] = ( triple >> 1 * 8 ) & 0xFF; + if ( j < *output_length ) + decoded_data[j++] = ( triple >> 0 * 8 ) & 0xFF; + } + + return decoded_data; +} + + + +void +base64_cleanup( ) +{ + free( decoding_table ); +} diff --git a/keep/base64.h b/keep/base64.h new file mode 100644 index 0000000..bd1bd2a --- /dev/null +++ b/keep/base64.h @@ -0,0 +1,8 @@ +#include + +void build_decoding_table( ); +char *base64_encode( const char *data, size_t input_length, + size_t * output_length, char *encoded_data ); +char *base64_decode( const char *data, size_t input_length, + size_t * output_length ); +void base64_cleanup( ); diff --git a/keep/ftp.c b/keep/ftp.c new file mode 100644 index 0000000..07627a0 --- /dev/null +++ b/keep/ftp.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include // Standard input/output definitions +#include // String function definitions +#include // UNIX standard function definitions +#include // File control definitions +#include // Error number definitions +#include // POSIX terminal control definitions +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "global.h" + +void +ConvertFile( char *FileName ) +{ + char TargetFile[100], CommandLine[200], *ptr; + + strcpy( TargetFile, FileName ); + ptr = strchr( TargetFile, '.' ); + if ( ptr && Config.SSDVJpegFolder[0] ) + { + *ptr = '\0'; + strcat( TargetFile, ".JPG" ); + + // Now convert the file + // LogMessage("Converting %s to %s\n", FileName, TargetFile); + + sprintf( CommandLine, + "ssdv -d /tmp/%s %s/%s 2> /dev/null > /dev/null", FileName, + Config.SSDVJpegFolder, TargetFile ); + // LogMessage("COMMAND %s\n", CommandLine); + system( CommandLine ); + + if ( Config.ftpServer[0] && Config.ftpUser[0] + && Config.ftpPassword[0] ) + { + // Upload to ftp server + sprintf( CommandLine, + "curl -T %s %s -Q \"TYPE I\" --user %s:%s 2> /dev/null > /dev/null", + TargetFile, Config.ftpServer, Config.ftpUser, + Config.ftpPassword ); + system( CommandLine ); + } + } +} + + +void * +FTPLoop( void *some_void_ptr ) +{ + while ( 1 ) + { + DIR *dp; + struct dirent *ep; + struct stat st; + char *SSDVFolder; + char FileName[100], TempName[100]; + + SSDVFolder = "/tmp"; + + dp = opendir( SSDVFolder ); + if ( dp != NULL ) + { + while ( ( ep = readdir( dp ) ) ) + { + if ( strstr( ep->d_name, ".bin" ) != NULL ) + { + sprintf( FileName, "%s/%s", SSDVFolder, ep->d_name ); + stat( FileName, &st ); + // LogMessage("Age of '%s' is %ld seconds\n", FileName, time(0) - st.st_mtime); + if ( ( time( 0 ) - st.st_mtime ) < 20 ) + { + ConvertFile( ep->d_name ); + } + else if ( ( time( 0 ) - st.st_mtime ) > 120 ) + { + sprintf( TempName, "%s/%s", SSDVFolder, ep->d_name ); + // LogMessage("Removing %s\n", TempName); + remove( TempName ); + } + } + } + } + ( void ) closedir( dp ); + + sleep( 5 ); + } +} diff --git a/keep/ftp.h b/keep/ftp.h new file mode 100644 index 0000000..b9cc578 --- /dev/null +++ b/keep/ftp.h @@ -0,0 +1 @@ +void *FTPLoop( void *some_void_ptr ); diff --git a/keep/gateway-sample.txt b/keep/gateway-sample.txt new file mode 100644 index 0000000..0c30b8f --- /dev/null +++ b/keep/gateway-sample.txt @@ -0,0 +1,58 @@ + +##### Your details ##### + +tracker=YOUR_CALLSIGN +Latitude=0.0 +Longitude=0.0 +Antenna=Watson W-50 + + +##### Config Options ##### + +EnableHabitat=Y +EnableSSDV=Y +JPGFolder=ssdv +LogTelemetry=Y +LogPackets=Y +CallingTimeout=60 +ServerPort=6004 +#SMSFolder=./ +EnableDev=N + +NetworkLED=22 +InternetLED=23 +ActivityLED_0=21 +ActivityLED_1=29 + +##### Config CE0 ##### + +#frequency_0=434.250 +#mode_0=1 +#AFC_0=Y +#bandwidth_0=125K +#implicit_0=0 +#coding_0=5 +#sf_0=8 +#lowopt_0=0 +#power_0=255 +#DIO0_0=31 +#DIO5_0=26 +#UplinkTime_0=2 +#UplinkCycle_0=60 + + +##### Config CE1 ##### + +frequency_1=434.500 +mode_1=1 +AFC_1=Y +#bandwidth_1=125K +#implicit_1=0 +#coding_1=5 +#sf_1=8 +#lowopt_1=0 +#power_1=255 +#DIO0_1=6 +#DIO5_1=5 +#UplinkTime_1=5 +#UplinkCycle_1=60 diff --git a/keep/gateway.c b/keep/gateway.c new file mode 100644 index 0000000..b20fbab --- /dev/null +++ b/keep/gateway.c @@ -0,0 +1,2458 @@ +#include +#define __USE_XOPEN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "urlencode.h" +#include "base64.h" +#include "ssdv.h" +#include "ftp.h" +#include "habitat.h" +#include "network.h" +#include "global.h" +#include "server.h" +#include "gateway.h" + +#define VERSION "V1.8.1" +bool run = TRUE; + +// RFM98 +uint8_t currentMode = 0x81; + +#define REG_FIFO 0x00 +#define REG_FIFO_ADDR_PTR 0x0D +#define REG_FIFO_TX_BASE_AD 0x0E +#define REG_FIFO_RX_BASE_AD 0x0F +#define REG_RX_NB_BYTES 0x13 +#define REG_OPMODE 0x01 +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_PACKET_SNR 0x19 +#define REG_PACKET_RSSI 0x1A +#define REG_CURRENT_RSSI 0x1B +#define REG_DIO_MAPPING_1 0x40 +#define REG_DIO_MAPPING_2 0x41 +#define REG_MODEM_CONFIG 0x1D +#define REG_MODEM_CONFIG2 0x1E +#define REG_MODEM_CONFIG3 0x26 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_IRQ_FLAGS_MASK 0x11 +#define REG_HOP_PERIOD 0x24 +#define REG_FREQ_ERROR 0x28 +#define REG_DETECT_OPT 0x31 +#define REG_DETECTION_THRESHOLD 0x37 + +// MODES +#define RF98_MODE_RX_CONTINUOUS 0x85 +#define RF98_MODE_TX 0x83 +#define RF98_MODE_SLEEP 0x80 +#define RF98_MODE_STANDBY 0x81 + +#define PAYLOAD_LENGTH 255 + +// Modem Config 1 +#define EXPLICIT_MODE 0x00 +#define IMPLICIT_MODE 0x01 + +#define ERROR_CODING_4_5 0x02 +#define ERROR_CODING_4_6 0x04 +#define ERROR_CODING_4_7 0x06 +#define ERROR_CODING_4_8 0x08 + +#define BANDWIDTH_7K8 0x00 +#define BANDWIDTH_10K4 0x10 +#define BANDWIDTH_15K6 0x20 +#define BANDWIDTH_20K8 0x30 +#define BANDWIDTH_31K25 0x40 +#define BANDWIDTH_41K7 0x50 +#define BANDWIDTH_62K5 0x60 +#define BANDWIDTH_125K 0x70 +#define BANDWIDTH_250K 0x80 +#define BANDWIDTH_500K 0x90 + +// Modem Config 2 + +#define SPREADING_6 0x60 +#define SPREADING_7 0x70 +#define SPREADING_8 0x80 +#define SPREADING_9 0x90 +#define SPREADING_10 0xA0 +#define SPREADING_11 0xB0 +#define SPREADING_12 0xC0 + +#define CRC_OFF 0x00 +#define CRC_ON 0x04 + +// POWER AMPLIFIER CONFIG +#define REG_PA_CONFIG 0x09 +#define PA_MAX_BOOST 0x8F +#define PA_LOW_BOOST 0x81 +#define PA_MED_BOOST 0x8A +#define PA_MAX_UK 0x88 +#define PA_OFF_BOOST 0x00 +#define RFO_MIN 0x00 + +// LOW NOISE AMPLIFIER +#define REG_LNA 0x0C +#define LNA_MAX_GAIN 0x23 // 0010 0011 +#define LNA_OFF_GAIN 0x00 +#define LNA_LOW_GAIN 0xC0 // 1100 0000 + +struct TPayload { + int InUse; + char Payload[32]; +}; + +struct TLoRaMode +{ + int ImplicitOrExplicit; + int ErrorCoding; + int Bandwidth; + int SpreadingFactor; + int LowDataRateOptimize; + int BaudRate; + char *Description; +} LoRaModes[] = +{ + {EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_20K8, SPREADING_11, 8, 60, "Telemetry"}, // 0: Normal mode for telemetry + {IMPLICIT_MODE, ERROR_CODING_4_5, BANDWIDTH_20K8, SPREADING_6, 0, 1400, "SSDV"}, // 1: Normal mode for SSDV + {EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_62K5, SPREADING_8, 0, 2000, "Repeater"}, // 2: Normal mode for repeater network + {EXPLICIT_MODE, ERROR_CODING_4_6, BANDWIDTH_250K, SPREADING_7, 0, 8000, "Turbo"}, // 3: Normal mode for high speed images in 868MHz band + {IMPLICIT_MODE, ERROR_CODING_4_5, BANDWIDTH_250K, SPREADING_6, 0, 16828, "TurboX"}, // 4: Fastest mode within IR2030 in 868MHz band + {EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_41K7, SPREADING_11, 0, 200, "Calling"}, // 5: Calling mode + {IMPLICIT_MODE, ERROR_CODING_4_5, BANDWIDTH_41K7, SPREADING_6, 0, 2800, "Uplink"} // 6: Uplink mode for 868 +}; + +struct TConfig Config; +struct TPayload Payloads[16]; + +int LEDCounts[2]; +pthread_mutex_t var = PTHREAD_MUTEX_INITIALIZER; + +#pragma pack(1) + +struct TBinaryPacket { + uint8_t PayloadIDs; + uint16_t Counter; + uint16_t BiSeconds; + float Latitude; + float Longitude; + uint16_t Altitude; +}; + +const char *Modes[6] = + { "Slow", "SSDV", "Repeater", "Turbo", "TurboX", "Calling" }; + +// Create pipes for inter proces communication +// GLOBAL AS CALLED FROM INTERRRUPT +int telem_pipe_fd[2]; +int ssdv_pipe_fd[2]; + +// Create a structure to share some variables with the habitat child process +// GLOBAL AS CALLED FROM INTERRRUPT +thread_shared_vars_t htsv; + +// Create a structure to share some variables with the ssdv child process +// GLOBAL AS CALLED FROM INTERRRUPT +thread_shared_vars_t stsv; + +int habitate_telem_packets = 0; + +void +hexdump_buffer( const char *title, const char *buffer, const int len_buffer ) +{ + int i, j = 0; + char message[200]; + FILE *fp; + + fp = fopen( "pkt.txt", "a" ); + + fprintf( fp, "Title = %s\n", title ); + + for ( i = 0; i < len_buffer; i++ ) + { + sprintf( &message[3 * j], "%02x ", buffer[i] ); + j++; + if ( i % 16 == 15 ) + { + j = 0; + fprintf( fp, "%s\n", message ); + message[0] = '\0'; + } + } + fprintf( fp, "%s\n", message ); + fclose( fp ); + +} + +void +writeRegister( int Channel, uint8_t reg, uint8_t val ) +{ + unsigned char data[2]; + + data[0] = reg | 0x80; + data[1] = val; + wiringPiSPIDataRW( Channel, data, 2 ); +} + +uint8_t +readRegister( int Channel, uint8_t reg ) +{ + unsigned char data[2]; + uint8_t val; + + data[0] = reg & 0x7F; + data[1] = 0; + wiringPiSPIDataRW( Channel, data, 2 ); + val = data[1]; + + return val; +} + +void +LogPacket( int Channel, int8_t SNR, int RSSI, double FreqError, int Bytes, + unsigned char MessageType ) +{ + if ( Config.EnablePacketLogging ) + { + FILE *fp; + + if ( ( fp = fopen( "packets.txt", "at" ) ) != NULL ) + { + time_t now; + struct tm *tm; + + now = time( 0 ); + tm = localtime( &now ); + + fprintf( fp, + "%02d:%02d:%02d - Ch %d, SNR %d, RSSI %d, FreqErr %.1lf, Bytes %d, Type %02Xh\n", + tm->tm_hour, tm->tm_min, tm->tm_sec, Channel, SNR, RSSI, + FreqError, Bytes, MessageType ); + + fclose( fp ); + } + } +} + +void +LogTelemetryPacket( char *Telemetry ) +{ + // if (Config.EnableTelemetryLogging) + { + FILE *fp; + + if ( ( fp = fopen( "telemetry.txt", "at" ) ) != NULL ) + { + time_t now; + struct tm *tm; + + now = time( 0 ); + tm = localtime( &now ); + + fprintf( fp, "%02d:%02d:%02d - %s\n", tm->tm_hour, tm->tm_min, + tm->tm_sec, Telemetry ); + + fclose( fp ); + } + } +} + +void +LogMessage( const char *format, ... ) +{ + static WINDOW *Window = NULL; + char Buffer[512]; + + pthread_mutex_lock( &var ); // lock the critical section + + if ( Window == NULL ) + { + // Window = newwin(25, 30, 0, 50); + Window = newwin( LINES - 16, COLS, 16, 0 ); + scrollok( Window, TRUE ); + } + + va_list args; + va_start( args, format ); + + vsprintf( Buffer, format, args ); + + va_end( args ); + + if ( strlen( Buffer ) > COLS - 1 ) + { + Buffer[COLS - 3] = '.'; + Buffer[COLS - 2] = '.'; + Buffer[COLS - 1] = '\n'; + Buffer[COLS] = 0; + } + + waddstr( Window, Buffer ); + + wrefresh( Window ); + + pthread_mutex_unlock( &var ); // unlock once you are done + +} + +void +ChannelPrintf( int Channel, int row, int column, const char *format, ... ) +{ + char Buffer[80]; + va_list args; + + pthread_mutex_lock( &var ); // lock the critical section + + va_start( args, format ); + + vsprintf( Buffer, format, args ); + + va_end( args ); + + mvwaddstr( Config.LoRaDevices[Channel].Window, row, column, Buffer ); + + wrefresh( Config.LoRaDevices[Channel].Window ); + + pthread_mutex_unlock( &var ); // unlock once you are done + +} + +void +setMode( int Channel, uint8_t newMode ) +{ + if ( newMode == currentMode ) + return; + + switch ( newMode ) + { + case RF98_MODE_TX: + writeRegister( Channel, REG_LNA, LNA_OFF_GAIN ); // TURN LNA OFF FOR TRANSMITT + writeRegister( Channel, REG_PA_CONFIG, Config.LoRaDevices[Channel].Power ); // PA_MAX_UK + writeRegister( Channel, REG_OPMODE, newMode ); + currentMode = newMode; + break; + case RF98_MODE_RX_CONTINUOUS: + writeRegister( Channel, REG_PA_CONFIG, PA_OFF_BOOST ); // TURN PA OFF FOR RECIEVE?? + writeRegister( Channel, REG_LNA, LNA_MAX_GAIN ); // MAX GAIN FOR RECEIVE + writeRegister( Channel, REG_OPMODE, newMode ); + currentMode = newMode; + // LogMessage("Changing to Receive Continuous Mode\n"); + break; + case RF98_MODE_SLEEP: + writeRegister( Channel, REG_OPMODE, newMode ); + currentMode = newMode; + // LogMessage("Changing to Sleep Mode\n"); + break; + case RF98_MODE_STANDBY: + writeRegister( Channel, REG_OPMODE, newMode ); + currentMode = newMode; + // LogMessage("Changing to Standby Mode\n"); + break; + default: + return; + } + + if ( newMode != RF98_MODE_SLEEP ) + { + while ( digitalRead( Config.LoRaDevices[Channel].DIO5 ) == 0 ) + { + } + // delay(1); + } + + // LogMessage("Mode Change Done\n"); + return; +} + +void +setFrequency( int Channel, double Frequency ) +{ + unsigned long FrequencyValue; + char FrequencyString[10]; + + // Format frequency as xxx.xxx.x Mhz + sprintf( FrequencyString, "%8.4lf ", Frequency ); + FrequencyString[8] = FrequencyString[7]; + FrequencyString[7] = '.'; + + FrequencyValue = ( unsigned long ) ( Frequency * 7110656 / 434 ); + + writeRegister( Channel, 0x06, ( FrequencyValue >> 16 ) & 0xFF ); // Set frequency + writeRegister( Channel, 0x07, ( FrequencyValue >> 8 ) & 0xFF ); + writeRegister( Channel, 0x08, FrequencyValue & 0xFF ); + + Config.LoRaDevices[Channel].activeFreq = Frequency; + + // LogMessage("Set Frequency to %lf\n", Frequency); + + ChannelPrintf( Channel, 1, 1, "Channel %d %s MHz ", Channel, + FrequencyString ); +} + +void +setLoRaMode( int Channel ) +{ + double Frequency; + + // LogMessage("Setting LoRa Mode\n"); + setMode( Channel, RF98_MODE_SLEEP ); + writeRegister( Channel, REG_OPMODE, 0x80 ); + + setMode( Channel, RF98_MODE_SLEEP ); + + if ( sscanf( Config.LoRaDevices[Channel].Frequency, "%lf", &Frequency ) ) + { + // LogMessage("Set Default Frequency\n"); + setFrequency( Channel, Frequency ); + } +} + +char * +BandwidthString( int Bandwidth ) +{ + if ( Bandwidth == BANDWIDTH_7K8 ) + return "7.8k"; + if ( Bandwidth == BANDWIDTH_10K4 ) + return "10.4k"; + if ( Bandwidth == BANDWIDTH_15K6 ) + return "15.6k"; + if ( Bandwidth == BANDWIDTH_20K8 ) + return "20.8k"; + if ( Bandwidth == BANDWIDTH_31K25 ) + return "31.25k"; + if ( Bandwidth == BANDWIDTH_41K7 ) + return "41.7k"; + if ( Bandwidth == BANDWIDTH_62K5 ) + return "62.5k"; + if ( Bandwidth == BANDWIDTH_125K ) + return "125k"; + if ( Bandwidth == BANDWIDTH_250K ) + return "250k"; + if ( Bandwidth == BANDWIDTH_500K ) + return "500k"; + return "??k"; +} + +void +SetLoRaParameters( int Channel, int ImplicitOrExplicit, int ErrorCoding, + int Bandwidth, int SpreadingFactor, + int LowDataRateOptimize ) +{ + writeRegister( Channel, REG_MODEM_CONFIG, + ImplicitOrExplicit | ErrorCoding | Bandwidth ); + writeRegister( Channel, REG_MODEM_CONFIG2, SpreadingFactor | CRC_ON ); + writeRegister( Channel, REG_MODEM_CONFIG3, 0x04 | LowDataRateOptimize ); // 0x04: AGC sets LNA gain + writeRegister( Channel, REG_DETECT_OPT, ( readRegister( Channel, REG_DETECT_OPT ) & 0xF8 ) | ( ( SpreadingFactor == SPREADING_6 ) ? 0x05 : 0x03 ) ); // 0x05 For SF6; 0x03 otherwise + writeRegister( Channel, REG_DETECTION_THRESHOLD, ( SpreadingFactor == SPREADING_6 ) ? 0x0C : 0x0A ); // 0x0C for SF6, 0x0A otherwise + + Config.LoRaDevices[Channel].CurrentBandwidth = Bandwidth; + + ChannelPrintf( Channel, 2, 1, "%s, %s, SF%d, EC4:%d %s", + ImplicitOrExplicit == + IMPLICIT_MODE ? "Implicit" : "Explicit", + BandwidthString( Bandwidth ), SpreadingFactor >> 4, + ( ErrorCoding >> 1 ) + 4, + LowDataRateOptimize ? "LDRO" : "" ); +} + +void +SetDefaultLoRaParameters( int Channel ) +{ + // LogMessage("Set Default Parameters\n"); + + SetLoRaParameters( Channel, + Config.LoRaDevices[Channel].ImplicitOrExplicit, + Config.LoRaDevices[Channel].ErrorCoding, + Config.LoRaDevices[Channel].Bandwidth, + Config.LoRaDevices[Channel].SpreadingFactor, + Config.LoRaDevices[Channel].LowDataRateOptimize ); +} + +///////////////////////////////////// +// Method: Setup to receive continuously +////////////////////////////////////// +void +startReceiving( int Channel ) +{ + writeRegister( Channel, REG_DIO_MAPPING_1, 0x00 ); // 00 00 00 00 maps DIO0 to RxDone + + writeRegister( Channel, REG_PAYLOAD_LENGTH, 255 ); + writeRegister( Channel, REG_RX_NB_BYTES, 255 ); + + writeRegister( Channel, REG_FIFO_RX_BASE_AD, 0 ); + writeRegister( Channel, REG_FIFO_ADDR_PTR, 0 ); + + // Setup Receive Continous Mode + setMode( Channel, RF98_MODE_RX_CONTINUOUS ); +} + +void ReTune( int Channel, double FreqShift ) +{ + setMode( Channel, RF98_MODE_SLEEP ); + LogMessage( "Retune by %lf kHz\n", FreqShift * 1000 ); + setFrequency( Channel, Config.LoRaDevices[Channel].activeFreq + FreqShift ); + startReceiving( Channel ); +} + +void SendLoRaData(int Channel, char *buffer, int Length) +{ + unsigned char data[257]; + int i; + + // Change frequency for the uplink ? + if (Config.LoRaDevices[Channel].UplinkFrequency > 0) + { + LogMessage("Change frequency to %.3lfMHz\n", Config.LoRaDevices[Channel].UplinkFrequency); + setFrequency(Channel, Config.LoRaDevices[Channel].UplinkFrequency); + } + + // Change mode for the uplink ? + if (Config.LoRaDevices[Channel].UplinkMode >= 0) + { + int UplinkMode; + + UplinkMode = Config.LoRaDevices[Channel].UplinkMode; + + LogMessage("Change LoRa mode to %d\n", Config.LoRaDevices[Channel].UplinkMode); + + SetLoRaParameters(Channel, + LoRaModes[UplinkMode].ImplicitOrExplicit, + LoRaModes[UplinkMode].ErrorCoding, + LoRaModes[UplinkMode].Bandwidth, + LoRaModes[UplinkMode].SpreadingFactor, + 0); + } + + LogMessage( "LoRa Channel %d Sending %d bytes\n", Channel, Length ); + Config.LoRaDevices[Channel].Sending = 1; + + setMode( Channel, RF98_MODE_STANDBY ); + + writeRegister( Channel, REG_DIO_MAPPING_1, 0x40 ); // 01 00 00 00 maps DIO0 to TxDone + + writeRegister( Channel, REG_FIFO_TX_BASE_AD, 0x00 ); // Update the address ptr to the current tx base address + writeRegister( Channel, REG_FIFO_ADDR_PTR, 0x00 ); + + data[0] = REG_FIFO | 0x80; + for ( i = 0; i < Length; i++ ) + { + data[i + 1] = buffer[i]; + } + wiringPiSPIDataRW( Channel, data, Length + 1 ); + + // Set the length. For implicit mode, since the length needs to match what the receiver expects, we have to set a value which is 255 for an SSDV packet + writeRegister( Channel, REG_PAYLOAD_LENGTH, Length ); + + // go into transmit mode + setMode( Channel, RF98_MODE_TX ); +} + +void +ShowPacketCounts( int Channel ) +{ + if ( Config.LoRaDevices[Channel].InUse ) + { + ChannelPrintf( Channel, 7, 1, "Telem Packets = %d (%us) ", + Config.LoRaDevices[Channel].TelemetryCount, + Config.LoRaDevices[Channel]. + LastTelemetryPacketAt ? ( unsigned int ) ( time( NULL ) + - + Config. + LoRaDevices + [Channel]. + LastTelemetryPacketAt ) + : 0 ); + ChannelPrintf( Channel, 8, 1, "Image Packets = %d (%us) ", + Config.LoRaDevices[Channel].SSDVCount, + Config.LoRaDevices[Channel]. + LastSSDVPacketAt ? ( unsigned int ) ( time( NULL ) - + Config. + LoRaDevices + [Channel]. + LastSSDVPacketAt ) + : 0 ); + + ChannelPrintf( Channel, 9, 1, "Bad CRC = %d Bad Type = %d", + Config.LoRaDevices[Channel].BadCRCCount, + Config.LoRaDevices[Channel].UnknownCount ); + + ChannelPrintf( Channel, 6, 16, "SSDV %d ", + Config.LoRaDevices[Channel].SSDVCount ); + } +} + +void +ProcessUploadMessage( int Channel, char *Message ) +{ + // LogMessage("Ch %d: Uploaded message %s\n", Channel, Message); +} + +void +ProcessCallingMessage( int Channel, char *Message ) +{ + char Payload[16]; + double Frequency; + int ImplicitOrExplicit, ErrorCoding, Bandwidth, SpreadingFactor, + LowDataRateOptimize; + + ChannelPrintf( Channel, 3, 1, "Calling message %d bytes ", + strlen( Message ) ); + + if ( sscanf( Message + 2, "%15[^,],%lf,%d,%d,%d,%d,%d", + Payload, + &Frequency, + &ImplicitOrExplicit, + &ErrorCoding, + &Bandwidth, &SpreadingFactor, &LowDataRateOptimize ) == 7 ) + { + if ( Config.LoRaDevices[Channel].AFC ) + { + double MasterFrequency; + + sscanf( Config.LoRaDevices[Channel].Frequency, "%lf", + &MasterFrequency ); + + Frequency += + Config.LoRaDevices[Channel].activeFreq - MasterFrequency; + } + + LogMessage( "Ch %d: Calling message, new frequency %7.3lf\n", Channel, + Frequency ); + + // Decoded OK + setMode( Channel, RF98_MODE_SLEEP ); + + // setFrequency(Channel, Config.LoRaDevices[Channel].activeFreq + ); + setFrequency( Channel, Frequency ); + + SetLoRaParameters( Channel, ImplicitOrExplicit, ErrorCoding, + Bandwidth, SpreadingFactor, LowDataRateOptimize ); + + setMode( Channel, RF98_MODE_RX_CONTINUOUS ); + + Config.LoRaDevices[Channel].InCallingMode = 1; + + // ChannelPrintf(Channel, 1, 1, "Channel %d %7.3lfMHz ", Channel, Frequency); + } +} + +size_t +write_data( void *buffer, size_t size, size_t nmemb, void *userp ) +{ + return size * nmemb; +} + +void +UploadListenerTelemetry( char *callsign, float gps_lat, float gps_lon, + char *antenna ) +{ + int time_epoch = ( int ) time( NULL ); + if ( Config.EnableHabitat ) + { + CURL *curl; + CURLcode res; + char PostFields[300]; + char JsonData[200]; + + /* In windows, this will init the winsock stuff */ + + /* get a curl handle */ + curl = curl_easy_init( ); + if ( curl ) + { + // So that the response to the curl POST doesn;'t mess up my finely crafted display! + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data ); + + // Set the URL that is about to receive our POST + curl_easy_setopt( curl, CURLOPT_URL, + "http://habitat.habhub.org/transition/listener_telemetry" ); + + // Now specify the POST data + sprintf( JsonData, "{\"latitude\": %f, \"longitude\": %f}", + gps_lat, gps_lon ); + sprintf( PostFields, "callsign=%s&time=%d&data=%s", callsign, + time_epoch, JsonData ); + curl_easy_setopt( curl, CURLOPT_POSTFIELDS, PostFields ); + + // Perform the request, res will get the return code + res = curl_easy_perform( curl ); + + // Check for errors + if ( res == CURLE_OK ) + { + LogMessage( "Uploaded listener %s position %f,%f\n", + Config.Tracker, Config.latitude, + Config.longitude ); + } + else + { + LogMessage( "curl_easy_perform() failed: %s\n", + curl_easy_strerror( res ) ); + } + + // always cleanup + curl_easy_cleanup( curl ); + } + + /* In windows, this will init the winsock stuff */ + + /* get a curl handle */ + curl = curl_easy_init( ); + if ( curl ) + { + // So that the response to the curl POST doesn;'t mess up my finely crafted display! + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data ); + + // Set the URL that is about to receive our POST + curl_easy_setopt( curl, CURLOPT_URL, + "http://habitat.habhub.org/transition/listener_information" ); + + // Now specify the POST data + sprintf( JsonData, "{\"radio\": \"%s\", \"antenna\": \"%s\"}", + "LoRa RFM98W", antenna ); + sprintf( PostFields, "callsign=%s&time=%d&data=%s", Config.Tracker, time_epoch, JsonData ); + curl_easy_setopt( curl, CURLOPT_POSTFIELDS, PostFields ); + + // Perform the request, res will get the return code + res = curl_easy_perform( curl ); + + // Check for errors + if ( res != CURLE_OK ) + { + LogMessage( "curl_easy_perform() failed: %s\n", + curl_easy_strerror( res ) ); + } + + // always cleanup + curl_easy_cleanup( curl ); + } + + } +} + + +void +DoPositionCalcs( Channel ) +{ + unsigned long Now; + struct tm tm; + float Climb, Period; + + strptime( Config.LoRaDevices[Channel].Time, "%H:%M:%S", &tm ); + Now = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec; + + if ( ( Config.LoRaDevices[Channel].LastPositionAt > 0 ) + && ( Now > Config.LoRaDevices[Channel].LastPositionAt ) ) + { + Climb = + ( float ) Config.LoRaDevices[Channel].Altitude - + ( float ) Config.LoRaDevices[Channel].PreviousAltitude; + Period = + ( float ) Now - + ( float ) Config.LoRaDevices[Channel].LastPositionAt; + Config.LoRaDevices[Channel].AscentRate = Climb / Period; + } + else + { + Config.LoRaDevices[Channel].AscentRate = 0; + } + + Config.LoRaDevices[Channel].PreviousAltitude = + Config.LoRaDevices[Channel].Altitude; + Config.LoRaDevices[Channel].LastPositionAt = Now; + + ChannelPrintf( Channel, 4, 1, "%8.5lf, %8.5lf, %05u ", + Config.LoRaDevices[Channel].Latitude, + Config.LoRaDevices[Channel].Longitude, + Config.LoRaDevices[Channel].Altitude ); +} + + +void +ProcessLine( int Channel, char *Line ) +{ + int Satellites; + float TempInt, TempExt; + + Config.LoRaDevices[Channel].FlightMode = -1; + + if ( Config.EnableDev ) + { + sscanf( Line + 2, + "%15[^,],%u,%8[^,],%lf,%lf,%u,%d,%d,%d,%f,%f,%lf,%lf,%lf,%lf,%d,%d,%d,%lf,%d,%d,%d,%d,%lf,%d", + ( Config.LoRaDevices[Channel].Payload ), + &( Config.LoRaDevices[Channel].Counter ), + ( Config.LoRaDevices[Channel].Time ), + &( Config.LoRaDevices[Channel].Latitude ), + &( Config.LoRaDevices[Channel].Longitude ), + &( Config.LoRaDevices[Channel].Altitude ), + &( Config.LoRaDevices[Channel].Speed ), + &( Config.LoRaDevices[Channel].Heading ), &Satellites, + &TempInt, &TempExt, &( Config.LoRaDevices[Channel].cda ), + &( Config.LoRaDevices[Channel].PredictedLatitude ), + &( Config.LoRaDevices[Channel].PredictedLongitude ), + &( Config.LoRaDevices[Channel].PredictedLandingSpeed ), + &( Config.LoRaDevices[Channel].PredictedTime ), + &( Config.LoRaDevices[Channel].CompassActual ), + &( Config.LoRaDevices[Channel].CompassTarget ), + &( Config.LoRaDevices[Channel].AirSpeed ), + &( Config.LoRaDevices[Channel].AirDirection ), + &( Config.LoRaDevices[Channel].ServoLeft ), + &( Config.LoRaDevices[Channel].ServoRight ), + &( Config.LoRaDevices[Channel].ServoTime ), + &( Config.LoRaDevices[Channel].GlideRatio ), + &( Config.LoRaDevices[Channel].FlightMode ) ); + } + else + { + sscanf( Line + 2, "%15[^,],%u,%8[^,],%lf,%lf,%u", + ( Config.LoRaDevices[Channel].Payload ), + &( Config.LoRaDevices[Channel].Counter ), + ( Config.LoRaDevices[Channel].Time ), + &( Config.LoRaDevices[Channel].Latitude ), + &( Config.LoRaDevices[Channel].Longitude ), + &( Config.LoRaDevices[Channel].Altitude ) ); + } +} + + +void +ProcessTelemetryMessage( int Channel, char *Message ) +{ + if ( strlen( Message + 1 ) < 250 ) + { + char *startmessage, *endmessage; + + ChannelPrintf( Channel, 3, 1, "Telemetry %d bytes ", + strlen( Message + 1 ) ); + + startmessage = Message; + endmessage = strchr( startmessage, '\n' ); + + if ( endmessage != NULL ) + { + habitate_telem_packets++; + + time_t now; + struct tm *tm; + + *endmessage = '\0'; + + LogTelemetryPacket(startmessage); + + strcpy( Config.LoRaDevices[Channel].Telemetry, startmessage ); + // UploadTelemetryPacket(startmessage); + + ProcessLine( Channel, startmessage ); + + now = time( 0 ); + tm = localtime( &now ); + + + if ( Config.EnableHabitat ) + { + + // Create a telemetry packet + telemetry_t t; + t.Channel = Channel; + t.Packet_Number = habitate_telem_packets; + memcpy( t.Telemetry, startmessage, + strlen( startmessage ) + 1 ); + + // Add the telemetry packet to the pipe + int result = write( telem_pipe_fd[1], &t, sizeof( t ) ); + if ( result == -1 ) + { + printf( "Error writing to the telemetry pipe\n" ); + exit( 1 ); + } + if ( result == 0 ) + { + LogMessage( "Nothing written to telemetry pipe \n" ); + } + if ( result > 1 ) + { + htsv.packet_count++; + } + } + + LogMessage( "%02d:%02d:%02d Ch%d: %s\n", tm->tm_hour, tm->tm_min, + tm->tm_sec, Channel, startmessage ); + + } + + DoPositionCalcs( Channel ); + + // RJH I think this should be moved up to the bottom of the loop above + Config.LoRaDevices[Channel].TelemetryCount++; + Config.LoRaDevices[Channel].LastTelemetryPacketAt = time( NULL ); + } +} + +static char * +decode_callsign( char *callsign, uint32_t code ) +{ + char *c, s; + + *callsign = '\0'; + + /* Is callsign valid? */ + if ( code > 0xF423FFFF ) + return ( callsign ); + + for ( c = callsign; code; c++ ) + { + s = code % 40; + if ( s == 0 ) + *c = '-'; + else if ( s < 11 ) + *c = '0' + s - 1; + else if ( s < 14 ) + *c = '-'; + else + *c = 'A' + s - 14; + code /= 40; + } + *c = '\0'; + + return ( callsign ); +} + +int +FileExists( char *filename ) +{ + struct stat st; + + return stat( filename, &st ) == 0; +} + +void +ProcessSSDVMessage( int Channel, char *Message ) +{ + // SSDV packet + uint32_t CallsignCode; + char Callsign[7], *FileMode; + int ImageNumber, PacketNumber; + char filename[100]; + FILE *fp; + + Message[0] = 0x55; + + CallsignCode = Message[2]; + CallsignCode <<= 8; + CallsignCode |= Message[3]; + CallsignCode <<= 8; + CallsignCode |= Message[4]; + CallsignCode <<= 8; + CallsignCode |= Message[5]; + + decode_callsign( Callsign, CallsignCode ); + + ImageNumber = Message[6]; + PacketNumber = Message[7] * 256 + Message[8]; + + LogMessage( "Ch%d: SSDV Packet, Callsign %s, Image %d, Packet %d\n", + Channel, Callsign, Message[6], PacketNumber ); + ChannelPrintf( Channel, 3, 1, "SSDV Packet " ); + ChannelPrintf( Channel, 5, 1, "SSDV %s: Image %d, Packet %d", Callsign, + Message[6], PacketNumber ); + + // Create new file ? + sprintf( filename, "/tmp/%s_%d.bin", Callsign, ImageNumber ); + if ( !FileExists( filename ) ) + { + // New image so new file + LogMessage( "Started image %d\n", ImageNumber ); + + // AddImageNumberToLog(Channel, ImageNumber); + + FileMode = "wb"; + } + else + { + FileMode = "r+b"; + } + + // Save to file + if ( ( fp = fopen( filename, FileMode ) ) ) + { + fseek( fp, PacketNumber * 256, SEEK_SET ); + if ( fwrite( Message, 1, 256, fp ) == 256 ) + { + // AddImagePacketToLog(Channel, ImageNumber, PacketNumber); + } + else + { + LogMessage( "** FAILED TO WRITE TO SSDV FILE\n" ); + } + + fclose( fp ); + } + + // ShowMissingPackets(Channel); + + if ( Config.EnableSSDV ) + { + // Create a SSDV packet + ssdv_t s; + s.Channel = Channel; + s.Packet_Number = Config.LoRaDevices[Channel].SSDVCount; + memcpy( s.SSDV_Packet, Message, 256 ); + + // Add the SSDV packet to the pipe + int result = write( ssdv_pipe_fd[1], &s, sizeof( s ) ); + if ( result == -1 ) + { + printf( "Error writing to the issdv pipe\n" ); + exit( 1 ); + } + if ( result == 0 ) + { + LogMessage( "Nothing written to ssdv pipe \n" ); + } + if ( result > 1 ) + { + stsv.packet_count++; + } + + } + + Config.LoRaDevices[Channel].SSDVCount++; + Config.LoRaDevices[Channel].LastSSDVPacketAt = time( NULL ); +} + +void +TestMessageForSMSAcknowledgement( int Channel, char *Message ) +{ + if ( Config.SMSFolder[0] ) + { + if ( strlen( Message ) < 250 ) + { + char Line[256]; + char *token, *value1, *value2; + int FileNumber; + + value1 = NULL; + value2 = NULL; + + strcpy( Line, Message ); + token = strtok( Line, "," ); + while ( token != NULL ) + { + value1 = value2; + value2 = token; + token = strtok( NULL, "," ); + } + + // Rename the file matching this parameter + if ( value1 ) + { + char OldFileName[256], NewFileName[256]; + FileNumber = atoi( value1 ); + if ( FileNumber > 0 ) + { + sprintf( OldFileName, "%s/%d.sms", Config.SMSFolder, FileNumber ); + if ( FileExists( OldFileName ) ) + { + sprintf( NewFileName, "%s/%d.ack", Config.SMSFolder, FileNumber ); + if ( FileExists( NewFileName ) ) + { + remove( NewFileName ); + } + rename( OldFileName, NewFileName ); + LogMessage( "Renamed %s as %s\n", OldFileName, + NewFileName ); + } + } + } + } + } +} + +void +DIO0_Interrupt( int Channel ) +{ + if ( Config.LoRaDevices[Channel].Sending ) + { + Config.LoRaDevices[Channel].Sending = 0; + LogMessage( "Ch%d: End of Tx\n", Channel ); + + setLoRaMode( Channel ); + SetDefaultLoRaParameters( Channel ); + startReceiving( Channel ); + } + else + { + int Bytes; + char Message[257]; + + Bytes = receiveMessage( Channel, Message + 1 ); + + // hexdump_buffer ("Raw Data", Message, 257); + + + if ( Bytes > 0 ) + { + if ( Config.LoRaDevices[Channel].ActivityLED >= 0 ) + { + digitalWrite( Config.LoRaDevices[Channel].ActivityLED, 1 ); + LEDCounts[Channel] = 5; + } + + if ( Message[1] == '!' ) + { + ProcessUploadMessage( Channel, Message + 1 ); + } + else if ( Message[1] == '^' ) + { + ProcessCallingMessage( Channel, Message + 1 ); + } + else if ( Message[1] == '$' ) + { + ProcessTelemetryMessage( Channel, Message + 1 ); + TestMessageForSMSAcknowledgement( Channel, Message + 1 ); + } + else if ( Message[1] == '>' ) + { + LogMessage( "Flight Controller message %d bytes = %s", Bytes, + Message + 1 ); + } + else if ( Message[1] == '*' ) + { + LogMessage( "Uplink Command message %d bytes = %s", Bytes, + Message + 1 ); + } + else if ((Message[1] == 0x66) || (Message[1] == 0x67) || (Message[1] == 0x68) || (Message[1] == 0x69)) + { + ProcessSSDVMessage( Channel, Message ); + } + else + { + LogMessage( "Unknown packet type is %02Xh, RSSI %d\n", + Message[1], readRegister( Channel, + REG_PACKET_RSSI ) - + 157 ); + ChannelPrintf( Channel, 3, 1, "Unknown Packet %d, %d bytes", + Message[0], Bytes ); + Config.LoRaDevices[Channel].UnknownCount++; + } + + Config.LoRaDevices[Channel].LastPacketAt = time( NULL ); + + if ( Config.LoRaDevices[Channel].InCallingMode + && ( Config.CallingTimeout > 0 ) ) + { + Config.LoRaDevices[Channel].ReturnToCallingModeAt = + time( NULL ) + Config.CallingTimeout; + } + + ShowPacketCounts( Channel ); + } + } +} + +void DIO_Ignore_Interrupt_0( void ) +{ + // nothing, obviously! +} + +void +DIO0_Interrupt_0( void ) +{ + DIO0_Interrupt( 0 ); +} + +void +DIO0_Interrupt_1( void ) +{ + DIO0_Interrupt( 1 ); +} + +void +setupRFM98( int Channel ) +{ + if ( Config.LoRaDevices[Channel].InUse ) + { + // initialize the pins + pinMode( Config.LoRaDevices[Channel].DIO0, INPUT ); + pinMode( Config.LoRaDevices[Channel].DIO5, INPUT ); + + wiringPiISR( Config.LoRaDevices[Channel].DIO0, INT_EDGE_RISING, + Channel > 0 ? &DIO0_Interrupt_1 : &DIO0_Interrupt_0 ); + + if ( wiringPiSPISetup( Channel, 500000 ) < 0 ) + { + fprintf( stderr, + "Failed to open SPI port. Try loading spi library with 'gpio load spi'" ); + exit( 1 ); + } + + // LoRa mode + setLoRaMode( Channel ); + + SetDefaultLoRaParameters( Channel ); + + startReceiving( Channel ); + } +} + +double +FrequencyReference( int Channel ) +{ + switch ( Config.LoRaDevices[Channel].CurrentBandwidth ) + { + case BANDWIDTH_7K8: + return 7800; + case BANDWIDTH_10K4: + return 10400; + case BANDWIDTH_15K6: + return 15600; + case BANDWIDTH_20K8: + return 20800; + case BANDWIDTH_31K25: + return 31250; + case BANDWIDTH_41K7: + return 41700; + case BANDWIDTH_62K5: + return 62500; + case BANDWIDTH_125K: + return 125000; + case BANDWIDTH_250K: + return 250000; + case BANDWIDTH_500K: + return 500000; + } + + return 0; +} + +double +FrequencyError( int Channel ) +{ + int32_t Temp; + + Temp = ( int32_t ) readRegister( Channel, REG_FREQ_ERROR ) & 7; + Temp <<= 8L; + Temp += ( int32_t ) readRegister( Channel, REG_FREQ_ERROR + 1 ); + Temp <<= 8L; + Temp += ( int32_t ) readRegister( Channel, REG_FREQ_ERROR + 2 ); + + if ( readRegister( Channel, REG_FREQ_ERROR ) & 8 ) + { + Temp = Temp - 524288; + } + + return -( ( double ) Temp * ( 1 << 24 ) / 32000000.0 ) * + ( FrequencyReference( Channel ) / 500000.0 ); +} + +int +receiveMessage( int Channel, char *message ) +{ + int i, Bytes, currentAddr, x; + unsigned char data[257]; + double FreqError; + + Bytes = 0; + + x = readRegister( Channel, REG_IRQ_FLAGS ); + // LogMessage("Message status = %02Xh\n", x); + + // clear the rxDone flag + writeRegister( Channel, REG_IRQ_FLAGS, 0x40 ); + + // check for payload crc issues (0x20 is the bit we are looking for + if ( ( x & 0x20 ) == 0x20 ) + { + LogMessage( "Ch%d: CRC Failure, RSSI %d\n", Channel, + readRegister( Channel, REG_PACKET_RSSI ) - 157 ); + // reset the crc flags + writeRegister( Channel, REG_IRQ_FLAGS, 0x20 ); + ChannelPrintf( Channel, 3, 1, "CRC Failure %02Xh!!\n", x ); + Config.LoRaDevices[Channel].BadCRCCount++; + ShowPacketCounts( Channel ); + } + else + { + int8_t SNR; + int RSSI; + + currentAddr = readRegister( Channel, REG_FIFO_RX_CURRENT_ADDR ); + Bytes = readRegister( Channel, REG_RX_NB_BYTES ); + + SNR = readRegister( Channel, REG_PACKET_SNR ); + SNR /= 4; + RSSI = readRegister( Channel, REG_PACKET_RSSI ) - 157; + if ( SNR < 0 ) + { + RSSI += SNR; + } + + ChannelPrintf( Channel, 10, 1, "Packet SNR = %d, RSSI = %d ", + ( int ) SNR, RSSI ); + + FreqError = FrequencyError( Channel ) / 1000; + ChannelPrintf( Channel, 11, 1, "Freq. Error = %5.1lfkHz ", + FreqError ); + + + writeRegister( Channel, REG_FIFO_ADDR_PTR, currentAddr ); + + data[0] = REG_FIFO; + wiringPiSPIDataRW( Channel, data, Bytes + 1 ); + for ( i = 0; i <= Bytes; i++ ) + { + message[i] = data[i + 1]; + } + + message[Bytes] = '\0'; + + LogPacket( Channel, SNR, RSSI, FreqError, Bytes, message[1] ); + + if ( Config.LoRaDevices[Channel].AFC && ( fabs( FreqError ) > 0.5 ) ) + { + ReTune( Channel, FreqError / 1000 ); + } + } + + // Clear all flags + writeRegister( Channel, REG_IRQ_FLAGS, 0xFF ); + + return Bytes; +} + +void ReadString( FILE * fp, char *keyword, char *Result, int Length, int NeedValue ) +{ + char line[100], *token, *value; + + fseek( fp, 0, SEEK_SET ); + *Result = '\0'; + + while ( fgets( line, sizeof( line ), fp ) != NULL ) + { + line[strcspn(line, "\r")] = '\0'; // Get rid of CR if there is one + + token = strtok( line, "=" ); + if ( strcasecmp( keyword, token ) == 0 ) + { + value = strtok( NULL, "\n" ); + strcpy( Result, value ); + return; + } + } + + if ( NeedValue ) + { + LogMessage( "Missing value for '%s' in configuration file\n", keyword ); + exit( 1 ); + } +} + +int +ReadInteger( FILE * fp, char *keyword, int NeedValue, int DefaultValue ) +{ + char Temp[64]; + + ReadString( fp, keyword, Temp, sizeof( Temp ), NeedValue ); + + if ( Temp[0] ) + { + return atoi( Temp ); + } + + return DefaultValue; +} + +float +ReadFloat( FILE * fp, char *keyword ) +{ + char Temp[64]; + + ReadString( fp, keyword, Temp, sizeof( Temp ), 0 ); + + if ( Temp[0] ) + { + return atof( Temp ); + } + + return 0; +} + +int +ReadBoolean( FILE * fp, char *keyword, int NeedValue, int *Result ) +{ + char Temp[32]; + + ReadString( fp, keyword, Temp, sizeof( Temp ), NeedValue ); + + if ( *Temp ) + { + *Result = ( *Temp == '1' ) || ( *Temp == 'Y' ) || ( *Temp == 'y' ) + || ( *Temp == 't' ) || ( *Temp == 'T' ); + } + + return *Temp; +} + +void RemoveTrailingSlash(char *Value) +{ + int Len; + + if ((Len = strlen(Value)) > 0) + { + if ((Value[Len-1] == '/') || (Value[Len-1] == '\\')) + { + Value[Len-1] = '\0'; + } + } +} + +void LoadConfigFile(void) +{ + FILE *fp; + char *filename = "gateway.txt"; + char Keyword[32]; + int Channel, Temp; + char TempString[16]; + + Config.LoRaDevices[0].InUse = 0; + Config.LoRaDevices[1].InUse = 0; + Config.EnableHabitat = 1; + Config.EnableSSDV = 1; + Config.EnableTelemetryLogging = 0; + Config.EnablePacketLogging = 0; + Config.SSDVJpegFolder[0] = '\0'; + Config.ftpServer[0] = '\0'; + Config.ftpUser[0] = '\0'; + Config.ftpPassword[0] = '\0'; + Config.ftpFolder[0] = '\0'; + Config.latitude = -999; + Config.longitude = -999; + Config.antenna[0] = '\0'; + Config.EnableDev = 0; + + // Default pin allocations + Config.LoRaDevices[0].DIO0 = 6; + Config.LoRaDevices[0].DIO5 = 5; + + Config.LoRaDevices[1].DIO0 = 27; + Config.LoRaDevices[1].DIO5 = 26; + + if ( ( fp = fopen( filename, "r" ) ) == NULL ) + { + printf + ( "\nFailed to open config file %s (error %d - %s).\nPlease check that it exists and has read permission.\n", + filename, errno, strerror( errno ) ); + exit( 1 ); + } + + // Receiver config + ReadString( fp, "tracker", Config.Tracker, sizeof( Config.Tracker ), 1 ); + LogMessage( "Tracker = '%s'\n", Config.Tracker ); + + // Enable uploads + ReadBoolean( fp, "EnableHabitat", 0, &Config.EnableHabitat ); + ReadBoolean( fp, "EnableSSDV", 0, &Config.EnableSSDV ); + + // Enable telemetry logging + ReadBoolean( fp, "LogTelemetry", 0, &Config.EnableTelemetryLogging ); + + // Enable packet logging + ReadBoolean( fp, "LogPackets", 0, &Config.EnablePacketLogging ); + + // Calling mode + Config.CallingTimeout = ReadInteger( fp, "CallingTimeout", 0, 300 ); + + // LED allocations + Config.NetworkLED = ReadInteger( fp, "NetworkLED", 0, -1 ); + Config.InternetLED = ReadInteger( fp, "InternetLED", 0, -1 ); + Config.LoRaDevices[0].ActivityLED = + ReadInteger( fp, "ActivityLED_0", 0, -1 ); + Config.LoRaDevices[1].ActivityLED = + ReadInteger( fp, "ActivityLED_1", 0, -1 ); + + // Server Port + Config.ServerPort = ReadInteger( fp, "ServerPort", 0, -1 ); + + // SSDV Settings + ReadString( fp, "jpgFolder", Config.SSDVJpegFolder, sizeof( Config.SSDVJpegFolder ), 0 ); + if ( Config.SSDVJpegFolder[0] ) + { + // Create SSDV Folders + struct stat st = { 0 }; + + if ( stat( Config.SSDVJpegFolder, &st ) == -1 ) + { + mkdir( Config.SSDVJpegFolder, 0777 ); + } + } + + // ftp images + ReadString( fp, "ftpserver", Config.ftpServer, sizeof( Config.ftpServer ), + 0 ); + ReadString( fp, "ftpUser", Config.ftpUser, sizeof( Config.ftpUser ), 0 ); + ReadString( fp, "ftpPassword", Config.ftpPassword, + sizeof( Config.ftpPassword ), 0 ); + ReadString( fp, "ftpFolder", Config.ftpFolder, sizeof( Config.ftpFolder ), + 0 ); + + // Listener + Config.latitude = ReadFloat( fp, "Latitude" ); + Config.longitude = ReadFloat( fp, "Longitude" ); + ReadString( fp, "antenna", Config.antenna, sizeof( Config.antenna ), 0 ); + + // Dev mode + ReadBoolean( fp, "EnableDev", 0, &Config.EnableDev ); + + // SMS upload to tracker + Config.SMSFolder[0] = '\0'; + ReadString(fp, "SMSFolder", Config.SMSFolder, sizeof( Config.SMSFolder ), 0); + if ( Config.SMSFolder[0] ) + { + RemoveTrailingSlash(Config.SMSFolder); + LogMessage( "Folder %s will be scanned for messages to upload\n", Config.SMSFolder ); + } + + for ( Channel = 0; Channel <= 1; Channel++ ) + { + // Defaults + Config.LoRaDevices[Channel].Frequency[0] = '\0'; + + sprintf( Keyword, "frequency_%d", Channel ); + ReadString( fp, Keyword, Config.LoRaDevices[Channel].Frequency, + sizeof( Config.LoRaDevices[Channel].Frequency ), 0 ); + if ( Config.LoRaDevices[Channel].Frequency[0] ) + { + Config.LoRaDevices[Channel].ImplicitOrExplicit = EXPLICIT_MODE; + Config.LoRaDevices[Channel].ErrorCoding = ERROR_CODING_4_8; + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_20K8; + Config.LoRaDevices[Channel].SpreadingFactor = SPREADING_11; + Config.LoRaDevices[Channel].LowDataRateOptimize = 0x00; + Config.LoRaDevices[Channel].AFC = FALSE; + + LogMessage( "Channel %d frequency set to %s\n", Channel, + Config.LoRaDevices[Channel].Frequency ); + Config.LoRaDevices[Channel].InUse = 1; + + // DIO0 / DIO5 overrides + sprintf( Keyword, "DIO0_%d", Channel ); + Config.LoRaDevices[Channel].DIO0 = + ReadInteger( fp, Keyword, 0, + Config.LoRaDevices[Channel].DIO0 ); + + sprintf( Keyword, "DIO5_%d", Channel ); + Config.LoRaDevices[Channel].DIO5 = + ReadInteger( fp, Keyword, 0, + Config.LoRaDevices[Channel].DIO5 ); + + LogMessage( "LoRa Channel %d DIO0=%d DIO5=%d\n", Channel, + Config.LoRaDevices[Channel].DIO0, + Config.LoRaDevices[Channel].DIO5 ); + + // Uplink + sprintf( Keyword, "UplinkTime_%d", Channel ); + Config.LoRaDevices[Channel].UplinkTime = ReadInteger( fp, Keyword, 0, 0 ); + sprintf( Keyword, "UplinkCycle_%d", Channel ); + Config.LoRaDevices[Channel].UplinkCycle = ReadInteger( fp, Keyword, 0, 0 ); + LogMessage( "Channel %d UplinkTime %d Uplink Cycle %d\n", Channel, + Config.LoRaDevices[Channel].UplinkTime, + Config.LoRaDevices[Channel].UplinkCycle ); + + sprintf( Keyword, "Power_%d", Channel ); + Config.LoRaDevices[Channel].Power = ReadInteger( fp, Keyword, 0, PA_MAX_UK ); + LogMessage( "Channel %d power set to %02Xh\n", Channel, Config.LoRaDevices[Channel].Power ); + + sprintf( Keyword, "UplinkMode_%d", Channel ); + Config.LoRaDevices[Channel].UplinkMode = ReadInteger( fp, Keyword, 0, -1); + if (Config.LoRaDevices[Channel].UplinkMode >= 0) + { + LogMessage( "Channel %d uplink mode %d\n", Channel, Config.LoRaDevices[Channel].UplinkMode); + } + + sprintf( Keyword, "UplinkFrequency_%d", Channel ); + Config.LoRaDevices[Channel].UplinkFrequency = 0; + Config.LoRaDevices[Channel].UplinkFrequency = ReadFloat(fp, Keyword); + if (Config.LoRaDevices[Channel].UplinkFrequency > 0) + { + LogMessage( "Channel %d uplink frequency %.3lfMHz\n", Channel, Config.LoRaDevices[Channel].UplinkFrequency); + } + + Config.LoRaDevices[Channel].SpeedMode = 0; + + sprintf( Keyword, "mode_%d", Channel ); + Config.LoRaDevices[Channel].SpeedMode = ReadInteger( fp, Keyword, 0, 0 ); + + if ((Config.LoRaDevices[Channel].SpeedMode < 0) || (Config.LoRaDevices[Channel].SpeedMode >= sizeof(LoRaModes)/sizeof(LoRaModes[0]))) Config.LoRaDevices[Channel].SpeedMode = 0; + + Config.LoRaDevices[Channel].ImplicitOrExplicit = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].ImplicitOrExplicit; + Config.LoRaDevices[Channel].ErrorCoding = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].ErrorCoding; + Config.LoRaDevices[Channel].Bandwidth = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].Bandwidth; + Config.LoRaDevices[Channel].SpreadingFactor = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].SpreadingFactor; + Config.LoRaDevices[Channel].LowDataRateOptimize = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].LowDataRateOptimize; + + sprintf( Keyword, "sf_%d", Channel ); + Temp = ReadInteger( fp, Keyword, 0, 0 ); + if ( ( Temp >= 6 ) && ( Temp <= 12 ) ) + { + Config.LoRaDevices[Channel].SpreadingFactor = Temp << 4; + LogMessage( "Setting SF=%d\n", Temp ); + } + + sprintf( Keyword, "bandwidth_%d", Channel ); + ReadString( fp, Keyword, TempString, sizeof( TempString ), 0 ); + if ( *TempString ) + { + LogMessage( "Setting BW=%s\n", TempString ); + } + if ( strcmp( TempString, "7K8" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_7K8; + } + else if ( strcmp( TempString, "10K4" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_10K4; + } + else if ( strcmp( TempString, "15K6" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_15K6; + } + else if ( strcmp( TempString, "20K8" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_20K8; + } + else if ( strcmp( TempString, "31K25" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_31K25; + } + else if ( strcmp( TempString, "41K7" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_41K7; + } + else if ( strcmp( TempString, "62K5" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_62K5; + } + else if ( strcmp( TempString, "125K" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_125K; + } + else if ( strcmp( TempString, "250K" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_250K; + } + else if ( strcmp( TempString, "500K" ) == 0 ) + { + Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_500K; + } + + sprintf( Keyword, "implicit_%d", Channel ); + if ( ReadBoolean( fp, Keyword, 0, &Temp ) ) + { + Config.LoRaDevices[Channel].ImplicitOrExplicit = + Temp ? IMPLICIT_MODE : EXPLICIT_MODE; + } + + sprintf( Keyword, "coding_%d", Channel ); + Temp = ReadInteger( fp, Keyword, 0, 0 ); + if ( ( Temp >= 5 ) && ( Temp <= 8 ) ) + { + Config.LoRaDevices[Channel].ErrorCoding = ( Temp - 4 ) << 1; + LogMessage( "Setting Error Coding=%d\n", Temp ); + } + + sprintf( Keyword, "lowopt_%d", Channel ); + if ( ReadBoolean( fp, Keyword, 0, &Temp ) ) + { + if ( Temp ) + { + Config.LoRaDevices[Channel].LowDataRateOptimize = 0x08; + } + } + + sprintf( Keyword, "AFC_%d", Channel ); + if ( ReadBoolean( fp, Keyword, 0, &Temp ) ) + { + if ( Temp ) + { + Config.LoRaDevices[Channel].AFC = TRUE; + ChannelPrintf( Channel, 11, 24, "AFC" ); + } + } + + // Clear any flags left over from a previous run + writeRegister( Channel, REG_IRQ_FLAGS, 0xFF ); + } + } + + fclose( fp ); +} + +void +LoadPayloadFile( int ID ) +{ + FILE *fp; + char filename[16]; + + sprintf( filename, "payload_%d.txt", ID ); + + if ( ( fp = fopen( filename, "r" ) ) != NULL ) + { + LogMessage( "Reading payload file %s\n", filename ); + ReadString( fp, "payload", Payloads[ID].Payload, + sizeof( Payloads[ID].Payload ), 1 ); + LogMessage( "Payload %d = '%s'\n", ID, Payloads[ID].Payload ); + + Payloads[ID].InUse = 1; + + fclose( fp ); + } + else + { + strcpy( Payloads[ID].Payload, "Unknown" ); + Payloads[ID].InUse = 0; + } +} + +void +LoadPayloadFiles( void ) +{ + int ID; + + for ( ID = 0; ID < 16; ID++ ) + { + LoadPayloadFile( ID ); + } +} + +WINDOW * +InitDisplay( void ) +{ + WINDOW *mainwin; + int Channel; + + /* Initialize ncurses */ + + if ( ( mainwin = initscr( ) ) == NULL ) + { + fprintf( stderr, "Error initialising ncurses.\n" ); + exit( EXIT_FAILURE ); + } + + start_color( ); /* Initialize colours */ + + if ( COLORS == 256 ) + { + init_pair( 1, COLOR_WHITE, 22 ); + init_pair( 2, COLOR_YELLOW, 22 ); + } + else + { + init_pair( 1, COLOR_WHITE, COLOR_BLUE ); + init_pair( 2, COLOR_YELLOW, COLOR_BLUE ); + } + + color_set( 1, NULL ); + // bkgd(COLOR_PAIR(1)); + // attrset(COLOR_PAIR(1) | A_BOLD); + + char title[80]; + + sprintf( title, "LoRa Habitat and SSDV Gateway by M0RPI, M0RJX - " VERSION); + + // Title bar + mvaddstr( 0, ( 80 - strlen( title ) ) / 2, title ); + refresh( ); + + // Windows for LoRa live data + for ( Channel = 0; Channel <= 1; Channel++ ) + { + Config.LoRaDevices[Channel].Window = + newwin( 14, 38, 1, Channel ? 41 : 1 ); + wbkgd( Config.LoRaDevices[Channel].Window, COLOR_PAIR( 2 ) ); + + // wcolor_set(Config.LoRaDevices[Channel].Window, 2, NULL); + // waddstr(Config.LoRaDevices[Channel].Window, "WINDOW"); + // mvwaddstr(Config.LoRaDevices[Channel].Window, 0, 0, "Window"); + wrefresh( Config.LoRaDevices[Channel].Window ); + } + + curs_set( 0 ); + + return mainwin; +} + +void +CloseDisplay( WINDOW * mainwin ) +{ + /* Clean up after ourselves */ + delwin( mainwin ); + endwin( ); + refresh( ); +} + + +uint16_t +CRC16( unsigned char *ptr ) +{ + uint16_t CRC; + int j; + + CRC = 0xffff; // Seed + + for ( ; *ptr; ptr++ ) + { // For speed, repeat calculation instead of looping for each bit + CRC ^= ( ( ( unsigned int ) *ptr ) << 8 ); + for ( j = 0; j < 8; j++ ) + { + if ( CRC & 0x8000 ) + CRC = ( CRC << 1 ) ^ 0x1021; + else + CRC <<= 1; + } + } + + return CRC; +} + +void +ProcessKeyPress( int ch ) +{ + int Channel = 0; + + /* shifted keys act on channel 1 */ + if ( ch >= 'A' && ch <= 'Z' ) + { + Channel = 1; + /* change from upper to lower case */ + ch += ( 'a' - 'A' ); + } + + if ( ch == 'q' ) + { + run = FALSE; + return; + } + + /* ignore if channel is not in use */ + if ( !Config.LoRaDevices[Channel].InUse ) + { + return; + } + + switch ( ch ) + { + case 'f': + Config.LoRaDevices[Channel].AFC = + !Config.LoRaDevices[Channel].AFC; + ChannelPrintf( Channel, 11, 24, "%s", + Config.LoRaDevices[Channel].AFC ? "AFC" : " " ); + break; + case 'a': + ReTune( Channel, 0.1 ); + break; + case 'z': + ReTune( Channel, -0.1 ); + break; + case 's': + ReTune( Channel, 0.01 ); + break; + case 'x': + ReTune( Channel, -0.01 ); + break; + case 'd': + ReTune( Channel, 0.001 ); + break; + case 'c': + ReTune( Channel, -0.001 ); + break; + default: + //LogMessage("KeyPress %d\n", ch); + return; + } +} + +int +prog_count( char *name ) +{ + DIR *dir; + struct dirent *ent; + char buf[512]; + long pid; + char pname[100] = { 0, }; + char state; + FILE *fp = NULL; + int Count = 0; + + if ( !( dir = opendir( "/proc" ) ) ) + { + perror( "can't open /proc" ); + return 0; + } + + while ( ( ent = readdir( dir ) ) != NULL ) + { + long lpid = atol( ent->d_name ); + if ( lpid < 0 ) + continue; + snprintf( buf, sizeof( buf ), "/proc/%ld/stat", lpid ); + fp = fopen( buf, "r" ); + + if ( fp ) + { + if ( ( fscanf( fp, "%ld (%[^)]) %c", &pid, pname, &state ) ) != + 3 ) + { + printf( "fscanf failed \n" ); + fclose( fp ); + closedir( dir ); + return 0; + } + + if ( !strcmp( pname, name ) ) + { + Count++; + } + fclose( fp ); + } + } + + closedir( dir ); + + return Count; +} + +int +GetTextMessageToUpload( int Channel, char *Message ) +{ + DIR *dp; + struct dirent *ep; + int Result; + + Result = 0; + + LogMessage("Checking for SMS file ...\n"); + + if ( Config.SMSFolder[0] ) + { + dp = opendir( Config.SMSFolder ); + if ( dp != NULL ) + { + while ( ( ep = readdir( dp ) ) && !Result ) + { + if ( strstr( ep->d_name, ".sms" ) ) + { + FILE *fp; + char Line[256], FileName[256]; + int FileNumber; + + sprintf( FileName, "%s%s", Config.SMSFolder, ep->d_name ); + sscanf( ep->d_name, "%d", &FileNumber ); + + if ( ( fp = fopen( FileName, "rt" ) ) != NULL ) + { + if ( fscanf( fp, "%[^\r]", Line ) ) + { + // #001,@daveake: Good Luck Tim !!\n + // @jonathenharty: RT @ProjectHeT: #astroPiTest The Essex Space Agency is looking forward to tweeting the real @esa! @GallarottiA: RT @ProjectHeT: #astroPiTest The Essex Space Agency is looking forward to tweeting the real @esa! + sprintf( Message, "#%d,%s\n", FileNumber, Line ); + + LogMessage( "UPLINK: %s", Message ); + Result = 1; + } + else + { + LogMessage( "FAIL\n" ); + } + fclose( fp ); + } + } + } + closedir( dp ); + } + } + + return Result; +} + +int +GetExternalListOfMissingSSDVPackets( int Channel, char *Message ) +{ + // First, create request file + FILE *fp; + + // LogMessage("GetExternalListOfMissingSSDVPackets()\n"); + + // if ((fp = fopen("get_list.txt", "wt")) != NULL) + { + int i; + + // fprintf(fp, "No Message\n"); + // fclose(fp); + + // LogMessage("File created\n"); + + // Now wait for uplink.txt file to appear. + // Timeout before the end of our Tx slot if no file appears + + for ( i = 0; i < 20; i++ ) + { + if ( ( fp = fopen( "uplink.txt", "r" ) ) ) + { + Message[0] = '\0'; + fgets( Message, 256, fp ); + + fclose( fp ); + + LogMessage( "Got uplink.txt %d bytes\n", strlen( Message ) ); + + // remove("get_list.txt"); + remove( "uplink.txt" ); + + return strlen( Message ); + } + + usleep( 100000 ); + } + + // LogMessage("Timed out waiting for file\n"); + // remove("get_list.txt"); + } + + return 0; +} + + +void SendUplinkMessage( int Channel ) +{ + char Message[512]; + + // Decide what type of message we need to send + if ( GetTextMessageToUpload( Channel, Message ) ) + { + SendLoRaData( Channel, Message, 255 ); + } + else if ( GetExternalListOfMissingSSDVPackets( Channel, Message ) ) + { + SendLoRaData( Channel, Message, 255 ); + } +} + +void +rjh_post_message( int Channel, char *buffer ) +{ + if ( Config.LoRaDevices[Channel].Sending ) + { + Config.LoRaDevices[Channel].Sending = 0; + // LogMessage("Ch%d: End of Tx\n", Channel); + + setLoRaMode( Channel ); + SetDefaultLoRaParameters( Channel ); + startReceiving( Channel ); + } + else + { + int Bytes; + char Message[257]; + + memcpy( Message + 1, buffer, 256 ); + + // hexdump_buffer ("RJH Raw Data", Message, 257); + + Bytes = strlen( buffer ); + + if ( Bytes > 0 ) + { + if ( Config.LoRaDevices[Channel].ActivityLED >= 0 ) + { + digitalWrite( Config.LoRaDevices[Channel].ActivityLED, 1 ); + LEDCounts[Channel] = 5; + } + + if ( Message[1] == '!' ) + { + ProcessUploadMessage( Channel, Message + 1 ); + } + else if ( Message[1] == '^' ) + { + ProcessCallingMessage( Channel, Message + 1 ); + } + else if ( Message[1] == '$' ) + { + //LogMessage("Ch %d: Uploaded message %s\n", Channel, Message+1); + ProcessTelemetryMessage( Channel, Message + 1 ); + } + else if ( Message[1] == '>' ) + { + LogMessage( "Flight Controller message %d bytes = %s", Bytes, + Message + 1 ); + } + else if ( Message[1] == '*' ) + { + LogMessage( "Uplink Command message %d bytes = %s", Bytes, + Message + 1 ); + } + else if ( Message[1] == 0x66 || Message[1] == 0x68 ) + { + ProcessSSDVMessage( Channel, Message ); + } + else + { + LogMessage( "Unknown packet type is %02Xh, RSSI %d\n", + Message[1], readRegister( Channel, + REG_PACKET_RSSI ) - + 157 ); + ChannelPrintf( Channel, 3, 1, "Unknown Packet %d, %d bytes", + Message[0], Bytes ); + Config.LoRaDevices[Channel].UnknownCount++; + } + + Config.LoRaDevices[Channel].LastPacketAt = time( NULL ); + + if ( Config.LoRaDevices[Channel].InCallingMode + && ( Config.CallingTimeout > 0 ) ) + { + Config.LoRaDevices[Channel].ReturnToCallingModeAt = + time( NULL ) + Config.CallingTimeout; + } + + ShowPacketCounts( Channel ); + } + } +} + +int +main( int argc, char **argv ) +{ + int ch; + int LoopPeriod; + int Channel; + pthread_t SSDVThread, FTPThread, NetworkThread, HabitatThread, + ServerThread; + WINDOW *mainwin; + + if ( prog_count( "gateway" ) > 1 ) + { + printf( "\nThe gateway program is already running!\n\n" ); + exit( 1 ); + } + + curl_global_init( CURL_GLOBAL_ALL ); // RJH thread safe + + mainwin = InitDisplay( ); + + // Settings for character input + noecho( ); + cbreak( ); + nodelay( stdscr, TRUE ); + keypad( stdscr, TRUE ); + + LEDCounts[0] = 0; + LEDCounts[1] = 0; + + // Remove any old SSDV files + // system("rm -f /tmp/*.bin"); + + LoadConfigFile(); + LoadPayloadFiles( ); + + int result; + + result = pipe( telem_pipe_fd ); + if ( result < 0 ) + { + fprintf( stderr, "Error creating telemetry pipe\n" ); + return 1; + } + + result = pipe( ssdv_pipe_fd ); + if ( result < 0 ) + { + fprintf( stderr, "Error creating ssdv pipe\n" ); + return 1; + } + + if ( wiringPiSetup( ) < 0 ) + { + fprintf( stderr, "Failed to open wiringPi\n" ); + exit( 1 ); + } + + if ( Config.LoRaDevices[0].ActivityLED >= 0 ) + pinMode( Config.LoRaDevices[0].ActivityLED, OUTPUT ); + if ( Config.LoRaDevices[1].ActivityLED >= 0 ) + pinMode( Config.LoRaDevices[1].ActivityLED, OUTPUT ); + if ( Config.InternetLED >= 0 ) + pinMode( Config.InternetLED, OUTPUT ); + if ( Config.NetworkLED >= 0 ) + pinMode( Config.NetworkLED, OUTPUT ); + + setupRFM98( 0 ); + setupRFM98( 1 ); + + ShowPacketCounts( 0 ); + ShowPacketCounts( 1 ); + + + LoopPeriod = 0; + + // Initialise the vars + stsv.parent_status = RUNNING; + stsv.packet_count = 0; + + if ( pthread_create( &SSDVThread, NULL, SSDVLoop, ( void * ) &stsv ) ) + { + fprintf( stderr, "Error creating SSDV thread\n" ); + return 1; + } + + if ( pthread_create( &FTPThread, NULL, FTPLoop, NULL ) ) + { + fprintf( stderr, "Error creating FTP thread\n" ); + return 1; + } + + + // Initialise the vars + htsv.parent_status = RUNNING; + htsv.packet_count = 0; + + + if ( pthread_create + ( &HabitatThread, NULL, HabitatLoop, ( void * ) &htsv ) ) + { + fprintf( stderr, "Error creating Habitat thread\n" ); + return 1; + } + + // RJH close (telem_pipe_fd[0]); // Close the read side of the pipe as we are writing here + + if ( Config.ServerPort > 0 ) + { + if ( pthread_create( &ServerThread, NULL, ServerLoop, NULL ) ) + { + fprintf( stderr, "Error creating server thread\n" ); + return 1; + } + } + + if ( ( Config.NetworkLED >= 0 ) && ( Config.InternetLED >= 0 ) ) + { + if ( pthread_create( &NetworkThread, NULL, NetworkLoop, NULL ) ) + { + fprintf( stderr, "Error creating Network thread\n" ); + return 1; + } + } + + if ( ( Config.latitude > -90 ) && ( Config.longitude > -90 ) ) + { + UploadListenerTelemetry( Config.Tracker, Config.latitude, + Config.longitude, Config.antenna ); + } + + char buffer[300]; + char ssdv_buff[257]; + int message_count = 0; + + char fileName[20] = "telem.txt"; + FILE *file_telem = fopen( fileName, "r" ); + + char fileName_ssdv[20] = "ssdv.bin"; + FILE *file_ssdv = fopen( fileName_ssdv, "rb" ); + + LogMessage( "Starting now ...\n" ); + + while ( run ) // && message_count< 9) // RJH Used for debug + { + if ( ( ch = getch( ) ) != ERR ) + { + ProcessKeyPress( ch ); + } + + /* RJH TEST */ + if ( message_count % 10 == 9 ) + { + if ( file_telem ) + { + if ( fgets( buffer, sizeof( buffer ), file_telem ) ) + { + rjh_post_message( 1, buffer ); + } + } + message_count++; // We need to increment this here or we will lock + } + else + { + if ( file_ssdv ) + { + if ( fread( ssdv_buff, 256, 1, file_ssdv ) ) + { + ssdv_buff[256] = '\0'; + rjh_post_message( 1, &ssdv_buff[1] ); + } + } + message_count++; // We need to increment this here or we will lock + } + + + /* RJH TEST */ + if ( LoopPeriod > 1000 ) + { + // Every 1 second + time_t now; + struct tm *tm; + + now = time( 0 ); + tm = localtime( &now ); + + LoopPeriod = 0; + + + for ( Channel = 0; Channel <= 1; Channel++ ) + { + if ( Config.LoRaDevices[Channel].InUse ) + { + + ShowPacketCounts( Channel ); + + ChannelPrintf( Channel, 12, 1, "Current RSSI = %4d ", + readRegister( Channel, + REG_CURRENT_RSSI ) - 157 ); + + // if (Config.LoRaDevices[Channel].LastPacketAt > 0) + // { + // ChannelPrintf(Channel, 6, 1, "%us since last packet ", (unsigned int)(time(NULL) - Config.LoRaDevices[Channel].LastPacketAt)); + // } + + if ( Config.LoRaDevices[Channel].InCallingMode + && ( Config.CallingTimeout > 0 ) + && ( Config. + LoRaDevices[Channel].ReturnToCallingModeAt > 0 ) + && ( time( NULL ) > + Config. + LoRaDevices[Channel].ReturnToCallingModeAt ) ) + { + Config.LoRaDevices[Channel].InCallingMode = 0; + Config.LoRaDevices[Channel].ReturnToCallingModeAt = 0; + + LogMessage( "Return to calling mode\n" ); + + setLoRaMode( Channel ); + + SetDefaultLoRaParameters( Channel ); + + setMode( Channel, RF98_MODE_RX_CONTINUOUS ); + + ChannelPrintf( Channel, 1, 1, + "Channel %d %sMHz %s mode", Channel, + Config.LoRaDevices[Channel].Frequency, + Modes[Config.LoRaDevices + [Channel].SpeedMode] ); + } + + if ( ( Config.LoRaDevices[Channel].UplinkTime > 0 ) + && ( Config.LoRaDevices[Channel].UplinkCycle > 0 ) ) + { + long CycleSeconds; + + + CycleSeconds = + ( tm->tm_hour * 3600 + tm->tm_min * 60 + + tm->tm_sec ) % + Config.LoRaDevices[Channel].UplinkCycle; + + if ( CycleSeconds == + Config.LoRaDevices[Channel].UplinkTime ) + { + // LogMessage("%02d:%02d:%02d - Time to send uplink message\n", tm->tm_hour, tm->tm_min, tm->tm_sec); + + SendUplinkMessage( Channel ); + } + } + + if ( LEDCounts[Channel] + && ( Config.LoRaDevices[Channel].ActivityLED >= 0 ) ) + { + if ( --LEDCounts[Channel] == 0 ) + { + digitalWrite( Config.LoRaDevices[Channel]. + ActivityLED, 0 ); + } + } + } + } + } + + delay( 10 ); + LoopPeriod += 10; + } + + LogMessage("Disabling DIO0 ISRs\n"); + for (Channel=0; Channel<2; Channel++) + { + if (Config.LoRaDevices[Channel].InUse) + { + wiringPiISR(Config.LoRaDevices[Channel].DIO0, INT_EDGE_RISING, &DIO_Ignore_Interrupt_0); + } + } + + LogMessage( "Closing SSDV pipe\n" ); + close( ssdv_pipe_fd[1] ); + + LogMessage( "Closing Habitat pipe\n" ); + close( telem_pipe_fd[1] ); + + LogMessage( "Stopping SSDV thread\n" ); + stsv.parent_status = STOPPED; + + LogMessage( "Stopping Habitat thread\n" ); + htsv.parent_status = STOPPED; + + LogMessage( "Waiting for SSDV thread to close ...\n" ); + pthread_join( SSDVThread, NULL ); + LogMessage( "SSDV thread closed\n" ); + + LogMessage( "Waiting for Habitat thread to close ...\n" ); + pthread_join( HabitatThread, NULL ); + LogMessage( "Habitat thread closed\n" ); + + pthread_mutex_destroy( &var ); + + // sleep (3); + + CloseDisplay( mainwin ); + curl_global_cleanup( ); // RJH thread safe + + if ( Config.NetworkLED >= 0 ) + digitalWrite( Config.NetworkLED, 0 ); + if ( Config.InternetLED >= 0 ) + digitalWrite( Config.InternetLED, 0 ); + if ( Config.LoRaDevices[0].ActivityLED >= 0 ) + digitalWrite( Config.LoRaDevices[0].ActivityLED, 0 ); + if ( Config.LoRaDevices[1].ActivityLED >= 0 ) + digitalWrite( Config.LoRaDevices[1].ActivityLED, 0 ); + + return 0; + +} diff --git a/keep/gateway.h b/keep/gateway.h new file mode 100644 index 0000000..4b9d09d --- /dev/null +++ b/keep/gateway.h @@ -0,0 +1,14 @@ +#ifndef _H_Gateway +#define _H_Gateway + +int receiveMessage( int Channel, char *message ); +void hexdump_buffer( const char *title, const char *buffer, + const int len_buffer ); +void LogPacket( int Channel, int8_t SNR, int RSSI, double FreqError, + int Bytes, unsigned char MessageType ); +void LogTelemetryPacket( char *Telemetry ); +void LogMessage( const char *format, ... ); +void ChannelPrintf( int Channel, int row, int column, const char *format, + ... ); + +#endif diff --git a/keep/global.h b/keep/global.h new file mode 100644 index 0000000..75cb1c1 --- /dev/null +++ b/keep/global.h @@ -0,0 +1,103 @@ +#include + +#define RUNNING 1 // The main program is running +#define STOPPED 0 // The main program has stopped + struct TSSDVPacket { + char Packet[256]; + char Callsign[7]; + }; + struct TSSDVPackets { + int ImageNumber; + int HighestPacket; + bool Packets[1024]; + }; + struct TLoRaDevice { + int InUse; + int DIO0; + int DIO5; + char Frequency[16]; + double activeFreq; + bool AFC; + int SpeedMode; + int Power; + int PayloadLength; + int ImplicitOrExplicit; + int ErrorCoding; + int Bandwidth; + int SpreadingFactor; + int LowDataRateOptimize; + int CurrentBandwidth; + WINDOW * Window; + unsigned int TelemetryCount, SSDVCount, BadCRCCount, UnknownCount; + int Sending; + char Telemetry[256]; + char Payload[16], Time[12]; + unsigned int Counter, LastCounter; + unsigned long Seconds; + double PredictedLongitude, PredictedLatitude; + double Longitude, Latitude; + unsigned int Altitude, PreviousAltitude; + unsigned int Satellites; + unsigned long LastPositionAt; + time_t LastPacketAt, LastSSDVPacketAt, LastTelemetryPacketAt; + float AscentRate; + time_t ReturnToCallingModeAt; + int InCallingMode; + int ActivityLED; + +double UplinkFrequency; + +int UplinkMode; + int Speed, Heading, PredictedTime, CompassActual, CompassTarget, + AirDirection, ServoLeft, ServoRight, ServoTime, FlightMode; + double cda, PredictedLandingSpeed, AirSpeed, GlideRatio; + + // Normal (non TDM) uplink + int UplinkTime; + int UplinkCycle; + + // SSDV Packet Log + struct TSSDVPackets SSDVPackets[3]; + }; + struct TConfig { + char Tracker[16]; + int EnableHabitat; + int EnableSSDV; + int EnableTelemetryLogging; + int EnablePacketLogging; + int CallingTimeout; + char SSDVJpegFolder[100]; + char ftpServer[100]; + char ftpUser[32]; + char ftpPassword[32]; + char ftpFolder[64]; + struct TLoRaDevice LoRaDevices[2]; + int NetworkLED; + int InternetLED; + int ServerPort; + float latitude, longitude; + char SMSFolder[64]; + char antenna[64]; + int EnableDev; + }; + typedef struct { + int parent_status; + unsigned long packet_count; +} thread_shared_vars_t; + +typedef struct { + short int Channel; + char Telemetry[257]; + int Packet_Number; +} telemetry_t; + +typedef struct { + short int Channel; + char SSDV_Packet[257]; + int Packet_Number; +} ssdv_t; + +extern struct TConfig Config; + extern int SSDVSendArrayIndex; + extern pthread_mutex_t ssdv_mutex; + void LogMessage( const char *format, ... ); diff --git a/keep/habitat.c b/keep/habitat.c new file mode 100644 index 0000000..e3874c1 --- /dev/null +++ b/keep/habitat.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include // Standard input/output definitions +#include // String function definitions +#include // UNIX standard function definitions +#include // File control definitions +#include // Error number definitions +#include // POSIX terminal control definitions +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base64.h" +#include "habitat.h" +#include "global.h" +#include "sha256.h" +#include "wiringPi.h" +#include "gateway.h" + +extern int telem_pipe_fd[2]; +extern pthread_mutex_t var; +extern void ChannelPrintf( int Channel, int row, int column, + const char *format, ... ); + +size_t +habitat_write_data( void *buffer, size_t size, size_t nmemb, void *userp ) +{ + // LogMessage("%s\n", (char *)buffer); + return size * nmemb; +} + +void +hash_to_hex( unsigned char *hash, char *line ) +{ + int idx; + + for ( idx = 0; idx < 32; idx++ ) + { + sprintf( &( line[idx * 2] ), "%02x", hash[idx] ); + } + line[64] = '\0'; + + // LogMessage(line); +} + +void +UploadTelemetryPacket( telemetry_t * t ) +{ + CURL *curl; + CURLcode res; + char curl_error[CURL_ERROR_SIZE]; + + /* get a curl handle */ + curl = curl_easy_init( ); + if ( curl ) + { + char url[200]; + char base64_data[1000]; + size_t base64_length; + SHA256_CTX ctx; + unsigned char hash[32]; + char doc_id[100]; + char json[1000], now[32]; + char Sentence[512]; + struct curl_slist *headers = NULL; + time_t rawtime; + struct tm *tm; + + // Get formatted timestamp + time( &rawtime ); + tm = gmtime( &rawtime ); + strftime( now, sizeof( now ), "%Y-%0m-%0dT%H:%M:%SZ", tm ); + + // So that the response to the curl PUT doesn't mess up my finely crafted display! + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, habitat_write_data ); + + // Set the timeout + curl_easy_setopt( curl, CURLOPT_TIMEOUT, 15 ); + + // RJH capture http errors and report + curl_easy_setopt( curl, CURLOPT_FAILONERROR, 1 ); + curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, curl_error ); + + // Avoid curl library bug that happens if above timeout occurs (sigh) + curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 ); + + // Grab current telemetry string and append a linefeed + sprintf( Sentence, "%s\n", t->Telemetry ); + + // Convert sentence to base64 + base64_encode( Sentence, strlen( Sentence ), &base64_length, + base64_data ); + base64_data[base64_length] = '\0'; + + // Take SHA256 hash of the base64 version and express as hex. This will be the document ID + sha256_init( &ctx ); + sha256_update( &ctx, base64_data, base64_length ); + sha256_final( &ctx, hash ); + hash_to_hex( hash, doc_id ); + + char counter[10]; + sprintf( counter, "%d", t->Packet_Number ); + + // Create json with the base64 data in hex, the tracker callsign and the current timestamp + sprintf( json, + "{\"data\": {\"_raw\": \"%s\"},\"receivers\": {\"%s\": {\"time_created\": \"%s\",\"time_uploaded\": \"%s\"}}}", + base64_data, Config.Tracker, now, now ); + + // LogTelemetryPacket(json); + + + // Set the URL that is about to receive our PUT + sprintf( url, + "http://habitat.habhub.org/habitat/_design/payload_telemetry/_update/add_listener/%s", + doc_id ); +// sprintf(url, "http://ext.hgf.com/ssdv/rjh.php"); + + // Set the headers + headers = NULL; + headers = curl_slist_append( headers, "Accept: application/json" ); + headers = + curl_slist_append( headers, "Content-Type: application/json" ); + headers = curl_slist_append( headers, "charsets: utf-8" ); + + // PUT to http://habitat.habhub.org/habitat/_design/payload_telemetry/_update/add_listener/ with content-type application/json + curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ); + curl_easy_setopt( curl, CURLOPT_URL, url ); + curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "PUT" ); + curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json ); + + // Perform the request, res will get the return code + res = curl_easy_perform( curl ); + + // Check for errors + if ( res == CURLE_OK ) + { + // LogMessage("OK\n"); + } + else + { + LogMessage( "Failed for URL '%s'\n", url ); + LogMessage( "curl_easy_perform() failed: %s\n", + curl_easy_strerror( res ) ); + LogMessage( "error: %s\n", curl_error ); + } + + // always cleanup + curl_slist_free_all( headers ); + curl_easy_cleanup( curl ); + // free(base64_data); + } + +} + + +void * +HabitatLoop( void *vars ) +{ + + if ( Config.EnableHabitat ) + { + thread_shared_vars_t *htsv; + htsv = vars; + telemetry_t t; + int packets = 0; + unsigned long total_packets = 0; + + int i = 1; + + // Keep looping until the parent quits and there are no more packets to + // send to habitat. + while ( ( htsv->parent_status == RUNNING ) || ( packets > 0 ) ) + { + + //THis is neded for some reason habitat thread has a pthread_mutex_lock set + // and this removes it + if ( i ) + { + // pthread_mutex_lock(&var); + pthread_mutex_unlock( &var ); + i = 0; + } + if ( htsv->packet_count > total_packets ) + { + packets = read( telem_pipe_fd[0], &t, sizeof( t ) ); + } + else + { + packets = 0; + // pthread_mutex_unlock(&var); + + // If we have have a rollover after processing 4294967295 packets + if ( htsv->packet_count < total_packets ) + total_packets = 0; + + } + + if ( packets ) + { + // LogMessage ("%s\n", t.Telemetry); + + ChannelPrintf( t.Channel, 6, 1, "Habitat" ); + + LogTelemetryPacket( t.Telemetry ); + + UploadTelemetryPacket( &t ); + + ChannelPrintf( t.Channel, 6, 1, " " ); + + total_packets++; + + } + } + } + + close( telem_pipe_fd[0] ); + close( telem_pipe_fd[1] ); + + LogMessage( "Habitat thread closing\n" ); + + return NULL; +} diff --git a/keep/habitat.h b/keep/habitat.h new file mode 100644 index 0000000..febfc3a --- /dev/null +++ b/keep/habitat.h @@ -0,0 +1 @@ +void *HabitatLoop( void *some_void_ptr ); diff --git a/keep/network.c b/keep/network.c new file mode 100644 index 0000000..36d483c --- /dev/null +++ b/keep/network.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include // Standard input/output definitions +#include // String function definitions +#include // UNIX standard function definitions +#include // File control definitions +#include // Error number definitions +#include // POSIX terminal control definitions +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Include WiringPi library! +#include "network.h" +#include "global.h" + +int +HaveAnIPAddress( void ) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sa; + char *addr; + int FoundAddress; + + FoundAddress = 0; + + if ( getifaddrs( &ifap ) == 0 ) + { + // Success + for ( ifa = ifap; ifa; ifa = ifa->ifa_next ) + { + if ( ifa->ifa_addr != NULL ) + { + // Family is known (which it isn't for a VPN) + if ( ifa->ifa_addr->sa_family == AF_INET ) + { + sa = ( struct sockaddr_in * ) ifa->ifa_addr; + addr = inet_ntoa( sa->sin_addr ); + if ( strcmp( addr, "127.0.0.1" ) != 0 ) + { + FoundAddress = 1; + } + } + } + } + } + + freeifaddrs( ifap ); + + return FoundAddress; +} + +int +CanSeeTheInternet( void ) +{ + struct addrinfo hints, *res; + int status, sockfd, FoundInternet; + + memset( &hints, 0, sizeof hints ); + hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_STREAM; + + if ( ( status = getaddrinfo( "google.com", "80", &hints, &res ) ) != 0 ) + { + return 0; + } + + sockfd = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); + + FoundInternet = 1; + if ( connect( sockfd, res->ai_addr, res->ai_addrlen ) == -1 ) + { + FoundInternet = 0; + } + + close( sockfd ); + + freeaddrinfo( res ); // free the linked list + + return FoundInternet; +} + +void * +NetworkLoop( void *some_void_ptr ) +{ + while ( 1 ) + { + if ( HaveAnIPAddress( ) ) + { + digitalWrite( Config.NetworkLED, 1 ); +// LogMessage("On network :-)\n"); + + if ( CanSeeTheInternet( ) ) + { + digitalWrite( Config.InternetLED, 1 ); +// LogMessage("On the internet :-)\n"); + } + else + { + digitalWrite( Config.InternetLED, 0 ); +// LogMessage("Not on internet :-(\n"); + } + } + else + { + digitalWrite( Config.NetworkLED, 0 ); +// LogMessage("No network :-(\n"); + } + + sleep( 5 ); + } +} diff --git a/keep/network.h b/keep/network.h new file mode 100644 index 0000000..4da403a --- /dev/null +++ b/keep/network.h @@ -0,0 +1 @@ +void *NetworkLoop( void *some_void_ptr ); diff --git a/keep/server.c b/keep/server.c new file mode 100644 index 0000000..4461d8d --- /dev/null +++ b/keep/server.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include // Standard input/output definitions +#include // String function definitions +#include // UNIX standard function definitions +#include // File control definitions +#include // Error number definitions +#include // POSIX terminal control definitions +#include +#include +#include +#include +#include +#include +#include +#include + +#include "server.h" +#include "global.h" + +extern bool run; +extern bool server_closed; + +void *ServerLoop( void *some_void_ptr ) +{ + int listenfd = 0, connfd = 0; + struct sockaddr_in serv_addr; + + char sendBuff[1025]; + + listenfd = socket( AF_INET, SOCK_STREAM, 0 ); + memset( &serv_addr, '0', sizeof( serv_addr ) ); + memset( sendBuff, '0', sizeof( sendBuff ) ); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl( INADDR_ANY ); + serv_addr.sin_port = htons( Config.ServerPort ); + + LogMessage( "Listening on port %d\n", Config.ServerPort ); + + if ( setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &( int ) + { + 1}, sizeof( int ) ) < 0 ) + { + LogMessage( "setsockopt(SO_REUSEADDR) failed" ); + } + + if ( bind + ( listenfd, ( struct sockaddr * ) &serv_addr, + sizeof( serv_addr ) ) < 0 ) + { + LogMessage( "Server failed errno %d\n", errno ); + exit( -1 ); + } + + listen( listenfd, 10 ); + + while ( run ) + { + int port_closed; + + connfd = accept( listenfd, ( struct sockaddr * ) NULL, NULL ); + + LogMessage( "Connected to client\n" ); + + for ( port_closed = 0; !port_closed; ) + { + int Channel; + // Build json + // sprintf(sendBuff, "{\"class\":\"POSN\",\"time\":\"12:34:56\",\"lat\":54.12345,\"lon\":-2.12345,\"alt\":169}\r\n"); + + for (Channel=0; Channel<=1; Channel++) + { + if ( Config.EnableDev ) + { + sprintf(sendBuff, "{\"class\":\"POSN\",\"index\":%d,\"payload\":\"%s\",\"time\":\"%s\",\"lat\":%.5lf,\"lon\":%.5lf,\"alt\":%d,\"rate\":%.1lf,\"predlat\":%.5lf,\"predlon\":%.5lf,\"speed\":%d," + "\"head\":%d,\"cda\":%.2lf,\"pls\":%.1lf,\"pt\":%d,\"ca\":%d,\"ct\":%d,\"as\":%.1lf,\"ad\":%d,\"sl\":%d,\"sr\":%d,\"st\":%d,\"gr\":%.2lf,\"fm\":%d}\r\n", + Channel, + Config.LoRaDevices[Channel].Payload, + Config.LoRaDevices[Channel].Time, + Config.LoRaDevices[Channel].Latitude, + Config.LoRaDevices[Channel].Longitude, + Config.LoRaDevices[Channel].Altitude, + Config.LoRaDevices[Channel].AscentRate, + Config.LoRaDevices[Channel].PredictedLatitude, + Config.LoRaDevices[Channel].PredictedLongitude, + Config.LoRaDevices[Channel].Speed, + + Config.LoRaDevices[Channel].Heading, + Config.LoRaDevices[Channel].cda, + Config.LoRaDevices[Channel].PredictedLandingSpeed, + Config.LoRaDevices[Channel].PredictedTime, + Config.LoRaDevices[Channel].CompassActual, + Config.LoRaDevices[Channel].CompassTarget, + Config.LoRaDevices[Channel].AirSpeed, + Config.LoRaDevices[Channel].AirDirection, + Config.LoRaDevices[Channel].ServoLeft, + Config.LoRaDevices[Channel].ServoRight, + Config.LoRaDevices[Channel].ServoTime, + Config.LoRaDevices[Channel].GlideRatio, + Config.LoRaDevices[Channel].FlightMode); + } + else + { + sprintf(sendBuff, "{\"class\":\"POSN\",\"index\":%d,\"payload\":\"%s\",\"time\":\"%s\",\"lat\":%.5lf,\"lon\":%.5lf,\"alt\":%d,\"rate\":%.1lf}\r\n", + Channel, + Config.LoRaDevices[Channel].Payload, + Config.LoRaDevices[Channel].Time, + Config.LoRaDevices[Channel].Latitude, + Config.LoRaDevices[Channel].Longitude, + Config.LoRaDevices[Channel].Altitude, + Config.LoRaDevices[Channel].AscentRate); + } + + if ( !run ) + { + port_closed = 1; + } + else if ( send(connfd, sendBuff, strlen(sendBuff), MSG_NOSIGNAL ) <= 0 ) + { + LogMessage( "Disconnected from client\n" ); + port_closed = 1; + } + else + { + sleep(1); + } + } + } + + close( connfd ); + } + + return NULL; +} diff --git a/keep/server.h b/keep/server.h new file mode 100644 index 0000000..44d908e --- /dev/null +++ b/keep/server.h @@ -0,0 +1,2 @@ +void *ServerLoop( void *some_void_ptr ); + diff --git a/keep/sha256.c b/keep/sha256.c new file mode 100644 index 0000000..3790b0f --- /dev/null +++ b/keep/sha256.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#include "sha256.h" + + +// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it +#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - (c)) ++b; a += c; +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + + +uint32_t k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, + 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, + 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, + 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, + 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, + 0xbef9a3f7, 0xc67178f2 +}; + + +void +sha256_transform( SHA256_CTX * ctx, uint8_t data[] ) +{ + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for ( i = 0, j = 0; i < 16; ++i, j += 4 ) + m[i] = + ( data[j] << 24 ) | ( data[j + 1] << 16 ) | ( data[j + 2] << 8 ) | + ( data[j + 3] ); + for ( ; i < 64; ++i ) + m[i] = SIG1( m[i - 2] ) + m[i - 7] + SIG0( m[i - 15] ) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for ( i = 0; i < 64; ++i ) + { + t1 = h + EP1( e ) + CH( e, f, g ) + k[i] + m[i]; + t2 = EP0( a ) + MAJ( a, b, c ); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void +sha256_init( SHA256_CTX * ctx ) +{ + ctx->datalen = 0; + ctx->bitlen[0] = 0; + ctx->bitlen[1] = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void +sha256_update( SHA256_CTX * ctx, char data[], uint32_t len ) +{ + uint32_t i; + + for ( i = 0; i < len; ++i ) + { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if ( ctx->datalen == 64 ) + { + sha256_transform( ctx, ctx->data ); + DBL_INT_ADD( ctx->bitlen[0], ctx->bitlen[1], 512 ); + ctx->datalen = 0; + } + } +} + +void +sha256_final( SHA256_CTX * ctx, uint8_t hash[] ) +{ + uint32_t i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if ( ctx->datalen < 56 ) + { + ctx->data[i++] = 0x80; + while ( i < 56 ) + ctx->data[i++] = 0x00; + } + else + { + ctx->data[i++] = 0x80; + while ( i < 64 ) + ctx->data[i++] = 0x00; + sha256_transform( ctx, ctx->data ); + memset( ctx->data, 0, 56 ); + } + + // Append to the padding the total message's length in bits and transform. + DBL_INT_ADD( ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8 ); + ctx->data[63] = ctx->bitlen[0]; + ctx->data[62] = ctx->bitlen[0] >> 8; + ctx->data[61] = ctx->bitlen[0] >> 16; + ctx->data[60] = ctx->bitlen[0] >> 24; + ctx->data[59] = ctx->bitlen[1]; + ctx->data[58] = ctx->bitlen[1] >> 8; + ctx->data[57] = ctx->bitlen[1] >> 16; + ctx->data[56] = ctx->bitlen[1] >> 24; + sha256_transform( ctx, ctx->data ); + + // Since this implementation uses little endian byte ordering and SHA uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for ( i = 0; i < 4; ++i ) + { + hash[i] = ( ctx->state[0] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 4] = ( ctx->state[1] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 8] = ( ctx->state[2] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 12] = ( ctx->state[3] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 16] = ( ctx->state[4] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 20] = ( ctx->state[5] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 24] = ( ctx->state[6] >> ( 24 - i * 8 ) ) & 0x000000ff; + hash[i + 28] = ( ctx->state[7] >> ( 24 - i * 8 ) ) & 0x000000ff; + } +} diff --git a/keep/sha256.h b/keep/sha256.h new file mode 100644 index 0000000..63c4945 --- /dev/null +++ b/keep/sha256.h @@ -0,0 +1,10 @@ +typedef struct { + uint8_t data[64]; + uint32_t datalen; + uint32_t bitlen[2]; + uint32_t state[8]; + } SHA256_CTX; + void sha256_transform( SHA256_CTX * ctx, uint8_t data[] ); +void sha256_init( SHA256_CTX * ctx ); +void sha256_update( SHA256_CTX * ctx, char data[], uint32_t len ); +void sha256_final( SHA256_CTX * ctx, uint8_t hash[] ); diff --git a/keep/ssdv.c b/keep/ssdv.c new file mode 100644 index 0000000..91cc0d7 --- /dev/null +++ b/keep/ssdv.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include // Standard input/output definitions +#include // String function definitions +#include // UNIX standard function definitions +#include // File control definitions +#include // Error number definitions +#include // POSIX terminal control definitions +#include +#include +#include +#include +#include +#include +#include + +#include "urlencode.h" +#include "base64.h" +#include "ssdv.h" +#include "gateway.h" +#include "global.h" + +extern int ssdv_pipe_fd[2]; +extern pthread_mutex_t var; + +size_t +write_ssdv_data( void *buffer, size_t size, size_t nmemb, void *userp ) +{ + return size * nmemb; +} + + +void +ConvertStringToHex( unsigned char *Target, unsigned char *Source, int Length ) +{ + const char Hex[16] = "0123456789ABCDEF"; + int i; + + for ( i = 0; i < Length; i++ ) + { + *Target++ = Hex[Source[i] >> 4]; + *Target++ = Hex[Source[i] & 0x0F]; + } + + *Target++ = '\0'; +} + + +void +UploadImagePacket( ssdv_t * s, unsigned int packets ) +{ + CURL *curl; + CURLcode res; + char curl_error[CURL_ERROR_SIZE]; + char base64_data[512], json[32768], packet_json[1000]; + struct curl_slist *headers = NULL; + size_t base64_length; + char now[32]; + time_t rawtime; + struct tm *tm; + char url[250]; + + /* get a curl handle */ + curl = curl_easy_init( ); + if ( curl ) + { + + // So that the response to the curl POST doesn;'t mess up my finely crafted display! + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_ssdv_data ); + + // Set the timeout + curl_easy_setopt( curl, CURLOPT_TIMEOUT, 15 ); + + // RJH capture http errors and report + curl_easy_setopt( curl, CURLOPT_FAILONERROR, 1 ); + curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, curl_error ); + + // Avoid curl library bug that happens if above timeout occurs (sigh) + curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 ); + + // Get formatted timestamp + time( &rawtime ); + tm = gmtime( &rawtime ); + strftime( now, sizeof( now ), "%Y-%0m-%0dT%H:%M:%SZ", tm ); + + int PacketIndex; + + // Create json with the base64 data in hex, the tracker callsign and the current timestamp + strcpy( json, "{\"type\": \"packets\",\"packets\":[" ); + + for ( PacketIndex = 0; PacketIndex < packets; PacketIndex++ ) + { + base64_encode( s[PacketIndex].SSDV_Packet, 256, &base64_length, + base64_data ); + base64_data[base64_length] = '\0'; + + sprintf( packet_json, + "{\"type\": \"packet\", \"packet\": \"%s\", \"encoding\": \"base64\", \"received\": \"%s\", \"receiver\": \"%s\"}%s", + base64_data, now, Config.Tracker, + PacketIndex == ( packets - 1 ) ? "" : "," ); + strcat( json, packet_json ); + } + strcat( json, "]}" ); + + // LogTelemetryPacket(json); + + strcpy( url, "http://ssdv.habhub.org/api/v0/packets" ); + // strcpy(url,"http://ext.hgf.com/ssdv/rjh.php"); + // strcpy(url,"http://ext.hgf.com/ssdv/apiv0.php?q=packets"); + + // Set the headers + headers = NULL; + headers = curl_slist_append( headers, "Accept: application/json" ); + headers = + curl_slist_append( headers, "Content-Type: application/json" ); + headers = curl_slist_append( headers, "charsets: utf-8" ); + + curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ); + curl_easy_setopt( curl, CURLOPT_URL, url ); + + curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "POST" ); + curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json ); + + // Perform the request, res will get the return code + res = curl_easy_perform( curl ); + + /* Check for errors */ + if ( res == CURLE_OK ) + { + } + else + { + LogMessage( "Failed for URL '%s'\n", url ); + LogMessage( "curl_easy_perform() failed: %s\n", + curl_easy_strerror( res ) ); + LogMessage( "error: %s\n", curl_error ); + } + + /* always cleanup */ + curl_slist_free_all( headers ); // RJH Added this from habitat.c as was missing + curl_easy_cleanup( curl ); + } +} + +void * +SSDVLoop( void *vars ) +{ + + if ( Config.EnableSSDV ) + { + const int max_packets = 51; + thread_shared_vars_t *stsv; + stsv = vars; + ssdv_t s[max_packets]; + unsigned int j = 0; + unsigned int packets = 0; + unsigned long total_packets = 0; + + // Keep looping until the parent quits and there are no more packets to + // send to ssdv. + while ( ( stsv->parent_status == RUNNING ) || ( packets > 0 ) ) + { + + if ( stsv->packet_count > total_packets ) + { + packets = read( ssdv_pipe_fd[0], &s[j], sizeof( ssdv_t ) ); + } + else + { + packets = 0; + + // If we have have a rollover after processing 4294967295 packets + if ( stsv->packet_count < total_packets ) + total_packets = 0; + + } + + if ( packets ) + { + j++; + total_packets++; + } + + if ( j == 50 || ( ( packets == 0 ) && ( j > 0 ) ) ) + { + ChannelPrintf( s[0].Channel, 6, 1, "Habitat" ); + + UploadImagePacket( s, j ); + + ChannelPrintf( s[0].Channel, 6, 1, " " ); + + j = 0; + + packets = 0; + } + + } + } + + close( ssdv_pipe_fd[0] ); + close( ssdv_pipe_fd[1] ); + + LogMessage( "SSDV thread closing\n" ); + + return NULL; + +} diff --git a/keep/ssdv.h b/keep/ssdv.h new file mode 100644 index 0000000..681a300 --- /dev/null +++ b/keep/ssdv.h @@ -0,0 +1,2 @@ +void *SSDVLoop( void *some_void_ptr ); + diff --git a/keep/ssdv_resend.py b/keep/ssdv_resend.py new file mode 100644 index 0000000..9e8d179 --- /dev/null +++ b/keep/ssdv_resend.py @@ -0,0 +1,103 @@ +import urllib.parse +import urllib.request +import json +from datetime import datetime, timedelta +import sys +import os.path +import time + +def get_list_of_missing_packets(PayloadID, Minutes): + result = '' + + # url = 'http://ssdv.habhub.org/api/v0/images?callsign=PI868&from=2016-01-22T11:00:00Z&missing_packets' + time_limit = datetime.utcnow() - timedelta(0,Minutes*60) # n minutes ago + url = 'http://ssdv.habhub.org/api/v0/images?callsign=' + PayloadID + '&from=' + time_limit.strftime('%Y-%m-%dT%H:%M:%SZ') + '&missing_packets' + + print("url", url) + + req = urllib.request.Request(url) + with urllib.request.urlopen(req) as response: + the_page = response.read() # content = urllib.request.urlopen(url=url, data=data).read() + # print(the_page) + + temp = the_page.decode('utf-8') + j = json.loads(temp) + # print(j) + + line = "" + for index, item in enumerate(j): + # only interested in latest 2 images + if index >= (len(j) - 2): + # only interested in images that have missing packets + if len(item['missing_packets']) > 0: + print(item['id'], item['image_id'], len(item['missing_packets'])) + print(item['missing_packets']) + pl = item['packets'] + # print("highest_packet_id = ", item['last_packet']) + first_missing_packet = -1 + last_missing_packet = -1 + missing_packets = item['missing_packets'] + [9999] + + # Header for this image + if line != "": + line = line + "," + line = line + str(item['image_id']) + ":" + str(item['last_packet']) + "=" + image_line = "" + + for mp_index, mp in enumerate(missing_packets): + if mp_index == 0: + first_missing_packet = mp + last_missing_packet = mp + + if (mp > (last_missing_packet+1)): # or (mp_index == len(item['missing_packets'])-1): + # emit section + if image_line != "": + image_line = image_line + "," + if last_missing_packet == first_missing_packet: + image_line = image_line + str(last_missing_packet) + else: + image_line = image_line + str(first_missing_packet) + "-" + str(last_missing_packet) + first_missing_packet = mp + + last_missing_packet = mp + line = line + image_line + + if line != '': + result = "!" + line + "\n" + + return result + + +if len(sys.argv) <= 1: + print ("Usage: ssdv_resend [folder]\n") + quit() + +payload_id = sys.argv[1] + +if len(sys.argv) >= 3: + folder = sys.argv[2] +else: + folder = './' + +print('Payload = ' + payload_id) +print('Folder = ' + folder) + +while True: + # if os.path.isfile(folder + 'get_list.txt'): + if (datetime.utcnow().second < 0) or (datetime.utcnow().second > 50): + print("Checking ...") + # os.remove(folder + 'get_list.txt') + line = get_list_of_missing_packets(payload_id, 5) + if line == '': + print("No missing packets") + line = "Nothing" + else: + print("Missing Packets:", line) + with open(folder + 'uplink.txt', "w") as text_file: + if line != '': + print(line, file=text_file) + time.sleep(1) + else: + print('.',end="",flush=True) + time.sleep(1) + diff --git a/keep/urlencode.c b/keep/urlencode.c new file mode 100644 index 0000000..d44b5be --- /dev/null +++ b/keep/urlencode.c @@ -0,0 +1,71 @@ +#include +#include +#include + + +/* Converts a hex character to its integer value */ +char +from_hex( char ch ) +{ + return isdigit( ch ) ? ch - '0' : tolower( ch ) - 'a' + 10; +} + +/* Converts an integer value to its hex character*/ +char +to_hex( char code ) +{ + static char hex[] = "0123456789abcdef"; + return hex[code & 15]; +} + +/* Returns a url-encoded version of str */ +/* IMPORTANT: be sure to free() the returned string after use */ +char * +url_encode( char *str ) +{ + char *pstr = str, *buf = malloc( strlen( str ) * 3 + 1 ), *pbuf = buf; + while ( *pstr ) + { + if ( isalnum( *pstr ) || *pstr == '-' || *pstr == '_' || *pstr == '.' + || *pstr == '~' ) + *pbuf++ = *pstr; + else if ( *pstr == ' ' ) + *pbuf++ = '+'; + else + *pbuf++ = '%', *pbuf++ = to_hex( *pstr >> 4 ), *pbuf++ = + to_hex( *pstr & 15 ); + pstr++; + } + *pbuf = '\0'; + return buf; +} + +/* Returns a url-decoded version of str */ +/* IMPORTANT: be sure to free() the returned string after use */ +char * +url_decode( char *str ) +{ + char *pstr = str, *buf = malloc( strlen( str ) + 1 ), *pbuf = buf; + while ( *pstr ) + { + if ( *pstr == '%' ) + { + if ( pstr[1] && pstr[2] ) + { + *pbuf++ = from_hex( pstr[1] ) << 4 | from_hex( pstr[2] ); + pstr += 2; + } + } + else if ( *pstr == '+' ) + { + *pbuf++ = ' '; + } + else + { + *pbuf++ = *pstr; + } + pstr++; + } + *pbuf = '\0'; + return buf; +} diff --git a/keep/urlencode.h b/keep/urlencode.h new file mode 100644 index 0000000..e8fc38f --- /dev/null +++ b/keep/urlencode.h @@ -0,0 +1,2 @@ +char *url_encode( char *str ); + From 10146fd11eabd1ce2f642b02f68cba389d447194 Mon Sep 17 00:00:00 2001 From: Dave Akerman Date: Sun, 4 Sep 2016 10:50:59 +0000 Subject: [PATCH 2/2] V1.8.1 - Caption added M0RJX, Ignore CR in gateway.txt, auto trailing slash on config filepaths --- keep/README.md | 321 ----- keep/base64.c | 119 -- keep/base64.h | 8 - keep/ftp.c | 95 -- keep/ftp.h | 1 - keep/gateway-sample.txt | 58 - keep/gateway.c | 2458 --------------------------------------- keep/gateway.h | 14 - keep/global.h | 103 -- keep/habitat.c | 229 ---- keep/habitat.h | 1 - keep/network.c | 120 -- keep/network.h | 1 - keep/server.c | 137 --- keep/server.h | 2 - keep/sha256.c | 170 --- keep/sha256.h | 10 - keep/ssdv.c | 209 ---- keep/ssdv.h | 2 - keep/ssdv_resend.py | 103 -- keep/urlencode.c | 71 -- keep/urlencode.h | 2 - 22 files changed, 4234 deletions(-) delete mode 100644 keep/README.md delete mode 100644 keep/base64.c delete mode 100644 keep/base64.h delete mode 100644 keep/ftp.c delete mode 100644 keep/ftp.h delete mode 100644 keep/gateway-sample.txt delete mode 100644 keep/gateway.c delete mode 100644 keep/gateway.h delete mode 100644 keep/global.h delete mode 100644 keep/habitat.c delete mode 100644 keep/habitat.h delete mode 100644 keep/network.c delete mode 100644 keep/network.h delete mode 100644 keep/server.c delete mode 100644 keep/server.h delete mode 100644 keep/sha256.c delete mode 100644 keep/sha256.h delete mode 100644 keep/ssdv.c delete mode 100644 keep/ssdv.h delete mode 100644 keep/ssdv_resend.py delete mode 100644 keep/urlencode.c delete mode 100644 keep/urlencode.h diff --git a/keep/README.md b/keep/README.md deleted file mode 100644 index 790b2e2..0000000 --- a/keep/README.md +++ /dev/null @@ -1,321 +0,0 @@ -Part of the LoRa Balloon Tracking System - -Runs on a Raspberry Pi with 1 or 2 RFM98HW modules attached to the SPI port. -Also works with other compatible HopeRF and Semtec LoRa devices. - -Connections -=========== - -If you're making your own board for the Pi, connect the LoRa module(s) like so: - - LORA PI - ---- -- - 3.3V 3.3V Power - GND Ground - MOSI MOSI (pin 19) - MISO MISO (pin 21) - NSS CE0 (pin 24) (CE1 (pin 26) for 2nd module) - SCK SLCK - DIO0 Wiring Pi 31 (Pin 28) (Wiring Pi 6 (pin 22) for 2nd module) - DIO5 Wiring Pi 26 (Pin 32) (Wiring Pi 5 (pin 18) for 2nd module) - - -Installation -============ - -Enable SPI in raspi-config. - -Install WiringPi: - - 1. cd ~ - 2. git clone git://git.drogon.net/wiringPi - 3. cd wiringPi - 4. ./build - -Install the curl library: - - sudo apt-get install libcurl4-openssl-dev - -Install the ncurses library - - sudo apt-get install libncurses5-dev - -Install the LoRa gateway - - 1. cd ~ - 2. git clone https://github.com/PiInTheSky/lora-gateway.git - 3. cd lora-gateway - 4. make - 5. cp gateway-sample.txt gateway.txt - - - -Configuration -============= - -The configuration is in the file gateway.txt. Example: - - tracker=M0RPI - EnableHabitat=N - EnableSSDV=Y - LogTelemetry=Y - LogPackets=Y - CallingTimeout=60 - JPGFolder=ssdv - CallingTimeout=60 - ServerPort=6004 - Latitude=51.95023 - Longitude=-2.5445 - Antenna=868MHz Yagi - - frequency_0=434.347 - mode_0=1 - DIO0_0=31 - DIO5_0=26 - AFC_0=N - - frequency_1=434.475 - mode_1=5 - DIO0_1=6 - DIO5_1=5 - AFC_1=Y - -The global options are: - - tracker=. This is whatever callsign you want to appear as on the tracking map and/or SSDV page. - - EnableHabitat=. Enables uploading of telemetry packets to Habitat. - - EnableSSDV=. Enables uploading of SSDV image packets to the SSDV server. - - JPGFolder=. Tells the gateway where to save local JPEG files built from incoming SSDV packets. - - LogTelemetry=. Enables logging of telemetry packets (ASCII only at present) to telemetry.txt. - - LogPackets=. Enables logging of packet information (SNR, RSSI, length, type) to packets.txt. - - SMSFolder=. Tells the gateway to check for incoming SMS messages or tweets that should be sent to the tracker via the uplink. - - CallingTimeout=. Sets a timeout for returning to calling mode after a period with no received packets. - - ServerPort=. Opens a server socket which can have 1 client connected. Sends JSON telemetry and status information to that client. - - Latitude= - Longitude=. These let you tell the gateway your position, for uploading to habitat, so your listener icon appears on the map in the correct position. - Antenna=. Lets you specify your antenna make/model or type. This appears on the map if your listener icon is clicked on. - - NetworkLED= - InternetLED= - ActivityLED_0= - ActivityLED_1=. These are used for LED status indicators. Useful for packaged gateways that don't have a monitor attached. - - -and the channel-specific options are: - - frequency_=. This sets the frequency for LoRa module (0 for first, 1 for second). e.g. frequency_0=434.450 - - AFC_=. Enables or disables automatic frequency control (retunes by the frequency error of last received packet). - - mode_=. Sets the "mode" for the selected LoRa module. This offers a simple way of setting the various - LoRa parameters (SF etc.) in one go. The modes are: - - 0 = (normal for telemetry) Explicit mode, Error coding 4:8, Bandwidth 20.8kHz, SF 11, Low data rate optimize on - 1 = (normal for SSDV) Implicit mode, Error coding 4:5, Bandwidth 20.8kHz, SF 6, Low data rate optimize off - 2 = (normal for repeater) Explicit mode, Error coding 4:8, Bandwidth 62.5kHz, SF 8, Low data rate optimize off - 3 = (normal for fast SSDV) Explicit mode, Error coding 4:6, Bandwidth 250kHz, SF 7, Low data rate optimize off - 4 = Test mode not for normal use. - 5 = (normal for calling mode) Explicit mode, Error coding 4:8, Bandwidth 41.7kHz, SF 11, Low data rate optimize off - - SF_= e.g. SF_0=7 - - Bandwidth_=. e.g. Bandwidth_0=41K7. Options are 7K8, 10K4, 15K6, 20K8, 31K25, 41K7, 62K5, 125K, 250K, 500K - - Implicit_=. e.g. Implicit_0=Y - - Coding_=. e.g. Coding_0=5 (4:5) - - lowopt_=. Enables or disables low data rate optimization. - - power_=. This is the power setting used for uplinks. Refer to the LoRa manual for details on setting this. ** Only set values that are legal in your location (for EU see IR2030) ** - - UplinkTime_0=. When to send any uplink messages, measured as seconds into each cycle. - - UplinkCycle_0=. Cycle time for uplinks. First cycle starts at 00:00:00. So for uplink time=2 and cycle=30, any transmissions will start at 2 and 32 seconds after each minute. - -Lines are commented out with "#" at the start. - -If the frequency_n line is commented out, then that channel is disabled. - - -Uplinks -======= - -The gateway can uplink messages to the tracker. Currently this is restricted to time-based uplink slots using "UplinkTime" and "UplinkCycle". - -The code uses Linux system time, so the gateway should ideally be using a GPS receiver the GPSD daemon. NTP may prove sufficient however. - -For uplinks to work, both UplinkTime and UplinkCycle have to be set for the appropriate channel. - -There are currently two types of uplink supported: - - - Uplink of messages from the "SMSFolder" folder. For this to work, "SMSFolder" has to be defined and present. The gateway will then check for "*.sms" files in that folder. - - Uplink of SSD packet re-send requests. The gateway looks for an "uplink.txt" file in the gateway folder. The file is created by an external Python script (supplied) which interrogates the SSDV server. - - -Calling Mode -============ - -It is possible for trackers to send out messages on a special "calling channel" as well as telemetry on their main frequency. The calling channel messages state the main frequency and LoRa modes. - -This allows for gateways tp be normally left on the calling channel, so they then switch to each tracker as it comes within range. - -There's nothing special about "calling mode" except that after a period (CallingTimeout seconds) of time without packets, the gateway returns to its default settings. - -There is no current standard claling channel. - - -Use -=== - -Run with: - - sudo ./gateway - - -Display -======= - -The display has a title bar at the top, scrolling log at the bottom, and 2 channel panels in the middle. Each panel shows something like: - - Channel 0 869.8500MHz - Explicit, 250k, SF7, EC4:6 - Telemetry 74 bytes - 51.95028, -2.54443, 00138 - Habitat SSDV 0000 - 0s since last packet - Telem Packets = 37 - Image Packets = 0 - Bad CRC = 0 Bad Type = 0 - Packet SNR = 10, RSSI = -67 - Freq. Error = 1.0kHz - Current RSSI = -64 - -The "Habitat" text appears during uploads to habitat. Normally it will flash up then disappear quickly; if it stays on (not flickering) then the upload is slow. - -The "SSDV 0000" text shows the current state of the SSDV upload buffers. There are 4 upload threads and each can handle up to 16 (0-9-A-F) packets in its queue. -Normally, even with fast SSDV, uplinks should happen quickly enough for there to be no more than 1 or 2 active threads each with 1 packet being uploaded. - - -Interactive Features -==================== - -The following key presses are available. Where appropriate unshifted keys affect Channel 0 and shifted keys affect Channel 1. -Many thanks to David Brooke for coding this feature and the AFC. - - q quit - - a increase frequency by 100kHz - z decrease frequency by 100kHz - s increase frequency by 10kHz - x decrease frequency by 10kHz - d increase frequency by 1kHz - c decrease frequency by 1kHz - - f toggle AFC - -Change History -============== - -03/09/2016 - V1.8 ------------------ - - Add configuration of uplink frequency, mode and power - LoRa modes now in array so easier to add new ones - Added LoRa mode for uplink - Re-instated logging to telemetry.txt - Fixed pipe errors which happened if packets arrived during program exit - Merged in changes to JSON format - Sends data both LoRa channels in JSON - Config disables CE0 by default (most cards have CE1 only) - Fixed typos in gateway-sample.txt - Accept new SSDV types - - -25/08/2016 - V1.7 ------------------ - - Robert Harrison (RJH) has made numerous changes. - - Highlights include :- - - Changed makefile to include -Wall and fixed all warnings generated - Added pipes for Inter-Process Communication - Moved none thread safe curl funtions from threads and into main() - Added reporting of curl errors to habitat and ssdv threads - Changed color to green but requires 256 color support in your terminal - - For putty users please set your terminal as shown - -![Alt text](http://i.imgur.com/B81bvEQ.png "Putty config") - - when you are connected to your pi - - # echo $TERM # should show something with 256 in - - or - - # tpu colors # Should show 256 - - -27/06/2016 - V1.6 ------------------ - - Single SSDV upload thread using new API to upload multiple packets at once - Fixed 100% CPU (SSDV thread not sleeping) - - -23/05/2016 - V1.5 ------------------ - - Better status screen - -13/05/2016 ----------- - - Bug fux to local conversion of large SSDV images - Added packet logging - - -04/04/2016 ----------- - - SSDV 8 buffers - JSON feed instead of old "transition" method - - -19/02/2016 ----------- - - Fixed listener_information JSON - Added antenna setting to gateway.txt - - -16/02/2016 ----------- - - JSON telemetry feed via a server port - JPEGFolder setting - Uplink of text messages to tracker (e.g. accepted from Twitter by an external script) - Uplink of SSDV re-send requests. These requests are built by an external Python script. - Separate thread for uploading latest telemetry to habitat - 4 separate threads for uploading SSDV packets to the SSDV server - Slightly different display layout, with extra information - - -07/10/2015 ----------- - - fsphil: Tidied up compiler warnings, makefile, file permissions - - - diff --git a/keep/base64.c b/keep/base64.c deleted file mode 100644 index 5383f30..0000000 --- a/keep/base64.c +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include - - -static char encoding_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/' -}; - -static char *decoding_table = NULL; -static int mod_table[] = { 0, 2, 1 }; - - -char * -base64_encode( const char *data, - size_t input_length, - size_t * output_length, char *encoded_data ) -{ - - int i, j; - - *output_length = 4 * ( ( input_length + 2 ) / 3 ); - - // char *encoded_data = malloc(*output_length); - // if (encoded_data == NULL) return NULL; - - for ( i = 0, j = 0; i < input_length; ) - { - uint32_t octet_a = i < input_length ? ( unsigned char ) data[i++] : 0; - uint32_t octet_b = i < input_length ? ( unsigned char ) data[i++] : 0; - uint32_t octet_c = i < input_length ? ( unsigned char ) data[i++] : 0; - - uint32_t triple = ( octet_a << 0x10 ) + ( octet_b << 0x08 ) + octet_c; - - encoded_data[j++] = encoding_table[( triple >> 3 * 6 ) & 0x3F]; - encoded_data[j++] = encoding_table[( triple >> 2 * 6 ) & 0x3F]; - encoded_data[j++] = encoding_table[( triple >> 1 * 6 ) & 0x3F]; - encoded_data[j++] = encoding_table[( triple >> 0 * 6 ) & 0x3F]; - } - - for ( i = 0; i < mod_table[input_length % 3]; i++ ) - encoded_data[*output_length - 1 - i] = '='; - - return encoded_data; -} - - -void -build_decoding_table( ) -{ - - int i; - - decoding_table = malloc( 256 ); - - for ( i = 0; i < 64; i++ ) - decoding_table[( unsigned char ) encoding_table[i]] = i; -} - -char * -base64_decode( const char *data, size_t input_length, size_t * output_length ) -{ - int i, j; - - if ( decoding_table == NULL ) - build_decoding_table( ); - - if ( input_length % 4 != 0 ) - return NULL; - - *output_length = input_length / 4 * 3; - if ( data[input_length - 1] == '=' ) - ( *output_length )--; - if ( data[input_length - 2] == '=' ) - ( *output_length )--; - - char *decoded_data = malloc( *output_length ); - if ( decoded_data == NULL ) - return NULL; - - for ( i = 0, j = 0; i < input_length; ) - { - - uint32_t sextet_a = - data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; - uint32_t sextet_b = - data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; - uint32_t sextet_c = - data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; - uint32_t sextet_d = - data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]]; - - uint32_t triple = ( sextet_a << 3 * 6 ) - + ( sextet_b << 2 * 6 ) - + ( sextet_c << 1 * 6 ) + ( sextet_d << 0 * 6 ); - - if ( j < *output_length ) - decoded_data[j++] = ( triple >> 2 * 8 ) & 0xFF; - if ( j < *output_length ) - decoded_data[j++] = ( triple >> 1 * 8 ) & 0xFF; - if ( j < *output_length ) - decoded_data[j++] = ( triple >> 0 * 8 ) & 0xFF; - } - - return decoded_data; -} - - - -void -base64_cleanup( ) -{ - free( decoding_table ); -} diff --git a/keep/base64.h b/keep/base64.h deleted file mode 100644 index bd1bd2a..0000000 --- a/keep/base64.h +++ /dev/null @@ -1,8 +0,0 @@ -#include - -void build_decoding_table( ); -char *base64_encode( const char *data, size_t input_length, - size_t * output_length, char *encoded_data ); -char *base64_decode( const char *data, size_t input_length, - size_t * output_length ); -void base64_cleanup( ); diff --git a/keep/ftp.c b/keep/ftp.c deleted file mode 100644 index 07627a0..0000000 --- a/keep/ftp.c +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include -#include -#include -#include // Standard input/output definitions -#include // String function definitions -#include // UNIX standard function definitions -#include // File control definitions -#include // Error number definitions -#include // POSIX terminal control definitions -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "global.h" - -void -ConvertFile( char *FileName ) -{ - char TargetFile[100], CommandLine[200], *ptr; - - strcpy( TargetFile, FileName ); - ptr = strchr( TargetFile, '.' ); - if ( ptr && Config.SSDVJpegFolder[0] ) - { - *ptr = '\0'; - strcat( TargetFile, ".JPG" ); - - // Now convert the file - // LogMessage("Converting %s to %s\n", FileName, TargetFile); - - sprintf( CommandLine, - "ssdv -d /tmp/%s %s/%s 2> /dev/null > /dev/null", FileName, - Config.SSDVJpegFolder, TargetFile ); - // LogMessage("COMMAND %s\n", CommandLine); - system( CommandLine ); - - if ( Config.ftpServer[0] && Config.ftpUser[0] - && Config.ftpPassword[0] ) - { - // Upload to ftp server - sprintf( CommandLine, - "curl -T %s %s -Q \"TYPE I\" --user %s:%s 2> /dev/null > /dev/null", - TargetFile, Config.ftpServer, Config.ftpUser, - Config.ftpPassword ); - system( CommandLine ); - } - } -} - - -void * -FTPLoop( void *some_void_ptr ) -{ - while ( 1 ) - { - DIR *dp; - struct dirent *ep; - struct stat st; - char *SSDVFolder; - char FileName[100], TempName[100]; - - SSDVFolder = "/tmp"; - - dp = opendir( SSDVFolder ); - if ( dp != NULL ) - { - while ( ( ep = readdir( dp ) ) ) - { - if ( strstr( ep->d_name, ".bin" ) != NULL ) - { - sprintf( FileName, "%s/%s", SSDVFolder, ep->d_name ); - stat( FileName, &st ); - // LogMessage("Age of '%s' is %ld seconds\n", FileName, time(0) - st.st_mtime); - if ( ( time( 0 ) - st.st_mtime ) < 20 ) - { - ConvertFile( ep->d_name ); - } - else if ( ( time( 0 ) - st.st_mtime ) > 120 ) - { - sprintf( TempName, "%s/%s", SSDVFolder, ep->d_name ); - // LogMessage("Removing %s\n", TempName); - remove( TempName ); - } - } - } - } - ( void ) closedir( dp ); - - sleep( 5 ); - } -} diff --git a/keep/ftp.h b/keep/ftp.h deleted file mode 100644 index b9cc578..0000000 --- a/keep/ftp.h +++ /dev/null @@ -1 +0,0 @@ -void *FTPLoop( void *some_void_ptr ); diff --git a/keep/gateway-sample.txt b/keep/gateway-sample.txt deleted file mode 100644 index 0c30b8f..0000000 --- a/keep/gateway-sample.txt +++ /dev/null @@ -1,58 +0,0 @@ - -##### Your details ##### - -tracker=YOUR_CALLSIGN -Latitude=0.0 -Longitude=0.0 -Antenna=Watson W-50 - - -##### Config Options ##### - -EnableHabitat=Y -EnableSSDV=Y -JPGFolder=ssdv -LogTelemetry=Y -LogPackets=Y -CallingTimeout=60 -ServerPort=6004 -#SMSFolder=./ -EnableDev=N - -NetworkLED=22 -InternetLED=23 -ActivityLED_0=21 -ActivityLED_1=29 - -##### Config CE0 ##### - -#frequency_0=434.250 -#mode_0=1 -#AFC_0=Y -#bandwidth_0=125K -#implicit_0=0 -#coding_0=5 -#sf_0=8 -#lowopt_0=0 -#power_0=255 -#DIO0_0=31 -#DIO5_0=26 -#UplinkTime_0=2 -#UplinkCycle_0=60 - - -##### Config CE1 ##### - -frequency_1=434.500 -mode_1=1 -AFC_1=Y -#bandwidth_1=125K -#implicit_1=0 -#coding_1=5 -#sf_1=8 -#lowopt_1=0 -#power_1=255 -#DIO0_1=6 -#DIO5_1=5 -#UplinkTime_1=5 -#UplinkCycle_1=60 diff --git a/keep/gateway.c b/keep/gateway.c deleted file mode 100644 index b20fbab..0000000 --- a/keep/gateway.c +++ /dev/null @@ -1,2458 +0,0 @@ -#include -#define __USE_XOPEN -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "urlencode.h" -#include "base64.h" -#include "ssdv.h" -#include "ftp.h" -#include "habitat.h" -#include "network.h" -#include "global.h" -#include "server.h" -#include "gateway.h" - -#define VERSION "V1.8.1" -bool run = TRUE; - -// RFM98 -uint8_t currentMode = 0x81; - -#define REG_FIFO 0x00 -#define REG_FIFO_ADDR_PTR 0x0D -#define REG_FIFO_TX_BASE_AD 0x0E -#define REG_FIFO_RX_BASE_AD 0x0F -#define REG_RX_NB_BYTES 0x13 -#define REG_OPMODE 0x01 -#define REG_FIFO_RX_CURRENT_ADDR 0x10 -#define REG_IRQ_FLAGS 0x12 -#define REG_PACKET_SNR 0x19 -#define REG_PACKET_RSSI 0x1A -#define REG_CURRENT_RSSI 0x1B -#define REG_DIO_MAPPING_1 0x40 -#define REG_DIO_MAPPING_2 0x41 -#define REG_MODEM_CONFIG 0x1D -#define REG_MODEM_CONFIG2 0x1E -#define REG_MODEM_CONFIG3 0x26 -#define REG_PAYLOAD_LENGTH 0x22 -#define REG_IRQ_FLAGS_MASK 0x11 -#define REG_HOP_PERIOD 0x24 -#define REG_FREQ_ERROR 0x28 -#define REG_DETECT_OPT 0x31 -#define REG_DETECTION_THRESHOLD 0x37 - -// MODES -#define RF98_MODE_RX_CONTINUOUS 0x85 -#define RF98_MODE_TX 0x83 -#define RF98_MODE_SLEEP 0x80 -#define RF98_MODE_STANDBY 0x81 - -#define PAYLOAD_LENGTH 255 - -// Modem Config 1 -#define EXPLICIT_MODE 0x00 -#define IMPLICIT_MODE 0x01 - -#define ERROR_CODING_4_5 0x02 -#define ERROR_CODING_4_6 0x04 -#define ERROR_CODING_4_7 0x06 -#define ERROR_CODING_4_8 0x08 - -#define BANDWIDTH_7K8 0x00 -#define BANDWIDTH_10K4 0x10 -#define BANDWIDTH_15K6 0x20 -#define BANDWIDTH_20K8 0x30 -#define BANDWIDTH_31K25 0x40 -#define BANDWIDTH_41K7 0x50 -#define BANDWIDTH_62K5 0x60 -#define BANDWIDTH_125K 0x70 -#define BANDWIDTH_250K 0x80 -#define BANDWIDTH_500K 0x90 - -// Modem Config 2 - -#define SPREADING_6 0x60 -#define SPREADING_7 0x70 -#define SPREADING_8 0x80 -#define SPREADING_9 0x90 -#define SPREADING_10 0xA0 -#define SPREADING_11 0xB0 -#define SPREADING_12 0xC0 - -#define CRC_OFF 0x00 -#define CRC_ON 0x04 - -// POWER AMPLIFIER CONFIG -#define REG_PA_CONFIG 0x09 -#define PA_MAX_BOOST 0x8F -#define PA_LOW_BOOST 0x81 -#define PA_MED_BOOST 0x8A -#define PA_MAX_UK 0x88 -#define PA_OFF_BOOST 0x00 -#define RFO_MIN 0x00 - -// LOW NOISE AMPLIFIER -#define REG_LNA 0x0C -#define LNA_MAX_GAIN 0x23 // 0010 0011 -#define LNA_OFF_GAIN 0x00 -#define LNA_LOW_GAIN 0xC0 // 1100 0000 - -struct TPayload { - int InUse; - char Payload[32]; -}; - -struct TLoRaMode -{ - int ImplicitOrExplicit; - int ErrorCoding; - int Bandwidth; - int SpreadingFactor; - int LowDataRateOptimize; - int BaudRate; - char *Description; -} LoRaModes[] = -{ - {EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_20K8, SPREADING_11, 8, 60, "Telemetry"}, // 0: Normal mode for telemetry - {IMPLICIT_MODE, ERROR_CODING_4_5, BANDWIDTH_20K8, SPREADING_6, 0, 1400, "SSDV"}, // 1: Normal mode for SSDV - {EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_62K5, SPREADING_8, 0, 2000, "Repeater"}, // 2: Normal mode for repeater network - {EXPLICIT_MODE, ERROR_CODING_4_6, BANDWIDTH_250K, SPREADING_7, 0, 8000, "Turbo"}, // 3: Normal mode for high speed images in 868MHz band - {IMPLICIT_MODE, ERROR_CODING_4_5, BANDWIDTH_250K, SPREADING_6, 0, 16828, "TurboX"}, // 4: Fastest mode within IR2030 in 868MHz band - {EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_41K7, SPREADING_11, 0, 200, "Calling"}, // 5: Calling mode - {IMPLICIT_MODE, ERROR_CODING_4_5, BANDWIDTH_41K7, SPREADING_6, 0, 2800, "Uplink"} // 6: Uplink mode for 868 -}; - -struct TConfig Config; -struct TPayload Payloads[16]; - -int LEDCounts[2]; -pthread_mutex_t var = PTHREAD_MUTEX_INITIALIZER; - -#pragma pack(1) - -struct TBinaryPacket { - uint8_t PayloadIDs; - uint16_t Counter; - uint16_t BiSeconds; - float Latitude; - float Longitude; - uint16_t Altitude; -}; - -const char *Modes[6] = - { "Slow", "SSDV", "Repeater", "Turbo", "TurboX", "Calling" }; - -// Create pipes for inter proces communication -// GLOBAL AS CALLED FROM INTERRRUPT -int telem_pipe_fd[2]; -int ssdv_pipe_fd[2]; - -// Create a structure to share some variables with the habitat child process -// GLOBAL AS CALLED FROM INTERRRUPT -thread_shared_vars_t htsv; - -// Create a structure to share some variables with the ssdv child process -// GLOBAL AS CALLED FROM INTERRRUPT -thread_shared_vars_t stsv; - -int habitate_telem_packets = 0; - -void -hexdump_buffer( const char *title, const char *buffer, const int len_buffer ) -{ - int i, j = 0; - char message[200]; - FILE *fp; - - fp = fopen( "pkt.txt", "a" ); - - fprintf( fp, "Title = %s\n", title ); - - for ( i = 0; i < len_buffer; i++ ) - { - sprintf( &message[3 * j], "%02x ", buffer[i] ); - j++; - if ( i % 16 == 15 ) - { - j = 0; - fprintf( fp, "%s\n", message ); - message[0] = '\0'; - } - } - fprintf( fp, "%s\n", message ); - fclose( fp ); - -} - -void -writeRegister( int Channel, uint8_t reg, uint8_t val ) -{ - unsigned char data[2]; - - data[0] = reg | 0x80; - data[1] = val; - wiringPiSPIDataRW( Channel, data, 2 ); -} - -uint8_t -readRegister( int Channel, uint8_t reg ) -{ - unsigned char data[2]; - uint8_t val; - - data[0] = reg & 0x7F; - data[1] = 0; - wiringPiSPIDataRW( Channel, data, 2 ); - val = data[1]; - - return val; -} - -void -LogPacket( int Channel, int8_t SNR, int RSSI, double FreqError, int Bytes, - unsigned char MessageType ) -{ - if ( Config.EnablePacketLogging ) - { - FILE *fp; - - if ( ( fp = fopen( "packets.txt", "at" ) ) != NULL ) - { - time_t now; - struct tm *tm; - - now = time( 0 ); - tm = localtime( &now ); - - fprintf( fp, - "%02d:%02d:%02d - Ch %d, SNR %d, RSSI %d, FreqErr %.1lf, Bytes %d, Type %02Xh\n", - tm->tm_hour, tm->tm_min, tm->tm_sec, Channel, SNR, RSSI, - FreqError, Bytes, MessageType ); - - fclose( fp ); - } - } -} - -void -LogTelemetryPacket( char *Telemetry ) -{ - // if (Config.EnableTelemetryLogging) - { - FILE *fp; - - if ( ( fp = fopen( "telemetry.txt", "at" ) ) != NULL ) - { - time_t now; - struct tm *tm; - - now = time( 0 ); - tm = localtime( &now ); - - fprintf( fp, "%02d:%02d:%02d - %s\n", tm->tm_hour, tm->tm_min, - tm->tm_sec, Telemetry ); - - fclose( fp ); - } - } -} - -void -LogMessage( const char *format, ... ) -{ - static WINDOW *Window = NULL; - char Buffer[512]; - - pthread_mutex_lock( &var ); // lock the critical section - - if ( Window == NULL ) - { - // Window = newwin(25, 30, 0, 50); - Window = newwin( LINES - 16, COLS, 16, 0 ); - scrollok( Window, TRUE ); - } - - va_list args; - va_start( args, format ); - - vsprintf( Buffer, format, args ); - - va_end( args ); - - if ( strlen( Buffer ) > COLS - 1 ) - { - Buffer[COLS - 3] = '.'; - Buffer[COLS - 2] = '.'; - Buffer[COLS - 1] = '\n'; - Buffer[COLS] = 0; - } - - waddstr( Window, Buffer ); - - wrefresh( Window ); - - pthread_mutex_unlock( &var ); // unlock once you are done - -} - -void -ChannelPrintf( int Channel, int row, int column, const char *format, ... ) -{ - char Buffer[80]; - va_list args; - - pthread_mutex_lock( &var ); // lock the critical section - - va_start( args, format ); - - vsprintf( Buffer, format, args ); - - va_end( args ); - - mvwaddstr( Config.LoRaDevices[Channel].Window, row, column, Buffer ); - - wrefresh( Config.LoRaDevices[Channel].Window ); - - pthread_mutex_unlock( &var ); // unlock once you are done - -} - -void -setMode( int Channel, uint8_t newMode ) -{ - if ( newMode == currentMode ) - return; - - switch ( newMode ) - { - case RF98_MODE_TX: - writeRegister( Channel, REG_LNA, LNA_OFF_GAIN ); // TURN LNA OFF FOR TRANSMITT - writeRegister( Channel, REG_PA_CONFIG, Config.LoRaDevices[Channel].Power ); // PA_MAX_UK - writeRegister( Channel, REG_OPMODE, newMode ); - currentMode = newMode; - break; - case RF98_MODE_RX_CONTINUOUS: - writeRegister( Channel, REG_PA_CONFIG, PA_OFF_BOOST ); // TURN PA OFF FOR RECIEVE?? - writeRegister( Channel, REG_LNA, LNA_MAX_GAIN ); // MAX GAIN FOR RECEIVE - writeRegister( Channel, REG_OPMODE, newMode ); - currentMode = newMode; - // LogMessage("Changing to Receive Continuous Mode\n"); - break; - case RF98_MODE_SLEEP: - writeRegister( Channel, REG_OPMODE, newMode ); - currentMode = newMode; - // LogMessage("Changing to Sleep Mode\n"); - break; - case RF98_MODE_STANDBY: - writeRegister( Channel, REG_OPMODE, newMode ); - currentMode = newMode; - // LogMessage("Changing to Standby Mode\n"); - break; - default: - return; - } - - if ( newMode != RF98_MODE_SLEEP ) - { - while ( digitalRead( Config.LoRaDevices[Channel].DIO5 ) == 0 ) - { - } - // delay(1); - } - - // LogMessage("Mode Change Done\n"); - return; -} - -void -setFrequency( int Channel, double Frequency ) -{ - unsigned long FrequencyValue; - char FrequencyString[10]; - - // Format frequency as xxx.xxx.x Mhz - sprintf( FrequencyString, "%8.4lf ", Frequency ); - FrequencyString[8] = FrequencyString[7]; - FrequencyString[7] = '.'; - - FrequencyValue = ( unsigned long ) ( Frequency * 7110656 / 434 ); - - writeRegister( Channel, 0x06, ( FrequencyValue >> 16 ) & 0xFF ); // Set frequency - writeRegister( Channel, 0x07, ( FrequencyValue >> 8 ) & 0xFF ); - writeRegister( Channel, 0x08, FrequencyValue & 0xFF ); - - Config.LoRaDevices[Channel].activeFreq = Frequency; - - // LogMessage("Set Frequency to %lf\n", Frequency); - - ChannelPrintf( Channel, 1, 1, "Channel %d %s MHz ", Channel, - FrequencyString ); -} - -void -setLoRaMode( int Channel ) -{ - double Frequency; - - // LogMessage("Setting LoRa Mode\n"); - setMode( Channel, RF98_MODE_SLEEP ); - writeRegister( Channel, REG_OPMODE, 0x80 ); - - setMode( Channel, RF98_MODE_SLEEP ); - - if ( sscanf( Config.LoRaDevices[Channel].Frequency, "%lf", &Frequency ) ) - { - // LogMessage("Set Default Frequency\n"); - setFrequency( Channel, Frequency ); - } -} - -char * -BandwidthString( int Bandwidth ) -{ - if ( Bandwidth == BANDWIDTH_7K8 ) - return "7.8k"; - if ( Bandwidth == BANDWIDTH_10K4 ) - return "10.4k"; - if ( Bandwidth == BANDWIDTH_15K6 ) - return "15.6k"; - if ( Bandwidth == BANDWIDTH_20K8 ) - return "20.8k"; - if ( Bandwidth == BANDWIDTH_31K25 ) - return "31.25k"; - if ( Bandwidth == BANDWIDTH_41K7 ) - return "41.7k"; - if ( Bandwidth == BANDWIDTH_62K5 ) - return "62.5k"; - if ( Bandwidth == BANDWIDTH_125K ) - return "125k"; - if ( Bandwidth == BANDWIDTH_250K ) - return "250k"; - if ( Bandwidth == BANDWIDTH_500K ) - return "500k"; - return "??k"; -} - -void -SetLoRaParameters( int Channel, int ImplicitOrExplicit, int ErrorCoding, - int Bandwidth, int SpreadingFactor, - int LowDataRateOptimize ) -{ - writeRegister( Channel, REG_MODEM_CONFIG, - ImplicitOrExplicit | ErrorCoding | Bandwidth ); - writeRegister( Channel, REG_MODEM_CONFIG2, SpreadingFactor | CRC_ON ); - writeRegister( Channel, REG_MODEM_CONFIG3, 0x04 | LowDataRateOptimize ); // 0x04: AGC sets LNA gain - writeRegister( Channel, REG_DETECT_OPT, ( readRegister( Channel, REG_DETECT_OPT ) & 0xF8 ) | ( ( SpreadingFactor == SPREADING_6 ) ? 0x05 : 0x03 ) ); // 0x05 For SF6; 0x03 otherwise - writeRegister( Channel, REG_DETECTION_THRESHOLD, ( SpreadingFactor == SPREADING_6 ) ? 0x0C : 0x0A ); // 0x0C for SF6, 0x0A otherwise - - Config.LoRaDevices[Channel].CurrentBandwidth = Bandwidth; - - ChannelPrintf( Channel, 2, 1, "%s, %s, SF%d, EC4:%d %s", - ImplicitOrExplicit == - IMPLICIT_MODE ? "Implicit" : "Explicit", - BandwidthString( Bandwidth ), SpreadingFactor >> 4, - ( ErrorCoding >> 1 ) + 4, - LowDataRateOptimize ? "LDRO" : "" ); -} - -void -SetDefaultLoRaParameters( int Channel ) -{ - // LogMessage("Set Default Parameters\n"); - - SetLoRaParameters( Channel, - Config.LoRaDevices[Channel].ImplicitOrExplicit, - Config.LoRaDevices[Channel].ErrorCoding, - Config.LoRaDevices[Channel].Bandwidth, - Config.LoRaDevices[Channel].SpreadingFactor, - Config.LoRaDevices[Channel].LowDataRateOptimize ); -} - -///////////////////////////////////// -// Method: Setup to receive continuously -////////////////////////////////////// -void -startReceiving( int Channel ) -{ - writeRegister( Channel, REG_DIO_MAPPING_1, 0x00 ); // 00 00 00 00 maps DIO0 to RxDone - - writeRegister( Channel, REG_PAYLOAD_LENGTH, 255 ); - writeRegister( Channel, REG_RX_NB_BYTES, 255 ); - - writeRegister( Channel, REG_FIFO_RX_BASE_AD, 0 ); - writeRegister( Channel, REG_FIFO_ADDR_PTR, 0 ); - - // Setup Receive Continous Mode - setMode( Channel, RF98_MODE_RX_CONTINUOUS ); -} - -void ReTune( int Channel, double FreqShift ) -{ - setMode( Channel, RF98_MODE_SLEEP ); - LogMessage( "Retune by %lf kHz\n", FreqShift * 1000 ); - setFrequency( Channel, Config.LoRaDevices[Channel].activeFreq + FreqShift ); - startReceiving( Channel ); -} - -void SendLoRaData(int Channel, char *buffer, int Length) -{ - unsigned char data[257]; - int i; - - // Change frequency for the uplink ? - if (Config.LoRaDevices[Channel].UplinkFrequency > 0) - { - LogMessage("Change frequency to %.3lfMHz\n", Config.LoRaDevices[Channel].UplinkFrequency); - setFrequency(Channel, Config.LoRaDevices[Channel].UplinkFrequency); - } - - // Change mode for the uplink ? - if (Config.LoRaDevices[Channel].UplinkMode >= 0) - { - int UplinkMode; - - UplinkMode = Config.LoRaDevices[Channel].UplinkMode; - - LogMessage("Change LoRa mode to %d\n", Config.LoRaDevices[Channel].UplinkMode); - - SetLoRaParameters(Channel, - LoRaModes[UplinkMode].ImplicitOrExplicit, - LoRaModes[UplinkMode].ErrorCoding, - LoRaModes[UplinkMode].Bandwidth, - LoRaModes[UplinkMode].SpreadingFactor, - 0); - } - - LogMessage( "LoRa Channel %d Sending %d bytes\n", Channel, Length ); - Config.LoRaDevices[Channel].Sending = 1; - - setMode( Channel, RF98_MODE_STANDBY ); - - writeRegister( Channel, REG_DIO_MAPPING_1, 0x40 ); // 01 00 00 00 maps DIO0 to TxDone - - writeRegister( Channel, REG_FIFO_TX_BASE_AD, 0x00 ); // Update the address ptr to the current tx base address - writeRegister( Channel, REG_FIFO_ADDR_PTR, 0x00 ); - - data[0] = REG_FIFO | 0x80; - for ( i = 0; i < Length; i++ ) - { - data[i + 1] = buffer[i]; - } - wiringPiSPIDataRW( Channel, data, Length + 1 ); - - // Set the length. For implicit mode, since the length needs to match what the receiver expects, we have to set a value which is 255 for an SSDV packet - writeRegister( Channel, REG_PAYLOAD_LENGTH, Length ); - - // go into transmit mode - setMode( Channel, RF98_MODE_TX ); -} - -void -ShowPacketCounts( int Channel ) -{ - if ( Config.LoRaDevices[Channel].InUse ) - { - ChannelPrintf( Channel, 7, 1, "Telem Packets = %d (%us) ", - Config.LoRaDevices[Channel].TelemetryCount, - Config.LoRaDevices[Channel]. - LastTelemetryPacketAt ? ( unsigned int ) ( time( NULL ) - - - Config. - LoRaDevices - [Channel]. - LastTelemetryPacketAt ) - : 0 ); - ChannelPrintf( Channel, 8, 1, "Image Packets = %d (%us) ", - Config.LoRaDevices[Channel].SSDVCount, - Config.LoRaDevices[Channel]. - LastSSDVPacketAt ? ( unsigned int ) ( time( NULL ) - - Config. - LoRaDevices - [Channel]. - LastSSDVPacketAt ) - : 0 ); - - ChannelPrintf( Channel, 9, 1, "Bad CRC = %d Bad Type = %d", - Config.LoRaDevices[Channel].BadCRCCount, - Config.LoRaDevices[Channel].UnknownCount ); - - ChannelPrintf( Channel, 6, 16, "SSDV %d ", - Config.LoRaDevices[Channel].SSDVCount ); - } -} - -void -ProcessUploadMessage( int Channel, char *Message ) -{ - // LogMessage("Ch %d: Uploaded message %s\n", Channel, Message); -} - -void -ProcessCallingMessage( int Channel, char *Message ) -{ - char Payload[16]; - double Frequency; - int ImplicitOrExplicit, ErrorCoding, Bandwidth, SpreadingFactor, - LowDataRateOptimize; - - ChannelPrintf( Channel, 3, 1, "Calling message %d bytes ", - strlen( Message ) ); - - if ( sscanf( Message + 2, "%15[^,],%lf,%d,%d,%d,%d,%d", - Payload, - &Frequency, - &ImplicitOrExplicit, - &ErrorCoding, - &Bandwidth, &SpreadingFactor, &LowDataRateOptimize ) == 7 ) - { - if ( Config.LoRaDevices[Channel].AFC ) - { - double MasterFrequency; - - sscanf( Config.LoRaDevices[Channel].Frequency, "%lf", - &MasterFrequency ); - - Frequency += - Config.LoRaDevices[Channel].activeFreq - MasterFrequency; - } - - LogMessage( "Ch %d: Calling message, new frequency %7.3lf\n", Channel, - Frequency ); - - // Decoded OK - setMode( Channel, RF98_MODE_SLEEP ); - - // setFrequency(Channel, Config.LoRaDevices[Channel].activeFreq + ); - setFrequency( Channel, Frequency ); - - SetLoRaParameters( Channel, ImplicitOrExplicit, ErrorCoding, - Bandwidth, SpreadingFactor, LowDataRateOptimize ); - - setMode( Channel, RF98_MODE_RX_CONTINUOUS ); - - Config.LoRaDevices[Channel].InCallingMode = 1; - - // ChannelPrintf(Channel, 1, 1, "Channel %d %7.3lfMHz ", Channel, Frequency); - } -} - -size_t -write_data( void *buffer, size_t size, size_t nmemb, void *userp ) -{ - return size * nmemb; -} - -void -UploadListenerTelemetry( char *callsign, float gps_lat, float gps_lon, - char *antenna ) -{ - int time_epoch = ( int ) time( NULL ); - if ( Config.EnableHabitat ) - { - CURL *curl; - CURLcode res; - char PostFields[300]; - char JsonData[200]; - - /* In windows, this will init the winsock stuff */ - - /* get a curl handle */ - curl = curl_easy_init( ); - if ( curl ) - { - // So that the response to the curl POST doesn;'t mess up my finely crafted display! - curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data ); - - // Set the URL that is about to receive our POST - curl_easy_setopt( curl, CURLOPT_URL, - "http://habitat.habhub.org/transition/listener_telemetry" ); - - // Now specify the POST data - sprintf( JsonData, "{\"latitude\": %f, \"longitude\": %f}", - gps_lat, gps_lon ); - sprintf( PostFields, "callsign=%s&time=%d&data=%s", callsign, - time_epoch, JsonData ); - curl_easy_setopt( curl, CURLOPT_POSTFIELDS, PostFields ); - - // Perform the request, res will get the return code - res = curl_easy_perform( curl ); - - // Check for errors - if ( res == CURLE_OK ) - { - LogMessage( "Uploaded listener %s position %f,%f\n", - Config.Tracker, Config.latitude, - Config.longitude ); - } - else - { - LogMessage( "curl_easy_perform() failed: %s\n", - curl_easy_strerror( res ) ); - } - - // always cleanup - curl_easy_cleanup( curl ); - } - - /* In windows, this will init the winsock stuff */ - - /* get a curl handle */ - curl = curl_easy_init( ); - if ( curl ) - { - // So that the response to the curl POST doesn;'t mess up my finely crafted display! - curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data ); - - // Set the URL that is about to receive our POST - curl_easy_setopt( curl, CURLOPT_URL, - "http://habitat.habhub.org/transition/listener_information" ); - - // Now specify the POST data - sprintf( JsonData, "{\"radio\": \"%s\", \"antenna\": \"%s\"}", - "LoRa RFM98W", antenna ); - sprintf( PostFields, "callsign=%s&time=%d&data=%s", Config.Tracker, time_epoch, JsonData ); - curl_easy_setopt( curl, CURLOPT_POSTFIELDS, PostFields ); - - // Perform the request, res will get the return code - res = curl_easy_perform( curl ); - - // Check for errors - if ( res != CURLE_OK ) - { - LogMessage( "curl_easy_perform() failed: %s\n", - curl_easy_strerror( res ) ); - } - - // always cleanup - curl_easy_cleanup( curl ); - } - - } -} - - -void -DoPositionCalcs( Channel ) -{ - unsigned long Now; - struct tm tm; - float Climb, Period; - - strptime( Config.LoRaDevices[Channel].Time, "%H:%M:%S", &tm ); - Now = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec; - - if ( ( Config.LoRaDevices[Channel].LastPositionAt > 0 ) - && ( Now > Config.LoRaDevices[Channel].LastPositionAt ) ) - { - Climb = - ( float ) Config.LoRaDevices[Channel].Altitude - - ( float ) Config.LoRaDevices[Channel].PreviousAltitude; - Period = - ( float ) Now - - ( float ) Config.LoRaDevices[Channel].LastPositionAt; - Config.LoRaDevices[Channel].AscentRate = Climb / Period; - } - else - { - Config.LoRaDevices[Channel].AscentRate = 0; - } - - Config.LoRaDevices[Channel].PreviousAltitude = - Config.LoRaDevices[Channel].Altitude; - Config.LoRaDevices[Channel].LastPositionAt = Now; - - ChannelPrintf( Channel, 4, 1, "%8.5lf, %8.5lf, %05u ", - Config.LoRaDevices[Channel].Latitude, - Config.LoRaDevices[Channel].Longitude, - Config.LoRaDevices[Channel].Altitude ); -} - - -void -ProcessLine( int Channel, char *Line ) -{ - int Satellites; - float TempInt, TempExt; - - Config.LoRaDevices[Channel].FlightMode = -1; - - if ( Config.EnableDev ) - { - sscanf( Line + 2, - "%15[^,],%u,%8[^,],%lf,%lf,%u,%d,%d,%d,%f,%f,%lf,%lf,%lf,%lf,%d,%d,%d,%lf,%d,%d,%d,%d,%lf,%d", - ( Config.LoRaDevices[Channel].Payload ), - &( Config.LoRaDevices[Channel].Counter ), - ( Config.LoRaDevices[Channel].Time ), - &( Config.LoRaDevices[Channel].Latitude ), - &( Config.LoRaDevices[Channel].Longitude ), - &( Config.LoRaDevices[Channel].Altitude ), - &( Config.LoRaDevices[Channel].Speed ), - &( Config.LoRaDevices[Channel].Heading ), &Satellites, - &TempInt, &TempExt, &( Config.LoRaDevices[Channel].cda ), - &( Config.LoRaDevices[Channel].PredictedLatitude ), - &( Config.LoRaDevices[Channel].PredictedLongitude ), - &( Config.LoRaDevices[Channel].PredictedLandingSpeed ), - &( Config.LoRaDevices[Channel].PredictedTime ), - &( Config.LoRaDevices[Channel].CompassActual ), - &( Config.LoRaDevices[Channel].CompassTarget ), - &( Config.LoRaDevices[Channel].AirSpeed ), - &( Config.LoRaDevices[Channel].AirDirection ), - &( Config.LoRaDevices[Channel].ServoLeft ), - &( Config.LoRaDevices[Channel].ServoRight ), - &( Config.LoRaDevices[Channel].ServoTime ), - &( Config.LoRaDevices[Channel].GlideRatio ), - &( Config.LoRaDevices[Channel].FlightMode ) ); - } - else - { - sscanf( Line + 2, "%15[^,],%u,%8[^,],%lf,%lf,%u", - ( Config.LoRaDevices[Channel].Payload ), - &( Config.LoRaDevices[Channel].Counter ), - ( Config.LoRaDevices[Channel].Time ), - &( Config.LoRaDevices[Channel].Latitude ), - &( Config.LoRaDevices[Channel].Longitude ), - &( Config.LoRaDevices[Channel].Altitude ) ); - } -} - - -void -ProcessTelemetryMessage( int Channel, char *Message ) -{ - if ( strlen( Message + 1 ) < 250 ) - { - char *startmessage, *endmessage; - - ChannelPrintf( Channel, 3, 1, "Telemetry %d bytes ", - strlen( Message + 1 ) ); - - startmessage = Message; - endmessage = strchr( startmessage, '\n' ); - - if ( endmessage != NULL ) - { - habitate_telem_packets++; - - time_t now; - struct tm *tm; - - *endmessage = '\0'; - - LogTelemetryPacket(startmessage); - - strcpy( Config.LoRaDevices[Channel].Telemetry, startmessage ); - // UploadTelemetryPacket(startmessage); - - ProcessLine( Channel, startmessage ); - - now = time( 0 ); - tm = localtime( &now ); - - - if ( Config.EnableHabitat ) - { - - // Create a telemetry packet - telemetry_t t; - t.Channel = Channel; - t.Packet_Number = habitate_telem_packets; - memcpy( t.Telemetry, startmessage, - strlen( startmessage ) + 1 ); - - // Add the telemetry packet to the pipe - int result = write( telem_pipe_fd[1], &t, sizeof( t ) ); - if ( result == -1 ) - { - printf( "Error writing to the telemetry pipe\n" ); - exit( 1 ); - } - if ( result == 0 ) - { - LogMessage( "Nothing written to telemetry pipe \n" ); - } - if ( result > 1 ) - { - htsv.packet_count++; - } - } - - LogMessage( "%02d:%02d:%02d Ch%d: %s\n", tm->tm_hour, tm->tm_min, - tm->tm_sec, Channel, startmessage ); - - } - - DoPositionCalcs( Channel ); - - // RJH I think this should be moved up to the bottom of the loop above - Config.LoRaDevices[Channel].TelemetryCount++; - Config.LoRaDevices[Channel].LastTelemetryPacketAt = time( NULL ); - } -} - -static char * -decode_callsign( char *callsign, uint32_t code ) -{ - char *c, s; - - *callsign = '\0'; - - /* Is callsign valid? */ - if ( code > 0xF423FFFF ) - return ( callsign ); - - for ( c = callsign; code; c++ ) - { - s = code % 40; - if ( s == 0 ) - *c = '-'; - else if ( s < 11 ) - *c = '0' + s - 1; - else if ( s < 14 ) - *c = '-'; - else - *c = 'A' + s - 14; - code /= 40; - } - *c = '\0'; - - return ( callsign ); -} - -int -FileExists( char *filename ) -{ - struct stat st; - - return stat( filename, &st ) == 0; -} - -void -ProcessSSDVMessage( int Channel, char *Message ) -{ - // SSDV packet - uint32_t CallsignCode; - char Callsign[7], *FileMode; - int ImageNumber, PacketNumber; - char filename[100]; - FILE *fp; - - Message[0] = 0x55; - - CallsignCode = Message[2]; - CallsignCode <<= 8; - CallsignCode |= Message[3]; - CallsignCode <<= 8; - CallsignCode |= Message[4]; - CallsignCode <<= 8; - CallsignCode |= Message[5]; - - decode_callsign( Callsign, CallsignCode ); - - ImageNumber = Message[6]; - PacketNumber = Message[7] * 256 + Message[8]; - - LogMessage( "Ch%d: SSDV Packet, Callsign %s, Image %d, Packet %d\n", - Channel, Callsign, Message[6], PacketNumber ); - ChannelPrintf( Channel, 3, 1, "SSDV Packet " ); - ChannelPrintf( Channel, 5, 1, "SSDV %s: Image %d, Packet %d", Callsign, - Message[6], PacketNumber ); - - // Create new file ? - sprintf( filename, "/tmp/%s_%d.bin", Callsign, ImageNumber ); - if ( !FileExists( filename ) ) - { - // New image so new file - LogMessage( "Started image %d\n", ImageNumber ); - - // AddImageNumberToLog(Channel, ImageNumber); - - FileMode = "wb"; - } - else - { - FileMode = "r+b"; - } - - // Save to file - if ( ( fp = fopen( filename, FileMode ) ) ) - { - fseek( fp, PacketNumber * 256, SEEK_SET ); - if ( fwrite( Message, 1, 256, fp ) == 256 ) - { - // AddImagePacketToLog(Channel, ImageNumber, PacketNumber); - } - else - { - LogMessage( "** FAILED TO WRITE TO SSDV FILE\n" ); - } - - fclose( fp ); - } - - // ShowMissingPackets(Channel); - - if ( Config.EnableSSDV ) - { - // Create a SSDV packet - ssdv_t s; - s.Channel = Channel; - s.Packet_Number = Config.LoRaDevices[Channel].SSDVCount; - memcpy( s.SSDV_Packet, Message, 256 ); - - // Add the SSDV packet to the pipe - int result = write( ssdv_pipe_fd[1], &s, sizeof( s ) ); - if ( result == -1 ) - { - printf( "Error writing to the issdv pipe\n" ); - exit( 1 ); - } - if ( result == 0 ) - { - LogMessage( "Nothing written to ssdv pipe \n" ); - } - if ( result > 1 ) - { - stsv.packet_count++; - } - - } - - Config.LoRaDevices[Channel].SSDVCount++; - Config.LoRaDevices[Channel].LastSSDVPacketAt = time( NULL ); -} - -void -TestMessageForSMSAcknowledgement( int Channel, char *Message ) -{ - if ( Config.SMSFolder[0] ) - { - if ( strlen( Message ) < 250 ) - { - char Line[256]; - char *token, *value1, *value2; - int FileNumber; - - value1 = NULL; - value2 = NULL; - - strcpy( Line, Message ); - token = strtok( Line, "," ); - while ( token != NULL ) - { - value1 = value2; - value2 = token; - token = strtok( NULL, "," ); - } - - // Rename the file matching this parameter - if ( value1 ) - { - char OldFileName[256], NewFileName[256]; - FileNumber = atoi( value1 ); - if ( FileNumber > 0 ) - { - sprintf( OldFileName, "%s/%d.sms", Config.SMSFolder, FileNumber ); - if ( FileExists( OldFileName ) ) - { - sprintf( NewFileName, "%s/%d.ack", Config.SMSFolder, FileNumber ); - if ( FileExists( NewFileName ) ) - { - remove( NewFileName ); - } - rename( OldFileName, NewFileName ); - LogMessage( "Renamed %s as %s\n", OldFileName, - NewFileName ); - } - } - } - } - } -} - -void -DIO0_Interrupt( int Channel ) -{ - if ( Config.LoRaDevices[Channel].Sending ) - { - Config.LoRaDevices[Channel].Sending = 0; - LogMessage( "Ch%d: End of Tx\n", Channel ); - - setLoRaMode( Channel ); - SetDefaultLoRaParameters( Channel ); - startReceiving( Channel ); - } - else - { - int Bytes; - char Message[257]; - - Bytes = receiveMessage( Channel, Message + 1 ); - - // hexdump_buffer ("Raw Data", Message, 257); - - - if ( Bytes > 0 ) - { - if ( Config.LoRaDevices[Channel].ActivityLED >= 0 ) - { - digitalWrite( Config.LoRaDevices[Channel].ActivityLED, 1 ); - LEDCounts[Channel] = 5; - } - - if ( Message[1] == '!' ) - { - ProcessUploadMessage( Channel, Message + 1 ); - } - else if ( Message[1] == '^' ) - { - ProcessCallingMessage( Channel, Message + 1 ); - } - else if ( Message[1] == '$' ) - { - ProcessTelemetryMessage( Channel, Message + 1 ); - TestMessageForSMSAcknowledgement( Channel, Message + 1 ); - } - else if ( Message[1] == '>' ) - { - LogMessage( "Flight Controller message %d bytes = %s", Bytes, - Message + 1 ); - } - else if ( Message[1] == '*' ) - { - LogMessage( "Uplink Command message %d bytes = %s", Bytes, - Message + 1 ); - } - else if ((Message[1] == 0x66) || (Message[1] == 0x67) || (Message[1] == 0x68) || (Message[1] == 0x69)) - { - ProcessSSDVMessage( Channel, Message ); - } - else - { - LogMessage( "Unknown packet type is %02Xh, RSSI %d\n", - Message[1], readRegister( Channel, - REG_PACKET_RSSI ) - - 157 ); - ChannelPrintf( Channel, 3, 1, "Unknown Packet %d, %d bytes", - Message[0], Bytes ); - Config.LoRaDevices[Channel].UnknownCount++; - } - - Config.LoRaDevices[Channel].LastPacketAt = time( NULL ); - - if ( Config.LoRaDevices[Channel].InCallingMode - && ( Config.CallingTimeout > 0 ) ) - { - Config.LoRaDevices[Channel].ReturnToCallingModeAt = - time( NULL ) + Config.CallingTimeout; - } - - ShowPacketCounts( Channel ); - } - } -} - -void DIO_Ignore_Interrupt_0( void ) -{ - // nothing, obviously! -} - -void -DIO0_Interrupt_0( void ) -{ - DIO0_Interrupt( 0 ); -} - -void -DIO0_Interrupt_1( void ) -{ - DIO0_Interrupt( 1 ); -} - -void -setupRFM98( int Channel ) -{ - if ( Config.LoRaDevices[Channel].InUse ) - { - // initialize the pins - pinMode( Config.LoRaDevices[Channel].DIO0, INPUT ); - pinMode( Config.LoRaDevices[Channel].DIO5, INPUT ); - - wiringPiISR( Config.LoRaDevices[Channel].DIO0, INT_EDGE_RISING, - Channel > 0 ? &DIO0_Interrupt_1 : &DIO0_Interrupt_0 ); - - if ( wiringPiSPISetup( Channel, 500000 ) < 0 ) - { - fprintf( stderr, - "Failed to open SPI port. Try loading spi library with 'gpio load spi'" ); - exit( 1 ); - } - - // LoRa mode - setLoRaMode( Channel ); - - SetDefaultLoRaParameters( Channel ); - - startReceiving( Channel ); - } -} - -double -FrequencyReference( int Channel ) -{ - switch ( Config.LoRaDevices[Channel].CurrentBandwidth ) - { - case BANDWIDTH_7K8: - return 7800; - case BANDWIDTH_10K4: - return 10400; - case BANDWIDTH_15K6: - return 15600; - case BANDWIDTH_20K8: - return 20800; - case BANDWIDTH_31K25: - return 31250; - case BANDWIDTH_41K7: - return 41700; - case BANDWIDTH_62K5: - return 62500; - case BANDWIDTH_125K: - return 125000; - case BANDWIDTH_250K: - return 250000; - case BANDWIDTH_500K: - return 500000; - } - - return 0; -} - -double -FrequencyError( int Channel ) -{ - int32_t Temp; - - Temp = ( int32_t ) readRegister( Channel, REG_FREQ_ERROR ) & 7; - Temp <<= 8L; - Temp += ( int32_t ) readRegister( Channel, REG_FREQ_ERROR + 1 ); - Temp <<= 8L; - Temp += ( int32_t ) readRegister( Channel, REG_FREQ_ERROR + 2 ); - - if ( readRegister( Channel, REG_FREQ_ERROR ) & 8 ) - { - Temp = Temp - 524288; - } - - return -( ( double ) Temp * ( 1 << 24 ) / 32000000.0 ) * - ( FrequencyReference( Channel ) / 500000.0 ); -} - -int -receiveMessage( int Channel, char *message ) -{ - int i, Bytes, currentAddr, x; - unsigned char data[257]; - double FreqError; - - Bytes = 0; - - x = readRegister( Channel, REG_IRQ_FLAGS ); - // LogMessage("Message status = %02Xh\n", x); - - // clear the rxDone flag - writeRegister( Channel, REG_IRQ_FLAGS, 0x40 ); - - // check for payload crc issues (0x20 is the bit we are looking for - if ( ( x & 0x20 ) == 0x20 ) - { - LogMessage( "Ch%d: CRC Failure, RSSI %d\n", Channel, - readRegister( Channel, REG_PACKET_RSSI ) - 157 ); - // reset the crc flags - writeRegister( Channel, REG_IRQ_FLAGS, 0x20 ); - ChannelPrintf( Channel, 3, 1, "CRC Failure %02Xh!!\n", x ); - Config.LoRaDevices[Channel].BadCRCCount++; - ShowPacketCounts( Channel ); - } - else - { - int8_t SNR; - int RSSI; - - currentAddr = readRegister( Channel, REG_FIFO_RX_CURRENT_ADDR ); - Bytes = readRegister( Channel, REG_RX_NB_BYTES ); - - SNR = readRegister( Channel, REG_PACKET_SNR ); - SNR /= 4; - RSSI = readRegister( Channel, REG_PACKET_RSSI ) - 157; - if ( SNR < 0 ) - { - RSSI += SNR; - } - - ChannelPrintf( Channel, 10, 1, "Packet SNR = %d, RSSI = %d ", - ( int ) SNR, RSSI ); - - FreqError = FrequencyError( Channel ) / 1000; - ChannelPrintf( Channel, 11, 1, "Freq. Error = %5.1lfkHz ", - FreqError ); - - - writeRegister( Channel, REG_FIFO_ADDR_PTR, currentAddr ); - - data[0] = REG_FIFO; - wiringPiSPIDataRW( Channel, data, Bytes + 1 ); - for ( i = 0; i <= Bytes; i++ ) - { - message[i] = data[i + 1]; - } - - message[Bytes] = '\0'; - - LogPacket( Channel, SNR, RSSI, FreqError, Bytes, message[1] ); - - if ( Config.LoRaDevices[Channel].AFC && ( fabs( FreqError ) > 0.5 ) ) - { - ReTune( Channel, FreqError / 1000 ); - } - } - - // Clear all flags - writeRegister( Channel, REG_IRQ_FLAGS, 0xFF ); - - return Bytes; -} - -void ReadString( FILE * fp, char *keyword, char *Result, int Length, int NeedValue ) -{ - char line[100], *token, *value; - - fseek( fp, 0, SEEK_SET ); - *Result = '\0'; - - while ( fgets( line, sizeof( line ), fp ) != NULL ) - { - line[strcspn(line, "\r")] = '\0'; // Get rid of CR if there is one - - token = strtok( line, "=" ); - if ( strcasecmp( keyword, token ) == 0 ) - { - value = strtok( NULL, "\n" ); - strcpy( Result, value ); - return; - } - } - - if ( NeedValue ) - { - LogMessage( "Missing value for '%s' in configuration file\n", keyword ); - exit( 1 ); - } -} - -int -ReadInteger( FILE * fp, char *keyword, int NeedValue, int DefaultValue ) -{ - char Temp[64]; - - ReadString( fp, keyword, Temp, sizeof( Temp ), NeedValue ); - - if ( Temp[0] ) - { - return atoi( Temp ); - } - - return DefaultValue; -} - -float -ReadFloat( FILE * fp, char *keyword ) -{ - char Temp[64]; - - ReadString( fp, keyword, Temp, sizeof( Temp ), 0 ); - - if ( Temp[0] ) - { - return atof( Temp ); - } - - return 0; -} - -int -ReadBoolean( FILE * fp, char *keyword, int NeedValue, int *Result ) -{ - char Temp[32]; - - ReadString( fp, keyword, Temp, sizeof( Temp ), NeedValue ); - - if ( *Temp ) - { - *Result = ( *Temp == '1' ) || ( *Temp == 'Y' ) || ( *Temp == 'y' ) - || ( *Temp == 't' ) || ( *Temp == 'T' ); - } - - return *Temp; -} - -void RemoveTrailingSlash(char *Value) -{ - int Len; - - if ((Len = strlen(Value)) > 0) - { - if ((Value[Len-1] == '/') || (Value[Len-1] == '\\')) - { - Value[Len-1] = '\0'; - } - } -} - -void LoadConfigFile(void) -{ - FILE *fp; - char *filename = "gateway.txt"; - char Keyword[32]; - int Channel, Temp; - char TempString[16]; - - Config.LoRaDevices[0].InUse = 0; - Config.LoRaDevices[1].InUse = 0; - Config.EnableHabitat = 1; - Config.EnableSSDV = 1; - Config.EnableTelemetryLogging = 0; - Config.EnablePacketLogging = 0; - Config.SSDVJpegFolder[0] = '\0'; - Config.ftpServer[0] = '\0'; - Config.ftpUser[0] = '\0'; - Config.ftpPassword[0] = '\0'; - Config.ftpFolder[0] = '\0'; - Config.latitude = -999; - Config.longitude = -999; - Config.antenna[0] = '\0'; - Config.EnableDev = 0; - - // Default pin allocations - Config.LoRaDevices[0].DIO0 = 6; - Config.LoRaDevices[0].DIO5 = 5; - - Config.LoRaDevices[1].DIO0 = 27; - Config.LoRaDevices[1].DIO5 = 26; - - if ( ( fp = fopen( filename, "r" ) ) == NULL ) - { - printf - ( "\nFailed to open config file %s (error %d - %s).\nPlease check that it exists and has read permission.\n", - filename, errno, strerror( errno ) ); - exit( 1 ); - } - - // Receiver config - ReadString( fp, "tracker", Config.Tracker, sizeof( Config.Tracker ), 1 ); - LogMessage( "Tracker = '%s'\n", Config.Tracker ); - - // Enable uploads - ReadBoolean( fp, "EnableHabitat", 0, &Config.EnableHabitat ); - ReadBoolean( fp, "EnableSSDV", 0, &Config.EnableSSDV ); - - // Enable telemetry logging - ReadBoolean( fp, "LogTelemetry", 0, &Config.EnableTelemetryLogging ); - - // Enable packet logging - ReadBoolean( fp, "LogPackets", 0, &Config.EnablePacketLogging ); - - // Calling mode - Config.CallingTimeout = ReadInteger( fp, "CallingTimeout", 0, 300 ); - - // LED allocations - Config.NetworkLED = ReadInteger( fp, "NetworkLED", 0, -1 ); - Config.InternetLED = ReadInteger( fp, "InternetLED", 0, -1 ); - Config.LoRaDevices[0].ActivityLED = - ReadInteger( fp, "ActivityLED_0", 0, -1 ); - Config.LoRaDevices[1].ActivityLED = - ReadInteger( fp, "ActivityLED_1", 0, -1 ); - - // Server Port - Config.ServerPort = ReadInteger( fp, "ServerPort", 0, -1 ); - - // SSDV Settings - ReadString( fp, "jpgFolder", Config.SSDVJpegFolder, sizeof( Config.SSDVJpegFolder ), 0 ); - if ( Config.SSDVJpegFolder[0] ) - { - // Create SSDV Folders - struct stat st = { 0 }; - - if ( stat( Config.SSDVJpegFolder, &st ) == -1 ) - { - mkdir( Config.SSDVJpegFolder, 0777 ); - } - } - - // ftp images - ReadString( fp, "ftpserver", Config.ftpServer, sizeof( Config.ftpServer ), - 0 ); - ReadString( fp, "ftpUser", Config.ftpUser, sizeof( Config.ftpUser ), 0 ); - ReadString( fp, "ftpPassword", Config.ftpPassword, - sizeof( Config.ftpPassword ), 0 ); - ReadString( fp, "ftpFolder", Config.ftpFolder, sizeof( Config.ftpFolder ), - 0 ); - - // Listener - Config.latitude = ReadFloat( fp, "Latitude" ); - Config.longitude = ReadFloat( fp, "Longitude" ); - ReadString( fp, "antenna", Config.antenna, sizeof( Config.antenna ), 0 ); - - // Dev mode - ReadBoolean( fp, "EnableDev", 0, &Config.EnableDev ); - - // SMS upload to tracker - Config.SMSFolder[0] = '\0'; - ReadString(fp, "SMSFolder", Config.SMSFolder, sizeof( Config.SMSFolder ), 0); - if ( Config.SMSFolder[0] ) - { - RemoveTrailingSlash(Config.SMSFolder); - LogMessage( "Folder %s will be scanned for messages to upload\n", Config.SMSFolder ); - } - - for ( Channel = 0; Channel <= 1; Channel++ ) - { - // Defaults - Config.LoRaDevices[Channel].Frequency[0] = '\0'; - - sprintf( Keyword, "frequency_%d", Channel ); - ReadString( fp, Keyword, Config.LoRaDevices[Channel].Frequency, - sizeof( Config.LoRaDevices[Channel].Frequency ), 0 ); - if ( Config.LoRaDevices[Channel].Frequency[0] ) - { - Config.LoRaDevices[Channel].ImplicitOrExplicit = EXPLICIT_MODE; - Config.LoRaDevices[Channel].ErrorCoding = ERROR_CODING_4_8; - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_20K8; - Config.LoRaDevices[Channel].SpreadingFactor = SPREADING_11; - Config.LoRaDevices[Channel].LowDataRateOptimize = 0x00; - Config.LoRaDevices[Channel].AFC = FALSE; - - LogMessage( "Channel %d frequency set to %s\n", Channel, - Config.LoRaDevices[Channel].Frequency ); - Config.LoRaDevices[Channel].InUse = 1; - - // DIO0 / DIO5 overrides - sprintf( Keyword, "DIO0_%d", Channel ); - Config.LoRaDevices[Channel].DIO0 = - ReadInteger( fp, Keyword, 0, - Config.LoRaDevices[Channel].DIO0 ); - - sprintf( Keyword, "DIO5_%d", Channel ); - Config.LoRaDevices[Channel].DIO5 = - ReadInteger( fp, Keyword, 0, - Config.LoRaDevices[Channel].DIO5 ); - - LogMessage( "LoRa Channel %d DIO0=%d DIO5=%d\n", Channel, - Config.LoRaDevices[Channel].DIO0, - Config.LoRaDevices[Channel].DIO5 ); - - // Uplink - sprintf( Keyword, "UplinkTime_%d", Channel ); - Config.LoRaDevices[Channel].UplinkTime = ReadInteger( fp, Keyword, 0, 0 ); - sprintf( Keyword, "UplinkCycle_%d", Channel ); - Config.LoRaDevices[Channel].UplinkCycle = ReadInteger( fp, Keyword, 0, 0 ); - LogMessage( "Channel %d UplinkTime %d Uplink Cycle %d\n", Channel, - Config.LoRaDevices[Channel].UplinkTime, - Config.LoRaDevices[Channel].UplinkCycle ); - - sprintf( Keyword, "Power_%d", Channel ); - Config.LoRaDevices[Channel].Power = ReadInteger( fp, Keyword, 0, PA_MAX_UK ); - LogMessage( "Channel %d power set to %02Xh\n", Channel, Config.LoRaDevices[Channel].Power ); - - sprintf( Keyword, "UplinkMode_%d", Channel ); - Config.LoRaDevices[Channel].UplinkMode = ReadInteger( fp, Keyword, 0, -1); - if (Config.LoRaDevices[Channel].UplinkMode >= 0) - { - LogMessage( "Channel %d uplink mode %d\n", Channel, Config.LoRaDevices[Channel].UplinkMode); - } - - sprintf( Keyword, "UplinkFrequency_%d", Channel ); - Config.LoRaDevices[Channel].UplinkFrequency = 0; - Config.LoRaDevices[Channel].UplinkFrequency = ReadFloat(fp, Keyword); - if (Config.LoRaDevices[Channel].UplinkFrequency > 0) - { - LogMessage( "Channel %d uplink frequency %.3lfMHz\n", Channel, Config.LoRaDevices[Channel].UplinkFrequency); - } - - Config.LoRaDevices[Channel].SpeedMode = 0; - - sprintf( Keyword, "mode_%d", Channel ); - Config.LoRaDevices[Channel].SpeedMode = ReadInteger( fp, Keyword, 0, 0 ); - - if ((Config.LoRaDevices[Channel].SpeedMode < 0) || (Config.LoRaDevices[Channel].SpeedMode >= sizeof(LoRaModes)/sizeof(LoRaModes[0]))) Config.LoRaDevices[Channel].SpeedMode = 0; - - Config.LoRaDevices[Channel].ImplicitOrExplicit = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].ImplicitOrExplicit; - Config.LoRaDevices[Channel].ErrorCoding = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].ErrorCoding; - Config.LoRaDevices[Channel].Bandwidth = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].Bandwidth; - Config.LoRaDevices[Channel].SpreadingFactor = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].SpreadingFactor; - Config.LoRaDevices[Channel].LowDataRateOptimize = LoRaModes[Config.LoRaDevices[Channel].SpeedMode].LowDataRateOptimize; - - sprintf( Keyword, "sf_%d", Channel ); - Temp = ReadInteger( fp, Keyword, 0, 0 ); - if ( ( Temp >= 6 ) && ( Temp <= 12 ) ) - { - Config.LoRaDevices[Channel].SpreadingFactor = Temp << 4; - LogMessage( "Setting SF=%d\n", Temp ); - } - - sprintf( Keyword, "bandwidth_%d", Channel ); - ReadString( fp, Keyword, TempString, sizeof( TempString ), 0 ); - if ( *TempString ) - { - LogMessage( "Setting BW=%s\n", TempString ); - } - if ( strcmp( TempString, "7K8" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_7K8; - } - else if ( strcmp( TempString, "10K4" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_10K4; - } - else if ( strcmp( TempString, "15K6" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_15K6; - } - else if ( strcmp( TempString, "20K8" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_20K8; - } - else if ( strcmp( TempString, "31K25" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_31K25; - } - else if ( strcmp( TempString, "41K7" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_41K7; - } - else if ( strcmp( TempString, "62K5" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_62K5; - } - else if ( strcmp( TempString, "125K" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_125K; - } - else if ( strcmp( TempString, "250K" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_250K; - } - else if ( strcmp( TempString, "500K" ) == 0 ) - { - Config.LoRaDevices[Channel].Bandwidth = BANDWIDTH_500K; - } - - sprintf( Keyword, "implicit_%d", Channel ); - if ( ReadBoolean( fp, Keyword, 0, &Temp ) ) - { - Config.LoRaDevices[Channel].ImplicitOrExplicit = - Temp ? IMPLICIT_MODE : EXPLICIT_MODE; - } - - sprintf( Keyword, "coding_%d", Channel ); - Temp = ReadInteger( fp, Keyword, 0, 0 ); - if ( ( Temp >= 5 ) && ( Temp <= 8 ) ) - { - Config.LoRaDevices[Channel].ErrorCoding = ( Temp - 4 ) << 1; - LogMessage( "Setting Error Coding=%d\n", Temp ); - } - - sprintf( Keyword, "lowopt_%d", Channel ); - if ( ReadBoolean( fp, Keyword, 0, &Temp ) ) - { - if ( Temp ) - { - Config.LoRaDevices[Channel].LowDataRateOptimize = 0x08; - } - } - - sprintf( Keyword, "AFC_%d", Channel ); - if ( ReadBoolean( fp, Keyword, 0, &Temp ) ) - { - if ( Temp ) - { - Config.LoRaDevices[Channel].AFC = TRUE; - ChannelPrintf( Channel, 11, 24, "AFC" ); - } - } - - // Clear any flags left over from a previous run - writeRegister( Channel, REG_IRQ_FLAGS, 0xFF ); - } - } - - fclose( fp ); -} - -void -LoadPayloadFile( int ID ) -{ - FILE *fp; - char filename[16]; - - sprintf( filename, "payload_%d.txt", ID ); - - if ( ( fp = fopen( filename, "r" ) ) != NULL ) - { - LogMessage( "Reading payload file %s\n", filename ); - ReadString( fp, "payload", Payloads[ID].Payload, - sizeof( Payloads[ID].Payload ), 1 ); - LogMessage( "Payload %d = '%s'\n", ID, Payloads[ID].Payload ); - - Payloads[ID].InUse = 1; - - fclose( fp ); - } - else - { - strcpy( Payloads[ID].Payload, "Unknown" ); - Payloads[ID].InUse = 0; - } -} - -void -LoadPayloadFiles( void ) -{ - int ID; - - for ( ID = 0; ID < 16; ID++ ) - { - LoadPayloadFile( ID ); - } -} - -WINDOW * -InitDisplay( void ) -{ - WINDOW *mainwin; - int Channel; - - /* Initialize ncurses */ - - if ( ( mainwin = initscr( ) ) == NULL ) - { - fprintf( stderr, "Error initialising ncurses.\n" ); - exit( EXIT_FAILURE ); - } - - start_color( ); /* Initialize colours */ - - if ( COLORS == 256 ) - { - init_pair( 1, COLOR_WHITE, 22 ); - init_pair( 2, COLOR_YELLOW, 22 ); - } - else - { - init_pair( 1, COLOR_WHITE, COLOR_BLUE ); - init_pair( 2, COLOR_YELLOW, COLOR_BLUE ); - } - - color_set( 1, NULL ); - // bkgd(COLOR_PAIR(1)); - // attrset(COLOR_PAIR(1) | A_BOLD); - - char title[80]; - - sprintf( title, "LoRa Habitat and SSDV Gateway by M0RPI, M0RJX - " VERSION); - - // Title bar - mvaddstr( 0, ( 80 - strlen( title ) ) / 2, title ); - refresh( ); - - // Windows for LoRa live data - for ( Channel = 0; Channel <= 1; Channel++ ) - { - Config.LoRaDevices[Channel].Window = - newwin( 14, 38, 1, Channel ? 41 : 1 ); - wbkgd( Config.LoRaDevices[Channel].Window, COLOR_PAIR( 2 ) ); - - // wcolor_set(Config.LoRaDevices[Channel].Window, 2, NULL); - // waddstr(Config.LoRaDevices[Channel].Window, "WINDOW"); - // mvwaddstr(Config.LoRaDevices[Channel].Window, 0, 0, "Window"); - wrefresh( Config.LoRaDevices[Channel].Window ); - } - - curs_set( 0 ); - - return mainwin; -} - -void -CloseDisplay( WINDOW * mainwin ) -{ - /* Clean up after ourselves */ - delwin( mainwin ); - endwin( ); - refresh( ); -} - - -uint16_t -CRC16( unsigned char *ptr ) -{ - uint16_t CRC; - int j; - - CRC = 0xffff; // Seed - - for ( ; *ptr; ptr++ ) - { // For speed, repeat calculation instead of looping for each bit - CRC ^= ( ( ( unsigned int ) *ptr ) << 8 ); - for ( j = 0; j < 8; j++ ) - { - if ( CRC & 0x8000 ) - CRC = ( CRC << 1 ) ^ 0x1021; - else - CRC <<= 1; - } - } - - return CRC; -} - -void -ProcessKeyPress( int ch ) -{ - int Channel = 0; - - /* shifted keys act on channel 1 */ - if ( ch >= 'A' && ch <= 'Z' ) - { - Channel = 1; - /* change from upper to lower case */ - ch += ( 'a' - 'A' ); - } - - if ( ch == 'q' ) - { - run = FALSE; - return; - } - - /* ignore if channel is not in use */ - if ( !Config.LoRaDevices[Channel].InUse ) - { - return; - } - - switch ( ch ) - { - case 'f': - Config.LoRaDevices[Channel].AFC = - !Config.LoRaDevices[Channel].AFC; - ChannelPrintf( Channel, 11, 24, "%s", - Config.LoRaDevices[Channel].AFC ? "AFC" : " " ); - break; - case 'a': - ReTune( Channel, 0.1 ); - break; - case 'z': - ReTune( Channel, -0.1 ); - break; - case 's': - ReTune( Channel, 0.01 ); - break; - case 'x': - ReTune( Channel, -0.01 ); - break; - case 'd': - ReTune( Channel, 0.001 ); - break; - case 'c': - ReTune( Channel, -0.001 ); - break; - default: - //LogMessage("KeyPress %d\n", ch); - return; - } -} - -int -prog_count( char *name ) -{ - DIR *dir; - struct dirent *ent; - char buf[512]; - long pid; - char pname[100] = { 0, }; - char state; - FILE *fp = NULL; - int Count = 0; - - if ( !( dir = opendir( "/proc" ) ) ) - { - perror( "can't open /proc" ); - return 0; - } - - while ( ( ent = readdir( dir ) ) != NULL ) - { - long lpid = atol( ent->d_name ); - if ( lpid < 0 ) - continue; - snprintf( buf, sizeof( buf ), "/proc/%ld/stat", lpid ); - fp = fopen( buf, "r" ); - - if ( fp ) - { - if ( ( fscanf( fp, "%ld (%[^)]) %c", &pid, pname, &state ) ) != - 3 ) - { - printf( "fscanf failed \n" ); - fclose( fp ); - closedir( dir ); - return 0; - } - - if ( !strcmp( pname, name ) ) - { - Count++; - } - fclose( fp ); - } - } - - closedir( dir ); - - return Count; -} - -int -GetTextMessageToUpload( int Channel, char *Message ) -{ - DIR *dp; - struct dirent *ep; - int Result; - - Result = 0; - - LogMessage("Checking for SMS file ...\n"); - - if ( Config.SMSFolder[0] ) - { - dp = opendir( Config.SMSFolder ); - if ( dp != NULL ) - { - while ( ( ep = readdir( dp ) ) && !Result ) - { - if ( strstr( ep->d_name, ".sms" ) ) - { - FILE *fp; - char Line[256], FileName[256]; - int FileNumber; - - sprintf( FileName, "%s%s", Config.SMSFolder, ep->d_name ); - sscanf( ep->d_name, "%d", &FileNumber ); - - if ( ( fp = fopen( FileName, "rt" ) ) != NULL ) - { - if ( fscanf( fp, "%[^\r]", Line ) ) - { - // #001,@daveake: Good Luck Tim !!\n - // @jonathenharty: RT @ProjectHeT: #astroPiTest The Essex Space Agency is looking forward to tweeting the real @esa! @GallarottiA: RT @ProjectHeT: #astroPiTest The Essex Space Agency is looking forward to tweeting the real @esa! - sprintf( Message, "#%d,%s\n", FileNumber, Line ); - - LogMessage( "UPLINK: %s", Message ); - Result = 1; - } - else - { - LogMessage( "FAIL\n" ); - } - fclose( fp ); - } - } - } - closedir( dp ); - } - } - - return Result; -} - -int -GetExternalListOfMissingSSDVPackets( int Channel, char *Message ) -{ - // First, create request file - FILE *fp; - - // LogMessage("GetExternalListOfMissingSSDVPackets()\n"); - - // if ((fp = fopen("get_list.txt", "wt")) != NULL) - { - int i; - - // fprintf(fp, "No Message\n"); - // fclose(fp); - - // LogMessage("File created\n"); - - // Now wait for uplink.txt file to appear. - // Timeout before the end of our Tx slot if no file appears - - for ( i = 0; i < 20; i++ ) - { - if ( ( fp = fopen( "uplink.txt", "r" ) ) ) - { - Message[0] = '\0'; - fgets( Message, 256, fp ); - - fclose( fp ); - - LogMessage( "Got uplink.txt %d bytes\n", strlen( Message ) ); - - // remove("get_list.txt"); - remove( "uplink.txt" ); - - return strlen( Message ); - } - - usleep( 100000 ); - } - - // LogMessage("Timed out waiting for file\n"); - // remove("get_list.txt"); - } - - return 0; -} - - -void SendUplinkMessage( int Channel ) -{ - char Message[512]; - - // Decide what type of message we need to send - if ( GetTextMessageToUpload( Channel, Message ) ) - { - SendLoRaData( Channel, Message, 255 ); - } - else if ( GetExternalListOfMissingSSDVPackets( Channel, Message ) ) - { - SendLoRaData( Channel, Message, 255 ); - } -} - -void -rjh_post_message( int Channel, char *buffer ) -{ - if ( Config.LoRaDevices[Channel].Sending ) - { - Config.LoRaDevices[Channel].Sending = 0; - // LogMessage("Ch%d: End of Tx\n", Channel); - - setLoRaMode( Channel ); - SetDefaultLoRaParameters( Channel ); - startReceiving( Channel ); - } - else - { - int Bytes; - char Message[257]; - - memcpy( Message + 1, buffer, 256 ); - - // hexdump_buffer ("RJH Raw Data", Message, 257); - - Bytes = strlen( buffer ); - - if ( Bytes > 0 ) - { - if ( Config.LoRaDevices[Channel].ActivityLED >= 0 ) - { - digitalWrite( Config.LoRaDevices[Channel].ActivityLED, 1 ); - LEDCounts[Channel] = 5; - } - - if ( Message[1] == '!' ) - { - ProcessUploadMessage( Channel, Message + 1 ); - } - else if ( Message[1] == '^' ) - { - ProcessCallingMessage( Channel, Message + 1 ); - } - else if ( Message[1] == '$' ) - { - //LogMessage("Ch %d: Uploaded message %s\n", Channel, Message+1); - ProcessTelemetryMessage( Channel, Message + 1 ); - } - else if ( Message[1] == '>' ) - { - LogMessage( "Flight Controller message %d bytes = %s", Bytes, - Message + 1 ); - } - else if ( Message[1] == '*' ) - { - LogMessage( "Uplink Command message %d bytes = %s", Bytes, - Message + 1 ); - } - else if ( Message[1] == 0x66 || Message[1] == 0x68 ) - { - ProcessSSDVMessage( Channel, Message ); - } - else - { - LogMessage( "Unknown packet type is %02Xh, RSSI %d\n", - Message[1], readRegister( Channel, - REG_PACKET_RSSI ) - - 157 ); - ChannelPrintf( Channel, 3, 1, "Unknown Packet %d, %d bytes", - Message[0], Bytes ); - Config.LoRaDevices[Channel].UnknownCount++; - } - - Config.LoRaDevices[Channel].LastPacketAt = time( NULL ); - - if ( Config.LoRaDevices[Channel].InCallingMode - && ( Config.CallingTimeout > 0 ) ) - { - Config.LoRaDevices[Channel].ReturnToCallingModeAt = - time( NULL ) + Config.CallingTimeout; - } - - ShowPacketCounts( Channel ); - } - } -} - -int -main( int argc, char **argv ) -{ - int ch; - int LoopPeriod; - int Channel; - pthread_t SSDVThread, FTPThread, NetworkThread, HabitatThread, - ServerThread; - WINDOW *mainwin; - - if ( prog_count( "gateway" ) > 1 ) - { - printf( "\nThe gateway program is already running!\n\n" ); - exit( 1 ); - } - - curl_global_init( CURL_GLOBAL_ALL ); // RJH thread safe - - mainwin = InitDisplay( ); - - // Settings for character input - noecho( ); - cbreak( ); - nodelay( stdscr, TRUE ); - keypad( stdscr, TRUE ); - - LEDCounts[0] = 0; - LEDCounts[1] = 0; - - // Remove any old SSDV files - // system("rm -f /tmp/*.bin"); - - LoadConfigFile(); - LoadPayloadFiles( ); - - int result; - - result = pipe( telem_pipe_fd ); - if ( result < 0 ) - { - fprintf( stderr, "Error creating telemetry pipe\n" ); - return 1; - } - - result = pipe( ssdv_pipe_fd ); - if ( result < 0 ) - { - fprintf( stderr, "Error creating ssdv pipe\n" ); - return 1; - } - - if ( wiringPiSetup( ) < 0 ) - { - fprintf( stderr, "Failed to open wiringPi\n" ); - exit( 1 ); - } - - if ( Config.LoRaDevices[0].ActivityLED >= 0 ) - pinMode( Config.LoRaDevices[0].ActivityLED, OUTPUT ); - if ( Config.LoRaDevices[1].ActivityLED >= 0 ) - pinMode( Config.LoRaDevices[1].ActivityLED, OUTPUT ); - if ( Config.InternetLED >= 0 ) - pinMode( Config.InternetLED, OUTPUT ); - if ( Config.NetworkLED >= 0 ) - pinMode( Config.NetworkLED, OUTPUT ); - - setupRFM98( 0 ); - setupRFM98( 1 ); - - ShowPacketCounts( 0 ); - ShowPacketCounts( 1 ); - - - LoopPeriod = 0; - - // Initialise the vars - stsv.parent_status = RUNNING; - stsv.packet_count = 0; - - if ( pthread_create( &SSDVThread, NULL, SSDVLoop, ( void * ) &stsv ) ) - { - fprintf( stderr, "Error creating SSDV thread\n" ); - return 1; - } - - if ( pthread_create( &FTPThread, NULL, FTPLoop, NULL ) ) - { - fprintf( stderr, "Error creating FTP thread\n" ); - return 1; - } - - - // Initialise the vars - htsv.parent_status = RUNNING; - htsv.packet_count = 0; - - - if ( pthread_create - ( &HabitatThread, NULL, HabitatLoop, ( void * ) &htsv ) ) - { - fprintf( stderr, "Error creating Habitat thread\n" ); - return 1; - } - - // RJH close (telem_pipe_fd[0]); // Close the read side of the pipe as we are writing here - - if ( Config.ServerPort > 0 ) - { - if ( pthread_create( &ServerThread, NULL, ServerLoop, NULL ) ) - { - fprintf( stderr, "Error creating server thread\n" ); - return 1; - } - } - - if ( ( Config.NetworkLED >= 0 ) && ( Config.InternetLED >= 0 ) ) - { - if ( pthread_create( &NetworkThread, NULL, NetworkLoop, NULL ) ) - { - fprintf( stderr, "Error creating Network thread\n" ); - return 1; - } - } - - if ( ( Config.latitude > -90 ) && ( Config.longitude > -90 ) ) - { - UploadListenerTelemetry( Config.Tracker, Config.latitude, - Config.longitude, Config.antenna ); - } - - char buffer[300]; - char ssdv_buff[257]; - int message_count = 0; - - char fileName[20] = "telem.txt"; - FILE *file_telem = fopen( fileName, "r" ); - - char fileName_ssdv[20] = "ssdv.bin"; - FILE *file_ssdv = fopen( fileName_ssdv, "rb" ); - - LogMessage( "Starting now ...\n" ); - - while ( run ) // && message_count< 9) // RJH Used for debug - { - if ( ( ch = getch( ) ) != ERR ) - { - ProcessKeyPress( ch ); - } - - /* RJH TEST */ - if ( message_count % 10 == 9 ) - { - if ( file_telem ) - { - if ( fgets( buffer, sizeof( buffer ), file_telem ) ) - { - rjh_post_message( 1, buffer ); - } - } - message_count++; // We need to increment this here or we will lock - } - else - { - if ( file_ssdv ) - { - if ( fread( ssdv_buff, 256, 1, file_ssdv ) ) - { - ssdv_buff[256] = '\0'; - rjh_post_message( 1, &ssdv_buff[1] ); - } - } - message_count++; // We need to increment this here or we will lock - } - - - /* RJH TEST */ - if ( LoopPeriod > 1000 ) - { - // Every 1 second - time_t now; - struct tm *tm; - - now = time( 0 ); - tm = localtime( &now ); - - LoopPeriod = 0; - - - for ( Channel = 0; Channel <= 1; Channel++ ) - { - if ( Config.LoRaDevices[Channel].InUse ) - { - - ShowPacketCounts( Channel ); - - ChannelPrintf( Channel, 12, 1, "Current RSSI = %4d ", - readRegister( Channel, - REG_CURRENT_RSSI ) - 157 ); - - // if (Config.LoRaDevices[Channel].LastPacketAt > 0) - // { - // ChannelPrintf(Channel, 6, 1, "%us since last packet ", (unsigned int)(time(NULL) - Config.LoRaDevices[Channel].LastPacketAt)); - // } - - if ( Config.LoRaDevices[Channel].InCallingMode - && ( Config.CallingTimeout > 0 ) - && ( Config. - LoRaDevices[Channel].ReturnToCallingModeAt > 0 ) - && ( time( NULL ) > - Config. - LoRaDevices[Channel].ReturnToCallingModeAt ) ) - { - Config.LoRaDevices[Channel].InCallingMode = 0; - Config.LoRaDevices[Channel].ReturnToCallingModeAt = 0; - - LogMessage( "Return to calling mode\n" ); - - setLoRaMode( Channel ); - - SetDefaultLoRaParameters( Channel ); - - setMode( Channel, RF98_MODE_RX_CONTINUOUS ); - - ChannelPrintf( Channel, 1, 1, - "Channel %d %sMHz %s mode", Channel, - Config.LoRaDevices[Channel].Frequency, - Modes[Config.LoRaDevices - [Channel].SpeedMode] ); - } - - if ( ( Config.LoRaDevices[Channel].UplinkTime > 0 ) - && ( Config.LoRaDevices[Channel].UplinkCycle > 0 ) ) - { - long CycleSeconds; - - - CycleSeconds = - ( tm->tm_hour * 3600 + tm->tm_min * 60 + - tm->tm_sec ) % - Config.LoRaDevices[Channel].UplinkCycle; - - if ( CycleSeconds == - Config.LoRaDevices[Channel].UplinkTime ) - { - // LogMessage("%02d:%02d:%02d - Time to send uplink message\n", tm->tm_hour, tm->tm_min, tm->tm_sec); - - SendUplinkMessage( Channel ); - } - } - - if ( LEDCounts[Channel] - && ( Config.LoRaDevices[Channel].ActivityLED >= 0 ) ) - { - if ( --LEDCounts[Channel] == 0 ) - { - digitalWrite( Config.LoRaDevices[Channel]. - ActivityLED, 0 ); - } - } - } - } - } - - delay( 10 ); - LoopPeriod += 10; - } - - LogMessage("Disabling DIO0 ISRs\n"); - for (Channel=0; Channel<2; Channel++) - { - if (Config.LoRaDevices[Channel].InUse) - { - wiringPiISR(Config.LoRaDevices[Channel].DIO0, INT_EDGE_RISING, &DIO_Ignore_Interrupt_0); - } - } - - LogMessage( "Closing SSDV pipe\n" ); - close( ssdv_pipe_fd[1] ); - - LogMessage( "Closing Habitat pipe\n" ); - close( telem_pipe_fd[1] ); - - LogMessage( "Stopping SSDV thread\n" ); - stsv.parent_status = STOPPED; - - LogMessage( "Stopping Habitat thread\n" ); - htsv.parent_status = STOPPED; - - LogMessage( "Waiting for SSDV thread to close ...\n" ); - pthread_join( SSDVThread, NULL ); - LogMessage( "SSDV thread closed\n" ); - - LogMessage( "Waiting for Habitat thread to close ...\n" ); - pthread_join( HabitatThread, NULL ); - LogMessage( "Habitat thread closed\n" ); - - pthread_mutex_destroy( &var ); - - // sleep (3); - - CloseDisplay( mainwin ); - curl_global_cleanup( ); // RJH thread safe - - if ( Config.NetworkLED >= 0 ) - digitalWrite( Config.NetworkLED, 0 ); - if ( Config.InternetLED >= 0 ) - digitalWrite( Config.InternetLED, 0 ); - if ( Config.LoRaDevices[0].ActivityLED >= 0 ) - digitalWrite( Config.LoRaDevices[0].ActivityLED, 0 ); - if ( Config.LoRaDevices[1].ActivityLED >= 0 ) - digitalWrite( Config.LoRaDevices[1].ActivityLED, 0 ); - - return 0; - -} diff --git a/keep/gateway.h b/keep/gateway.h deleted file mode 100644 index 4b9d09d..0000000 --- a/keep/gateway.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _H_Gateway -#define _H_Gateway - -int receiveMessage( int Channel, char *message ); -void hexdump_buffer( const char *title, const char *buffer, - const int len_buffer ); -void LogPacket( int Channel, int8_t SNR, int RSSI, double FreqError, - int Bytes, unsigned char MessageType ); -void LogTelemetryPacket( char *Telemetry ); -void LogMessage( const char *format, ... ); -void ChannelPrintf( int Channel, int row, int column, const char *format, - ... ); - -#endif diff --git a/keep/global.h b/keep/global.h deleted file mode 100644 index 75cb1c1..0000000 --- a/keep/global.h +++ /dev/null @@ -1,103 +0,0 @@ -#include - -#define RUNNING 1 // The main program is running -#define STOPPED 0 // The main program has stopped - struct TSSDVPacket { - char Packet[256]; - char Callsign[7]; - }; - struct TSSDVPackets { - int ImageNumber; - int HighestPacket; - bool Packets[1024]; - }; - struct TLoRaDevice { - int InUse; - int DIO0; - int DIO5; - char Frequency[16]; - double activeFreq; - bool AFC; - int SpeedMode; - int Power; - int PayloadLength; - int ImplicitOrExplicit; - int ErrorCoding; - int Bandwidth; - int SpreadingFactor; - int LowDataRateOptimize; - int CurrentBandwidth; - WINDOW * Window; - unsigned int TelemetryCount, SSDVCount, BadCRCCount, UnknownCount; - int Sending; - char Telemetry[256]; - char Payload[16], Time[12]; - unsigned int Counter, LastCounter; - unsigned long Seconds; - double PredictedLongitude, PredictedLatitude; - double Longitude, Latitude; - unsigned int Altitude, PreviousAltitude; - unsigned int Satellites; - unsigned long LastPositionAt; - time_t LastPacketAt, LastSSDVPacketAt, LastTelemetryPacketAt; - float AscentRate; - time_t ReturnToCallingModeAt; - int InCallingMode; - int ActivityLED; - -double UplinkFrequency; - -int UplinkMode; - int Speed, Heading, PredictedTime, CompassActual, CompassTarget, - AirDirection, ServoLeft, ServoRight, ServoTime, FlightMode; - double cda, PredictedLandingSpeed, AirSpeed, GlideRatio; - - // Normal (non TDM) uplink - int UplinkTime; - int UplinkCycle; - - // SSDV Packet Log - struct TSSDVPackets SSDVPackets[3]; - }; - struct TConfig { - char Tracker[16]; - int EnableHabitat; - int EnableSSDV; - int EnableTelemetryLogging; - int EnablePacketLogging; - int CallingTimeout; - char SSDVJpegFolder[100]; - char ftpServer[100]; - char ftpUser[32]; - char ftpPassword[32]; - char ftpFolder[64]; - struct TLoRaDevice LoRaDevices[2]; - int NetworkLED; - int InternetLED; - int ServerPort; - float latitude, longitude; - char SMSFolder[64]; - char antenna[64]; - int EnableDev; - }; - typedef struct { - int parent_status; - unsigned long packet_count; -} thread_shared_vars_t; - -typedef struct { - short int Channel; - char Telemetry[257]; - int Packet_Number; -} telemetry_t; - -typedef struct { - short int Channel; - char SSDV_Packet[257]; - int Packet_Number; -} ssdv_t; - -extern struct TConfig Config; - extern int SSDVSendArrayIndex; - extern pthread_mutex_t ssdv_mutex; - void LogMessage( const char *format, ... ); diff --git a/keep/habitat.c b/keep/habitat.c deleted file mode 100644 index e3874c1..0000000 --- a/keep/habitat.c +++ /dev/null @@ -1,229 +0,0 @@ -#include -#include -#include -#include -#include // Standard input/output definitions -#include // String function definitions -#include // UNIX standard function definitions -#include // File control definitions -#include // Error number definitions -#include // POSIX terminal control definitions -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base64.h" -#include "habitat.h" -#include "global.h" -#include "sha256.h" -#include "wiringPi.h" -#include "gateway.h" - -extern int telem_pipe_fd[2]; -extern pthread_mutex_t var; -extern void ChannelPrintf( int Channel, int row, int column, - const char *format, ... ); - -size_t -habitat_write_data( void *buffer, size_t size, size_t nmemb, void *userp ) -{ - // LogMessage("%s\n", (char *)buffer); - return size * nmemb; -} - -void -hash_to_hex( unsigned char *hash, char *line ) -{ - int idx; - - for ( idx = 0; idx < 32; idx++ ) - { - sprintf( &( line[idx * 2] ), "%02x", hash[idx] ); - } - line[64] = '\0'; - - // LogMessage(line); -} - -void -UploadTelemetryPacket( telemetry_t * t ) -{ - CURL *curl; - CURLcode res; - char curl_error[CURL_ERROR_SIZE]; - - /* get a curl handle */ - curl = curl_easy_init( ); - if ( curl ) - { - char url[200]; - char base64_data[1000]; - size_t base64_length; - SHA256_CTX ctx; - unsigned char hash[32]; - char doc_id[100]; - char json[1000], now[32]; - char Sentence[512]; - struct curl_slist *headers = NULL; - time_t rawtime; - struct tm *tm; - - // Get formatted timestamp - time( &rawtime ); - tm = gmtime( &rawtime ); - strftime( now, sizeof( now ), "%Y-%0m-%0dT%H:%M:%SZ", tm ); - - // So that the response to the curl PUT doesn't mess up my finely crafted display! - curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, habitat_write_data ); - - // Set the timeout - curl_easy_setopt( curl, CURLOPT_TIMEOUT, 15 ); - - // RJH capture http errors and report - curl_easy_setopt( curl, CURLOPT_FAILONERROR, 1 ); - curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, curl_error ); - - // Avoid curl library bug that happens if above timeout occurs (sigh) - curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 ); - - // Grab current telemetry string and append a linefeed - sprintf( Sentence, "%s\n", t->Telemetry ); - - // Convert sentence to base64 - base64_encode( Sentence, strlen( Sentence ), &base64_length, - base64_data ); - base64_data[base64_length] = '\0'; - - // Take SHA256 hash of the base64 version and express as hex. This will be the document ID - sha256_init( &ctx ); - sha256_update( &ctx, base64_data, base64_length ); - sha256_final( &ctx, hash ); - hash_to_hex( hash, doc_id ); - - char counter[10]; - sprintf( counter, "%d", t->Packet_Number ); - - // Create json with the base64 data in hex, the tracker callsign and the current timestamp - sprintf( json, - "{\"data\": {\"_raw\": \"%s\"},\"receivers\": {\"%s\": {\"time_created\": \"%s\",\"time_uploaded\": \"%s\"}}}", - base64_data, Config.Tracker, now, now ); - - // LogTelemetryPacket(json); - - - // Set the URL that is about to receive our PUT - sprintf( url, - "http://habitat.habhub.org/habitat/_design/payload_telemetry/_update/add_listener/%s", - doc_id ); -// sprintf(url, "http://ext.hgf.com/ssdv/rjh.php"); - - // Set the headers - headers = NULL; - headers = curl_slist_append( headers, "Accept: application/json" ); - headers = - curl_slist_append( headers, "Content-Type: application/json" ); - headers = curl_slist_append( headers, "charsets: utf-8" ); - - // PUT to http://habitat.habhub.org/habitat/_design/payload_telemetry/_update/add_listener/ with content-type application/json - curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ); - curl_easy_setopt( curl, CURLOPT_URL, url ); - curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "PUT" ); - curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json ); - - // Perform the request, res will get the return code - res = curl_easy_perform( curl ); - - // Check for errors - if ( res == CURLE_OK ) - { - // LogMessage("OK\n"); - } - else - { - LogMessage( "Failed for URL '%s'\n", url ); - LogMessage( "curl_easy_perform() failed: %s\n", - curl_easy_strerror( res ) ); - LogMessage( "error: %s\n", curl_error ); - } - - // always cleanup - curl_slist_free_all( headers ); - curl_easy_cleanup( curl ); - // free(base64_data); - } - -} - - -void * -HabitatLoop( void *vars ) -{ - - if ( Config.EnableHabitat ) - { - thread_shared_vars_t *htsv; - htsv = vars; - telemetry_t t; - int packets = 0; - unsigned long total_packets = 0; - - int i = 1; - - // Keep looping until the parent quits and there are no more packets to - // send to habitat. - while ( ( htsv->parent_status == RUNNING ) || ( packets > 0 ) ) - { - - //THis is neded for some reason habitat thread has a pthread_mutex_lock set - // and this removes it - if ( i ) - { - // pthread_mutex_lock(&var); - pthread_mutex_unlock( &var ); - i = 0; - } - if ( htsv->packet_count > total_packets ) - { - packets = read( telem_pipe_fd[0], &t, sizeof( t ) ); - } - else - { - packets = 0; - // pthread_mutex_unlock(&var); - - // If we have have a rollover after processing 4294967295 packets - if ( htsv->packet_count < total_packets ) - total_packets = 0; - - } - - if ( packets ) - { - // LogMessage ("%s\n", t.Telemetry); - - ChannelPrintf( t.Channel, 6, 1, "Habitat" ); - - LogTelemetryPacket( t.Telemetry ); - - UploadTelemetryPacket( &t ); - - ChannelPrintf( t.Channel, 6, 1, " " ); - - total_packets++; - - } - } - } - - close( telem_pipe_fd[0] ); - close( telem_pipe_fd[1] ); - - LogMessage( "Habitat thread closing\n" ); - - return NULL; -} diff --git a/keep/habitat.h b/keep/habitat.h deleted file mode 100644 index febfc3a..0000000 --- a/keep/habitat.h +++ /dev/null @@ -1 +0,0 @@ -void *HabitatLoop( void *some_void_ptr ); diff --git a/keep/network.c b/keep/network.c deleted file mode 100644 index 36d483c..0000000 --- a/keep/network.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include -#include -#include // Standard input/output definitions -#include // String function definitions -#include // UNIX standard function definitions -#include // File control definitions -#include // Error number definitions -#include // POSIX terminal control definitions -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // Include WiringPi library! -#include "network.h" -#include "global.h" - -int -HaveAnIPAddress( void ) -{ - struct ifaddrs *ifap, *ifa; - struct sockaddr_in *sa; - char *addr; - int FoundAddress; - - FoundAddress = 0; - - if ( getifaddrs( &ifap ) == 0 ) - { - // Success - for ( ifa = ifap; ifa; ifa = ifa->ifa_next ) - { - if ( ifa->ifa_addr != NULL ) - { - // Family is known (which it isn't for a VPN) - if ( ifa->ifa_addr->sa_family == AF_INET ) - { - sa = ( struct sockaddr_in * ) ifa->ifa_addr; - addr = inet_ntoa( sa->sin_addr ); - if ( strcmp( addr, "127.0.0.1" ) != 0 ) - { - FoundAddress = 1; - } - } - } - } - } - - freeifaddrs( ifap ); - - return FoundAddress; -} - -int -CanSeeTheInternet( void ) -{ - struct addrinfo hints, *res; - int status, sockfd, FoundInternet; - - memset( &hints, 0, sizeof hints ); - hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version - hints.ai_socktype = SOCK_STREAM; - - if ( ( status = getaddrinfo( "google.com", "80", &hints, &res ) ) != 0 ) - { - return 0; - } - - sockfd = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); - - FoundInternet = 1; - if ( connect( sockfd, res->ai_addr, res->ai_addrlen ) == -1 ) - { - FoundInternet = 0; - } - - close( sockfd ); - - freeaddrinfo( res ); // free the linked list - - return FoundInternet; -} - -void * -NetworkLoop( void *some_void_ptr ) -{ - while ( 1 ) - { - if ( HaveAnIPAddress( ) ) - { - digitalWrite( Config.NetworkLED, 1 ); -// LogMessage("On network :-)\n"); - - if ( CanSeeTheInternet( ) ) - { - digitalWrite( Config.InternetLED, 1 ); -// LogMessage("On the internet :-)\n"); - } - else - { - digitalWrite( Config.InternetLED, 0 ); -// LogMessage("Not on internet :-(\n"); - } - } - else - { - digitalWrite( Config.NetworkLED, 0 ); -// LogMessage("No network :-(\n"); - } - - sleep( 5 ); - } -} diff --git a/keep/network.h b/keep/network.h deleted file mode 100644 index 4da403a..0000000 --- a/keep/network.h +++ /dev/null @@ -1 +0,0 @@ -void *NetworkLoop( void *some_void_ptr ); diff --git a/keep/server.c b/keep/server.c deleted file mode 100644 index 4461d8d..0000000 --- a/keep/server.c +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include -#include // Standard input/output definitions -#include // String function definitions -#include // UNIX standard function definitions -#include // File control definitions -#include // Error number definitions -#include // POSIX terminal control definitions -#include -#include -#include -#include -#include -#include -#include -#include - -#include "server.h" -#include "global.h" - -extern bool run; -extern bool server_closed; - -void *ServerLoop( void *some_void_ptr ) -{ - int listenfd = 0, connfd = 0; - struct sockaddr_in serv_addr; - - char sendBuff[1025]; - - listenfd = socket( AF_INET, SOCK_STREAM, 0 ); - memset( &serv_addr, '0', sizeof( serv_addr ) ); - memset( sendBuff, '0', sizeof( sendBuff ) ); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = htonl( INADDR_ANY ); - serv_addr.sin_port = htons( Config.ServerPort ); - - LogMessage( "Listening on port %d\n", Config.ServerPort ); - - if ( setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &( int ) - { - 1}, sizeof( int ) ) < 0 ) - { - LogMessage( "setsockopt(SO_REUSEADDR) failed" ); - } - - if ( bind - ( listenfd, ( struct sockaddr * ) &serv_addr, - sizeof( serv_addr ) ) < 0 ) - { - LogMessage( "Server failed errno %d\n", errno ); - exit( -1 ); - } - - listen( listenfd, 10 ); - - while ( run ) - { - int port_closed; - - connfd = accept( listenfd, ( struct sockaddr * ) NULL, NULL ); - - LogMessage( "Connected to client\n" ); - - for ( port_closed = 0; !port_closed; ) - { - int Channel; - // Build json - // sprintf(sendBuff, "{\"class\":\"POSN\",\"time\":\"12:34:56\",\"lat\":54.12345,\"lon\":-2.12345,\"alt\":169}\r\n"); - - for (Channel=0; Channel<=1; Channel++) - { - if ( Config.EnableDev ) - { - sprintf(sendBuff, "{\"class\":\"POSN\",\"index\":%d,\"payload\":\"%s\",\"time\":\"%s\",\"lat\":%.5lf,\"lon\":%.5lf,\"alt\":%d,\"rate\":%.1lf,\"predlat\":%.5lf,\"predlon\":%.5lf,\"speed\":%d," - "\"head\":%d,\"cda\":%.2lf,\"pls\":%.1lf,\"pt\":%d,\"ca\":%d,\"ct\":%d,\"as\":%.1lf,\"ad\":%d,\"sl\":%d,\"sr\":%d,\"st\":%d,\"gr\":%.2lf,\"fm\":%d}\r\n", - Channel, - Config.LoRaDevices[Channel].Payload, - Config.LoRaDevices[Channel].Time, - Config.LoRaDevices[Channel].Latitude, - Config.LoRaDevices[Channel].Longitude, - Config.LoRaDevices[Channel].Altitude, - Config.LoRaDevices[Channel].AscentRate, - Config.LoRaDevices[Channel].PredictedLatitude, - Config.LoRaDevices[Channel].PredictedLongitude, - Config.LoRaDevices[Channel].Speed, - - Config.LoRaDevices[Channel].Heading, - Config.LoRaDevices[Channel].cda, - Config.LoRaDevices[Channel].PredictedLandingSpeed, - Config.LoRaDevices[Channel].PredictedTime, - Config.LoRaDevices[Channel].CompassActual, - Config.LoRaDevices[Channel].CompassTarget, - Config.LoRaDevices[Channel].AirSpeed, - Config.LoRaDevices[Channel].AirDirection, - Config.LoRaDevices[Channel].ServoLeft, - Config.LoRaDevices[Channel].ServoRight, - Config.LoRaDevices[Channel].ServoTime, - Config.LoRaDevices[Channel].GlideRatio, - Config.LoRaDevices[Channel].FlightMode); - } - else - { - sprintf(sendBuff, "{\"class\":\"POSN\",\"index\":%d,\"payload\":\"%s\",\"time\":\"%s\",\"lat\":%.5lf,\"lon\":%.5lf,\"alt\":%d,\"rate\":%.1lf}\r\n", - Channel, - Config.LoRaDevices[Channel].Payload, - Config.LoRaDevices[Channel].Time, - Config.LoRaDevices[Channel].Latitude, - Config.LoRaDevices[Channel].Longitude, - Config.LoRaDevices[Channel].Altitude, - Config.LoRaDevices[Channel].AscentRate); - } - - if ( !run ) - { - port_closed = 1; - } - else if ( send(connfd, sendBuff, strlen(sendBuff), MSG_NOSIGNAL ) <= 0 ) - { - LogMessage( "Disconnected from client\n" ); - port_closed = 1; - } - else - { - sleep(1); - } - } - } - - close( connfd ); - } - - return NULL; -} diff --git a/keep/server.h b/keep/server.h deleted file mode 100644 index 44d908e..0000000 --- a/keep/server.h +++ /dev/null @@ -1,2 +0,0 @@ -void *ServerLoop( void *some_void_ptr ); - diff --git a/keep/sha256.c b/keep/sha256.c deleted file mode 100644 index 3790b0f..0000000 --- a/keep/sha256.c +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include -#include -#include - -#include "sha256.h" - - -// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it -#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - (c)) ++b; a += c; -#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) -#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) - -#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) -#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) -#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) -#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) -#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) - - -uint32_t k[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, - 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, - 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, - 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, - 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, - 0xbef9a3f7, 0xc67178f2 -}; - - -void -sha256_transform( SHA256_CTX * ctx, uint8_t data[] ) -{ - uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; - - for ( i = 0, j = 0; i < 16; ++i, j += 4 ) - m[i] = - ( data[j] << 24 ) | ( data[j + 1] << 16 ) | ( data[j + 2] << 8 ) | - ( data[j + 3] ); - for ( ; i < 64; ++i ) - m[i] = SIG1( m[i - 2] ) + m[i - 7] + SIG0( m[i - 15] ) + m[i - 16]; - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for ( i = 0; i < 64; ++i ) - { - t1 = h + EP1( e ) + CH( e, f, g ) + k[i] + m[i]; - t2 = EP0( a ) + MAJ( a, b, c ); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -void -sha256_init( SHA256_CTX * ctx ) -{ - ctx->datalen = 0; - ctx->bitlen[0] = 0; - ctx->bitlen[1] = 0; - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; -} - -void -sha256_update( SHA256_CTX * ctx, char data[], uint32_t len ) -{ - uint32_t i; - - for ( i = 0; i < len; ++i ) - { - ctx->data[ctx->datalen] = data[i]; - ctx->datalen++; - if ( ctx->datalen == 64 ) - { - sha256_transform( ctx, ctx->data ); - DBL_INT_ADD( ctx->bitlen[0], ctx->bitlen[1], 512 ); - ctx->datalen = 0; - } - } -} - -void -sha256_final( SHA256_CTX * ctx, uint8_t hash[] ) -{ - uint32_t i; - - i = ctx->datalen; - - // Pad whatever data is left in the buffer. - if ( ctx->datalen < 56 ) - { - ctx->data[i++] = 0x80; - while ( i < 56 ) - ctx->data[i++] = 0x00; - } - else - { - ctx->data[i++] = 0x80; - while ( i < 64 ) - ctx->data[i++] = 0x00; - sha256_transform( ctx, ctx->data ); - memset( ctx->data, 0, 56 ); - } - - // Append to the padding the total message's length in bits and transform. - DBL_INT_ADD( ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8 ); - ctx->data[63] = ctx->bitlen[0]; - ctx->data[62] = ctx->bitlen[0] >> 8; - ctx->data[61] = ctx->bitlen[0] >> 16; - ctx->data[60] = ctx->bitlen[0] >> 24; - ctx->data[59] = ctx->bitlen[1]; - ctx->data[58] = ctx->bitlen[1] >> 8; - ctx->data[57] = ctx->bitlen[1] >> 16; - ctx->data[56] = ctx->bitlen[1] >> 24; - sha256_transform( ctx, ctx->data ); - - // Since this implementation uses little endian byte ordering and SHA uses big endian, - // reverse all the bytes when copying the final state to the output hash. - for ( i = 0; i < 4; ++i ) - { - hash[i] = ( ctx->state[0] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 4] = ( ctx->state[1] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 8] = ( ctx->state[2] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 12] = ( ctx->state[3] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 16] = ( ctx->state[4] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 20] = ( ctx->state[5] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 24] = ( ctx->state[6] >> ( 24 - i * 8 ) ) & 0x000000ff; - hash[i + 28] = ( ctx->state[7] >> ( 24 - i * 8 ) ) & 0x000000ff; - } -} diff --git a/keep/sha256.h b/keep/sha256.h deleted file mode 100644 index 63c4945..0000000 --- a/keep/sha256.h +++ /dev/null @@ -1,10 +0,0 @@ -typedef struct { - uint8_t data[64]; - uint32_t datalen; - uint32_t bitlen[2]; - uint32_t state[8]; - } SHA256_CTX; - void sha256_transform( SHA256_CTX * ctx, uint8_t data[] ); -void sha256_init( SHA256_CTX * ctx ); -void sha256_update( SHA256_CTX * ctx, char data[], uint32_t len ); -void sha256_final( SHA256_CTX * ctx, uint8_t hash[] ); diff --git a/keep/ssdv.c b/keep/ssdv.c deleted file mode 100644 index 91cc0d7..0000000 --- a/keep/ssdv.c +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include -#include -#include -#include // Standard input/output definitions -#include // String function definitions -#include // UNIX standard function definitions -#include // File control definitions -#include // Error number definitions -#include // POSIX terminal control definitions -#include -#include -#include -#include -#include -#include -#include - -#include "urlencode.h" -#include "base64.h" -#include "ssdv.h" -#include "gateway.h" -#include "global.h" - -extern int ssdv_pipe_fd[2]; -extern pthread_mutex_t var; - -size_t -write_ssdv_data( void *buffer, size_t size, size_t nmemb, void *userp ) -{ - return size * nmemb; -} - - -void -ConvertStringToHex( unsigned char *Target, unsigned char *Source, int Length ) -{ - const char Hex[16] = "0123456789ABCDEF"; - int i; - - for ( i = 0; i < Length; i++ ) - { - *Target++ = Hex[Source[i] >> 4]; - *Target++ = Hex[Source[i] & 0x0F]; - } - - *Target++ = '\0'; -} - - -void -UploadImagePacket( ssdv_t * s, unsigned int packets ) -{ - CURL *curl; - CURLcode res; - char curl_error[CURL_ERROR_SIZE]; - char base64_data[512], json[32768], packet_json[1000]; - struct curl_slist *headers = NULL; - size_t base64_length; - char now[32]; - time_t rawtime; - struct tm *tm; - char url[250]; - - /* get a curl handle */ - curl = curl_easy_init( ); - if ( curl ) - { - - // So that the response to the curl POST doesn;'t mess up my finely crafted display! - curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_ssdv_data ); - - // Set the timeout - curl_easy_setopt( curl, CURLOPT_TIMEOUT, 15 ); - - // RJH capture http errors and report - curl_easy_setopt( curl, CURLOPT_FAILONERROR, 1 ); - curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, curl_error ); - - // Avoid curl library bug that happens if above timeout occurs (sigh) - curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 ); - - // Get formatted timestamp - time( &rawtime ); - tm = gmtime( &rawtime ); - strftime( now, sizeof( now ), "%Y-%0m-%0dT%H:%M:%SZ", tm ); - - int PacketIndex; - - // Create json with the base64 data in hex, the tracker callsign and the current timestamp - strcpy( json, "{\"type\": \"packets\",\"packets\":[" ); - - for ( PacketIndex = 0; PacketIndex < packets; PacketIndex++ ) - { - base64_encode( s[PacketIndex].SSDV_Packet, 256, &base64_length, - base64_data ); - base64_data[base64_length] = '\0'; - - sprintf( packet_json, - "{\"type\": \"packet\", \"packet\": \"%s\", \"encoding\": \"base64\", \"received\": \"%s\", \"receiver\": \"%s\"}%s", - base64_data, now, Config.Tracker, - PacketIndex == ( packets - 1 ) ? "" : "," ); - strcat( json, packet_json ); - } - strcat( json, "]}" ); - - // LogTelemetryPacket(json); - - strcpy( url, "http://ssdv.habhub.org/api/v0/packets" ); - // strcpy(url,"http://ext.hgf.com/ssdv/rjh.php"); - // strcpy(url,"http://ext.hgf.com/ssdv/apiv0.php?q=packets"); - - // Set the headers - headers = NULL; - headers = curl_slist_append( headers, "Accept: application/json" ); - headers = - curl_slist_append( headers, "Content-Type: application/json" ); - headers = curl_slist_append( headers, "charsets: utf-8" ); - - curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ); - curl_easy_setopt( curl, CURLOPT_URL, url ); - - curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "POST" ); - curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json ); - - // Perform the request, res will get the return code - res = curl_easy_perform( curl ); - - /* Check for errors */ - if ( res == CURLE_OK ) - { - } - else - { - LogMessage( "Failed for URL '%s'\n", url ); - LogMessage( "curl_easy_perform() failed: %s\n", - curl_easy_strerror( res ) ); - LogMessage( "error: %s\n", curl_error ); - } - - /* always cleanup */ - curl_slist_free_all( headers ); // RJH Added this from habitat.c as was missing - curl_easy_cleanup( curl ); - } -} - -void * -SSDVLoop( void *vars ) -{ - - if ( Config.EnableSSDV ) - { - const int max_packets = 51; - thread_shared_vars_t *stsv; - stsv = vars; - ssdv_t s[max_packets]; - unsigned int j = 0; - unsigned int packets = 0; - unsigned long total_packets = 0; - - // Keep looping until the parent quits and there are no more packets to - // send to ssdv. - while ( ( stsv->parent_status == RUNNING ) || ( packets > 0 ) ) - { - - if ( stsv->packet_count > total_packets ) - { - packets = read( ssdv_pipe_fd[0], &s[j], sizeof( ssdv_t ) ); - } - else - { - packets = 0; - - // If we have have a rollover after processing 4294967295 packets - if ( stsv->packet_count < total_packets ) - total_packets = 0; - - } - - if ( packets ) - { - j++; - total_packets++; - } - - if ( j == 50 || ( ( packets == 0 ) && ( j > 0 ) ) ) - { - ChannelPrintf( s[0].Channel, 6, 1, "Habitat" ); - - UploadImagePacket( s, j ); - - ChannelPrintf( s[0].Channel, 6, 1, " " ); - - j = 0; - - packets = 0; - } - - } - } - - close( ssdv_pipe_fd[0] ); - close( ssdv_pipe_fd[1] ); - - LogMessage( "SSDV thread closing\n" ); - - return NULL; - -} diff --git a/keep/ssdv.h b/keep/ssdv.h deleted file mode 100644 index 681a300..0000000 --- a/keep/ssdv.h +++ /dev/null @@ -1,2 +0,0 @@ -void *SSDVLoop( void *some_void_ptr ); - diff --git a/keep/ssdv_resend.py b/keep/ssdv_resend.py deleted file mode 100644 index 9e8d179..0000000 --- a/keep/ssdv_resend.py +++ /dev/null @@ -1,103 +0,0 @@ -import urllib.parse -import urllib.request -import json -from datetime import datetime, timedelta -import sys -import os.path -import time - -def get_list_of_missing_packets(PayloadID, Minutes): - result = '' - - # url = 'http://ssdv.habhub.org/api/v0/images?callsign=PI868&from=2016-01-22T11:00:00Z&missing_packets' - time_limit = datetime.utcnow() - timedelta(0,Minutes*60) # n minutes ago - url = 'http://ssdv.habhub.org/api/v0/images?callsign=' + PayloadID + '&from=' + time_limit.strftime('%Y-%m-%dT%H:%M:%SZ') + '&missing_packets' - - print("url", url) - - req = urllib.request.Request(url) - with urllib.request.urlopen(req) as response: - the_page = response.read() # content = urllib.request.urlopen(url=url, data=data).read() - # print(the_page) - - temp = the_page.decode('utf-8') - j = json.loads(temp) - # print(j) - - line = "" - for index, item in enumerate(j): - # only interested in latest 2 images - if index >= (len(j) - 2): - # only interested in images that have missing packets - if len(item['missing_packets']) > 0: - print(item['id'], item['image_id'], len(item['missing_packets'])) - print(item['missing_packets']) - pl = item['packets'] - # print("highest_packet_id = ", item['last_packet']) - first_missing_packet = -1 - last_missing_packet = -1 - missing_packets = item['missing_packets'] + [9999] - - # Header for this image - if line != "": - line = line + "," - line = line + str(item['image_id']) + ":" + str(item['last_packet']) + "=" - image_line = "" - - for mp_index, mp in enumerate(missing_packets): - if mp_index == 0: - first_missing_packet = mp - last_missing_packet = mp - - if (mp > (last_missing_packet+1)): # or (mp_index == len(item['missing_packets'])-1): - # emit section - if image_line != "": - image_line = image_line + "," - if last_missing_packet == first_missing_packet: - image_line = image_line + str(last_missing_packet) - else: - image_line = image_line + str(first_missing_packet) + "-" + str(last_missing_packet) - first_missing_packet = mp - - last_missing_packet = mp - line = line + image_line - - if line != '': - result = "!" + line + "\n" - - return result - - -if len(sys.argv) <= 1: - print ("Usage: ssdv_resend [folder]\n") - quit() - -payload_id = sys.argv[1] - -if len(sys.argv) >= 3: - folder = sys.argv[2] -else: - folder = './' - -print('Payload = ' + payload_id) -print('Folder = ' + folder) - -while True: - # if os.path.isfile(folder + 'get_list.txt'): - if (datetime.utcnow().second < 0) or (datetime.utcnow().second > 50): - print("Checking ...") - # os.remove(folder + 'get_list.txt') - line = get_list_of_missing_packets(payload_id, 5) - if line == '': - print("No missing packets") - line = "Nothing" - else: - print("Missing Packets:", line) - with open(folder + 'uplink.txt', "w") as text_file: - if line != '': - print(line, file=text_file) - time.sleep(1) - else: - print('.',end="",flush=True) - time.sleep(1) - diff --git a/keep/urlencode.c b/keep/urlencode.c deleted file mode 100644 index d44b5be..0000000 --- a/keep/urlencode.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include - - -/* Converts a hex character to its integer value */ -char -from_hex( char ch ) -{ - return isdigit( ch ) ? ch - '0' : tolower( ch ) - 'a' + 10; -} - -/* Converts an integer value to its hex character*/ -char -to_hex( char code ) -{ - static char hex[] = "0123456789abcdef"; - return hex[code & 15]; -} - -/* Returns a url-encoded version of str */ -/* IMPORTANT: be sure to free() the returned string after use */ -char * -url_encode( char *str ) -{ - char *pstr = str, *buf = malloc( strlen( str ) * 3 + 1 ), *pbuf = buf; - while ( *pstr ) - { - if ( isalnum( *pstr ) || *pstr == '-' || *pstr == '_' || *pstr == '.' - || *pstr == '~' ) - *pbuf++ = *pstr; - else if ( *pstr == ' ' ) - *pbuf++ = '+'; - else - *pbuf++ = '%', *pbuf++ = to_hex( *pstr >> 4 ), *pbuf++ = - to_hex( *pstr & 15 ); - pstr++; - } - *pbuf = '\0'; - return buf; -} - -/* Returns a url-decoded version of str */ -/* IMPORTANT: be sure to free() the returned string after use */ -char * -url_decode( char *str ) -{ - char *pstr = str, *buf = malloc( strlen( str ) + 1 ), *pbuf = buf; - while ( *pstr ) - { - if ( *pstr == '%' ) - { - if ( pstr[1] && pstr[2] ) - { - *pbuf++ = from_hex( pstr[1] ) << 4 | from_hex( pstr[2] ); - pstr += 2; - } - } - else if ( *pstr == '+' ) - { - *pbuf++ = ' '; - } - else - { - *pbuf++ = *pstr; - } - pstr++; - } - *pbuf = '\0'; - return buf; -} diff --git a/keep/urlencode.h b/keep/urlencode.h deleted file mode 100644 index e8fc38f..0000000 --- a/keep/urlencode.h +++ /dev/null @@ -1,2 +0,0 @@ -char *url_encode( char *str ); -