From 1210ec15cfd89f04811ec91db0aec71e14620567 Mon Sep 17 00:00:00 2001 From: Dave Akerman Date: Tue, 27 Sep 2016 19:23:41 +0000 Subject: [PATCH] V1.8.5 - AFC changes; rename config file from sample if missing --- *.dpa | 30 ------- README.md | 16 ++++ config.c | 0 config.h | 0 gateway.c | 229 ++++++++++++++++++++++++------------------------ gateway.txt.old | 34 ------- global.h | 134 ++++++++++++++-------------- habitat.c | 17 +++- server.c | 222 +++++++++++++++++++++++++--------------------- ssdv.c | 32 +++---- ssdv_resend.py | 204 +++++++++++++++++++++--------------------- 11 files changed, 444 insertions(+), 474 deletions(-) delete mode 100644 *.dpa mode change 100755 => 100644 config.c mode change 100755 => 100644 config.h delete mode 100644 gateway.txt.old diff --git a/*.dpa b/*.dpa deleted file mode 100644 index 055b51a..0000000 --- a/*.dpa +++ /dev/null @@ -1,30 +0,0 @@ -tracker=LCARS -EnableHabitat=N -EnableSSDV=N -LogTelemetry=Y -LogPackets=Y -CallingTimeout=60 -ServerPort=6004 -Latitude=51.950230 -Longitude=-2.544500 -Antenna=MagMount -JPGFolder=SSDV -EnableDev=N - -#NetworkLED=21 -#InternetLED=22 -#ActivityLED_0=23 -#ActivityLED_1=24 - -frequency_0=434.450000 -mode_0=1 -#mode_0=1 -DIO0_0=6 -DIO5_0=5 -AFC_0=N - -frequency_1=869.850000 -mode_1=3 -DIO0_1=27 -DIO5_1=26 -AFC_1=N diff --git a/README.md b/README.md index 152af55..78c2e13 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,22 @@ Many thanks to David Brooke for coding this feature and the AFC. Change History ============== +27/09/2016 - V1.8.5 +------------------- + + New config setting - AFCMaxStep - limits AFC delta to this amount in kHz for each new packet + New config setting - AFCTimeout - Resets AFC changes if no packets received in this period (in seconds) + If gateway.txt missing, and gateway-sample.txt present, rename latter as former + Fixed SSDV upload status marker + Fix to frequency format when retuning after calling mode + Some log display messages now only appear if the feature they describe is in use + + +15/09/2016 - V1.8.4 +------------------- + + Fix to handle disabled channel + 14/09/2016 - V1.8.3 ------------------- diff --git a/config.c b/config.c old mode 100755 new mode 100644 diff --git a/config.h b/config.h old mode 100755 new mode 100644 diff --git a/gateway.c b/gateway.c index b2e6238..f38ffea 100644 --- a/gateway.c +++ b/gateway.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,7 +34,7 @@ #include "gateway.h" #include "config.h" -#define VERSION "V1.8.4" +#define VERSION "V1.8.5" bool run = TRUE; // RFM98 @@ -174,8 +174,6 @@ struct TBinaryPacket { 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]; @@ -458,32 +456,6 @@ void setLoRaMode( int Channel ) setFrequency( Channel, Config.LoRaDevices[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"; -} - - int IntToSF(int Value) { return Value << 4; @@ -595,7 +567,7 @@ startReceiving( int Channel ) void ReTune( int Channel, double FreqShift ) { setMode( Channel, RF98_MODE_SLEEP ); - LogMessage( "Retune by %lf kHz\n", FreqShift * 1000 ); + LogMessage( "Retune by %.1lfkHz\n", FreqShift * 1000 ); setFrequency( Channel, Config.LoRaDevices[Channel].activeFreq + FreqShift ); startReceiving( Channel ); } @@ -673,20 +645,15 @@ void ShowPacketCounts(int Channel) 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 ) +void ProcessUploadMessage(int Channel, char *Message) { // LogMessage("Ch %d: Uploaded message %s\n", Channel, Message); } -void -ProcessCallingMessage( int Channel, char *Message ) +void ProcessCallingMessage(int Channel, char *Message) { char Payload[16]; double Frequency; @@ -702,7 +669,7 @@ ProcessCallingMessage( int Channel, char *Message ) &ErrorCoding, &Bandwidth, &SpreadingFactor, &LowDataRateOptimize ) == 7 ) { - if ( Config.LoRaDevices[Channel].AFC ) + if (Config.LoRaDevices[Channel].AFC) { Frequency += (Config.LoRaDevices[Channel].activeFreq - Config.LoRaDevices[Channel].Frequency); } @@ -726,15 +693,12 @@ ProcessCallingMessage( int Channel, char *Message ) } } -size_t -write_data( void *buffer, size_t size, size_t nmemb, void *userp ) +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 ) +void UploadListenerTelemetry( char *callsign, float gps_lat, float gps_lon, char *antenna ) { int time_epoch = ( int ) time( NULL ); if ( Config.EnableHabitat ) @@ -821,8 +785,7 @@ UploadListenerTelemetry( char *callsign, float gps_lat, float gps_lon, } -void -DoPositionCalcs( Channel ) +void DoPositionCalcs(Channel) { unsigned long Now; struct tm tm; @@ -982,8 +945,7 @@ void ProcessTelemetryMessage( int Channel, char *Message) } } -static char * -decode_callsign( char *callsign, uint32_t code ) +static char *decode_callsign( char *callsign, uint32_t code ) { char *c, s; @@ -1216,7 +1178,7 @@ void DIO0_Interrupt( int Channel ) if ( Config.LoRaDevices[Channel].Sending ) { Config.LoRaDevices[Channel].Sending = 0; - LogMessage( "Ch%d: End of Tx\n", Channel ); + // LogMessage( "Ch%d: End of Tx\n", Channel ); setLoRaMode( Channel ); SetDefaultLoRaParameters( Channel ); @@ -1229,11 +1191,8 @@ void DIO0_Interrupt( int Channel ) 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 ); @@ -1255,12 +1214,11 @@ void DIO0_Interrupt( int Channel ) } else if ( Message[1] == '>' ) { - LogMessage( "Flight Controller message %d bytes = %s", Bytes, - Message + 1 ); + LogMessage( "Flight Controller message %d bytes = %s\n", Bytes, Message + 1 ); } else if ( Message[1] == '*' ) { - LogMessage( "Uplink Command message %d bytes = %s", Bytes, Message + 1 ); + LogMessage( "Uplink Command message %d bytes = %s\n", Bytes, Message + 1 ); } else if (((Message[1] & 0x7F) == 0x66) || // SSDV JPG format ((Message[1] & 0x7F) == 0x67) || // SSDV other formats @@ -1284,13 +1242,16 @@ void DIO0_Interrupt( int Channel ) Config.LoRaDevices[Channel].LastPacketAt = time( NULL ); - if ( Config.LoRaDevices[Channel].InCallingMode - && ( Config.CallingTimeout > 0 ) ) + if (Config.LoRaDevices[Channel].InCallingMode && (Config.CallingTimeout > 0)) { - Config.LoRaDevices[Channel].ReturnToCallingModeAt = - time( NULL ) + Config.CallingTimeout; + Config.LoRaDevices[Channel].ReturnToCallingModeAt = time( NULL ) + Config.CallingTimeout; } + if (!Config.LoRaDevices[Channel].InCallingMode && (Config.LoRaDevices[Channel].AFCTimeout > 0)) + { + Config.LoRaDevices[Channel].ReturnToOriginalFrequencyAt = time(NULL) + Config.LoRaDevices[Channel].AFCTimeout; + } + ShowPacketCounts( Channel ); } } @@ -1313,8 +1274,7 @@ DIO0_Interrupt_1( void ) DIO0_Interrupt( 1 ); } -void -setupRFM98( int Channel ) +void setupRFM98( int Channel ) { if ( Config.LoRaDevices[Channel].InUse ) { @@ -1354,8 +1314,7 @@ double FrequencyError( int Channel ) Temp = Temp - 524288; } - return -( ( double ) Temp * ( 1 << 24 ) / 32000000.0 ) * - ( Config.LoRaDevices[Channel].CurrentBandwidth / 500.0 ); + return -( ( double ) Temp * ( 1 << 24 ) / 32000000.0 ) * (Config.LoRaDevices[Channel].CurrentBandwidth / 500.0); } int @@ -1407,8 +1366,20 @@ receiveMessage( int Channel, char *message ) LogPacket(Channel, PacketSNR(Channel), PacketRSSI(Channel), FreqError, Bytes, message[1]); - if ( Config.LoRaDevices[Channel].AFC && ( fabs( FreqError ) > 0.5 ) ) + if (Config.LoRaDevices[Channel].AFC && (fabs( FreqError ) > 0.5)) { + if (Config.LoRaDevices[Channel].MaxAFCStep > 0) + { + // Limit step to MaxAFCStep + if (FreqError > Config.LoRaDevices[Channel].MaxAFCStep) + { + FreqError = Config.LoRaDevices[Channel].MaxAFCStep; + } + else if (FreqError < -Config.LoRaDevices[Channel].MaxAFCStep) + { + FreqError = -Config.LoRaDevices[Channel].MaxAFCStep; + } + } ReTune( Channel, FreqError / 1000 ); } } @@ -1436,7 +1407,18 @@ void LoadConfigFile(void) { FILE *fp; char *filename = "gateway.txt"; + char *sample_filename = "gateway-sample.txt"; int Channel, MainSection; + + if (access(filename, F_OK) != 0) + { + LogMessage("%s missing\n", filename); + if (access(sample_filename, F_OK) == 0) + { + LogMessage("Renaming %s as %s\n", sample_filename, filename); + rename(sample_filename, filename); + } + } // Default configuration Config.latitude = -999; @@ -1489,9 +1471,9 @@ void LoadConfigFile(void) RegisterConfigInteger(MainSection, -1, "ActivityLED_0", &Config.LoRaDevices[0].ActivityLED, NULL); RegisterConfigInteger(MainSection, -1, "ActivityLED_1", &Config.LoRaDevices[1].ActivityLED, NULL); - // Server Port - RegisterConfigInteger(MainSection, -1, "ServerPort", &Config.ServerPort, NULL); - + // Socket + RegisterConfigInteger(MainSection, -1, "ServerPort", &Config.ServerPort, NULL); // JSON server + // SSDV Settings RegisterConfigString(MainSection, -1, "JPGFolder", Config.SSDVJpegFolder, sizeof(Config.SSDVJpegFolder), NULL); if ( Config.SSDVJpegFolder[0] ) @@ -1553,12 +1535,17 @@ void LoadConfigFile(void) // Uplink RegisterConfigInteger(MainSection, Channel, "UplinkTime", &Config.LoRaDevices[Channel].UplinkTime, NULL); - RegisterConfigInteger(MainSection, Channel, "UplinkCycle", &Config.LoRaDevices[Channel].UplinkCycle, NULL); - LogMessage( "Channel %d UplinkTime %d Uplink Cycle %d\n", Channel, Config.LoRaDevices[Channel].UplinkTime, Config.LoRaDevices[Channel].UplinkCycle); + if ((Config.LoRaDevices[Channel].UplinkTime > 0) && (Config.LoRaDevices[Channel].UplinkCycle)) + { + LogMessage( "Channel %d UplinkTime %d Uplink Cycle %d\n", Channel, Config.LoRaDevices[Channel].UplinkTime, Config.LoRaDevices[Channel].UplinkCycle); + } RegisterConfigInteger(MainSection, Channel, "Power", &Config.LoRaDevices[Channel].Power, NULL); - LogMessage( "Channel %d power set to %02Xh\n", Channel, Config.LoRaDevices[Channel].Power ); + if ((Config.LoRaDevices[Channel].UplinkTime > 0) && (Config.LoRaDevices[Channel].UplinkCycle)) + { + LogMessage( "Channel %d power set to %02Xh\n", Channel, Config.LoRaDevices[Channel].Power ); + } RegisterConfigInteger(MainSection, Channel, "UplinkMode", &Config.LoRaDevices[Channel].UplinkMode, NULL); if (Config.LoRaDevices[Channel].UplinkMode >= 0) @@ -1608,6 +1595,18 @@ void LoadConfigFile(void) if (Config.LoRaDevices[Channel].AFC) { ChannelPrintf( Channel, 11, 24, "AFC" ); + + RegisterConfigDouble(MainSection, Channel, "MaxAFCStep", &Config.LoRaDevices[Channel].MaxAFCStep, NULL); + if (Config.LoRaDevices[Channel].MaxAFCStep > 0) + { + LogMessage("Maximum AFC Step = %.0lfkHz\n", Config.LoRaDevices[Channel].MaxAFCStep); + } + + RegisterConfigInteger(MainSection, Channel, "AFCTimeout", &Config.LoRaDevices[Channel].AFCTimeout, NULL); + if (Config.LoRaDevices[Channel].AFCTimeout > 0) + { + LogMessage("AFC Timeout = %.0ds\n", Config.LoRaDevices[Channel].AFCTimeout); + } } // Clear any flags left over from a previous run @@ -1615,10 +1614,10 @@ void LoadConfigFile(void) } } - fclose( fp ); + fclose(fp); } -WINDOW *InitDisplay( void ) +WINDOW *InitDisplay(void) { WINDOW *mainwin; int Channel; @@ -1982,13 +1981,6 @@ rjh_post_message( int Channel, char *buffer ) Config.LoRaDevices[Channel].LastPacketAt = time( NULL ); - if ( Config.LoRaDevices[Channel].InCallingMode - && ( Config.CallingTimeout > 0 ) ) - { - Config.LoRaDevices[Channel].ReturnToCallingModeAt = - time( NULL ) + Config.CallingTimeout; - } - ShowPacketCounts( Channel ); } } @@ -1998,9 +1990,10 @@ rjh_post_message( int Channel, char *buffer ) int main( int argc, char **argv ) { int ch; - int LoopPeriod; + int LoopPeriod, MSPerLoop; int Channel; pthread_t SSDVThread, FTPThread, NetworkThread, HabitatThread, ServerThread; + struct TServerInfo JSONInfo; atexit(bye); @@ -2064,6 +2057,7 @@ int main( int argc, char **argv ) LoopPeriod = 0; + MSPerLoop = 10; // Initialise the vars stsv.parent_status = RUNNING; @@ -2101,15 +2095,18 @@ int main( int argc, char **argv ) // RJH close (telem_pipe_fd[0]); // Close the read side of the pipe as we are writing here - if ( Config.ServerPort > 0 ) + if (Config.ServerPort > 0) { - if ( pthread_create( &ServerThread, NULL, ServerLoop, NULL ) ) + JSONInfo.Port = Config.ServerPort; + JSONInfo.Connected = 0; + + if (pthread_create(&ServerThread, NULL, ServerLoop, (void *)(&JSONInfo))) { - fprintf( stderr, "Error creating server thread\n" ); + fprintf( stderr, "Error creating JSON server thread\n" ); return 1; } } - + if ( ( Config.NetworkLED >= 0 ) && ( Config.InternetLED >= 0 ) ) { if ( pthread_create( &NetworkThread, NULL, NetworkLoop, NULL ) ) @@ -2139,12 +2136,13 @@ int main( int argc, char **argv ) while ( run ) // && message_count< 9) // RJH Used for debug { - if ( ( ch = getch( ) ) != ERR ) + // Keypress tests + if ((ch = getch()) != ERR ) { ProcessKeyPress( ch ); } - /* RJH TEST */ + // RJH Test mode if ( message_count % 10 == 9 ) { if ( file_telem ) @@ -2169,9 +2167,8 @@ int main( int argc, char **argv ) message_count++; // We need to increment this here or we will lock } - - /* RJH TEST */ - if ( LoopPeriod > 1000 ) + + if (LoopPeriod > 1000) { // Every 1 second time_t now; @@ -2182,8 +2179,7 @@ int main( int argc, char **argv ) LoopPeriod = 0; - - for ( Channel = 0; Channel <= 1; Channel++ ) + for (Channel=0; Channel<=1; Channel++) { if ( Config.LoRaDevices[Channel].InUse ) { @@ -2191,11 +2187,7 @@ int main( int argc, char **argv ) ChannelPrintf( Channel, 12, 1, "Current RSSI = %4d ", CurrentRSSI(Channel)); - // if (Config.LoRaDevices[Channel].LastPacketAt > 0) - // { - // ChannelPrintf(Channel, 6, 1, "%us since last packet ", (unsigned int)(time(NULL) - Config.LoRaDevices[Channel].LastPacketAt)); - // } - + // Calling mode timeout? if ( Config.LoRaDevices[Channel].InCallingMode && ( Config.CallingTimeout > 0 ) && ( Config.LoRaDevices[Channel].ReturnToCallingModeAt > 0 ) @@ -2211,45 +2203,52 @@ int main( int argc, char **argv ) SetDefaultLoRaParameters( Channel ); setMode( Channel, RF98_MODE_RX_CONTINUOUS ); - - ChannelPrintf( Channel, 1, 1, "Channel %d %.3lfMHz %s mode", Channel, Config.LoRaDevices[Channel].Frequency, Modes[Config.LoRaDevices[Channel].SpeedMode]); } + + // AFC Timeout ? + if (!Config.LoRaDevices[Channel].InCallingMode && + (Config.LoRaDevices[Channel].AFCTimeout > 0) && + (Config.LoRaDevices[Channel].ReturnToOriginalFrequencyAt > 0) && + (time(NULL) > Config.LoRaDevices[Channel].ReturnToOriginalFrequencyAt)) + { + Config.LoRaDevices[Channel].ReturnToOriginalFrequencyAt = 0; - if ( ( Config.LoRaDevices[Channel].UplinkTime > 0 ) - && ( Config.LoRaDevices[Channel].UplinkCycle > 0 ) ) + LogMessage("AFC timeout - return to original frequency\n"); + + setMode(Channel, RF98_MODE_SLEEP); + setFrequency(Channel, Config.LoRaDevices[Channel].Frequency); + startReceiving(Channel); + } + + // Uplink cycle time ? + 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; - CycleSeconds = - ( tm->tm_hour * 3600 + tm->tm_min * 60 + - tm->tm_sec ) % - Config.LoRaDevices[Channel].UplinkCycle; - - if ( CycleSeconds == - Config.LoRaDevices[Channel].UplinkTime ) + 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); + 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 ) ) + // LEDs + if (LEDCounts[Channel] && ( Config.LoRaDevices[Channel].ActivityLED >= 0)) { if ( --LEDCounts[Channel] == 0 ) { - digitalWrite( Config.LoRaDevices[Channel]. - ActivityLED, 0 ); + digitalWrite(Config.LoRaDevices[Channel].ActivityLED, 0); } } } } } - delay( 10 ); - LoopPeriod += 10; + delay(MSPerLoop); + LoopPeriod += MSPerLoop; } LogMessage("Disabling DIO0 ISRs\n"); diff --git a/gateway.txt.old b/gateway.txt.old deleted file mode 100644 index a787d40..0000000 --- a/gateway.txt.old +++ /dev/null @@ -1,34 +0,0 @@ -tracker=LCARS -EnableHabitat=N -EnableSSDV=N -LogTelemetry=Y -LogPackets=Y -CallingTimeout=60 -ServerPort=6004 -Latitude=51.950230 -Longitude=-2.544500 -Antenna=MagMount -JPGFolder=SSDV -EnableDev=N - -#NetworkLED=21 -#InternetLED=22 -#ActivityLED_0=23 -#ActivityLED_1=24 - -frequency_0=434.450000 -mode_0=0 -#frequency_0=434.447 -#mode_0=1 -DIO0_0=6 -DIO5_0=5 -AFC_0=N - -frequency_1=869.850000 -mode_1=3 -#bandwidth_1=125K -#implicit_1=n -#coding_1=5 -DIO0_1=27 -DIO5_1=26 -AFC_1=N diff --git a/global.h b/global.h index d098738..e8d28b2 100644 --- a/global.h +++ b/global.h @@ -2,83 +2,70 @@ #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 { double Frequency; double Bandwidth; double CurrentBandwidth; - int InUse; - int DIO0; - int DIO5; - double activeFreq; - int AFC; - int SpeedMode; - int Power; - int PayloadLength; - int ImplicitOrExplicit; - int ErrorCoding; - int SpreadingFactor; - int LowDataRateOptimize; - 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; + int InUse; int DIO0; int DIO5; double activeFreq; + + int AFC; // Enable Automatic Frequency Control + double MaxAFCStep; // Maximum adjustment, in kHz, per packet + int AFCTimeout; // Revert to original frequency if no packets for this period (in seconds) + + int SpeedMode; + int Power; + int PayloadLength; + int ImplicitOrExplicit; + int ErrorCoding; + int SpreadingFactor; + int LowDataRateOptimize; + 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; + time_t ReturnToOriginalFrequencyAt; + int InCallingMode; + int ActivityLED; + double UplinkFrequency; + int UplinkMode; + int Speed, Heading, PredictedTime, CompassActual, CompassTarget, AirDirection, ServoLeft, ServoRight, ServoTime, FlightMode; + double cda, PredictedLandingSpeed, AirSpeed, GlideRatio; -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]; - }; + // Normal (non TDM) uplink + int UplinkTime; + int UplinkCycle; +}; struct TConfig { char Tracker[16]; // Callsign or name of receiver double latitude, longitude; // Receiver's location - 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; - char SMSFolder[64]; - char antenna[64]; - int EnableDev; - }; + 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; // JSON port for telemetry, settings + char SMSFolder[64]; + char antenna[64]; + int EnableDev; +}; typedef struct { int parent_status; unsigned long packet_count; @@ -95,6 +82,13 @@ typedef struct { char SSDV_Packet[257]; int Packet_Number; } ssdv_t; + +struct TServerInfo +{ + int Port; + int Connected; + int sockfd; +}; extern struct TConfig Config; extern int SSDVSendArrayIndex; diff --git a/habitat.c b/habitat.c index 8321900..e2394a9 100644 --- a/habitat.c +++ b/habitat.c @@ -145,10 +145,19 @@ UploadTelemetryPacket( telemetry_t * t ) } 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 ); + long http_code = 0; + + // Get http return code + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + + if (http_code != 403) + { + // Because 403 happens normally if we receive the same telemetry twice, which happens for example with airborne repeaters + 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 diff --git a/server.c b/server.c index 2659ae8..dd2f789 100644 --- a/server.c +++ b/server.c @@ -23,13 +23,12 @@ #include "global.h" extern bool run; -extern bool server_closed; -void ProcessClientLine(int connfd, char *line) +void ProcessJSONClientLine(int connfd, char *line) { line[strcspn(line, "\r\n")] = '\0'; // Get rid of CR LF - LogMessage("Received %s from client\n", line); + LogMessage("Received %s from JSON client\n", line); if (strchr(line, '=') == NULL) { @@ -79,140 +78,165 @@ void ProcessClientLine(int connfd, char *line) } } +int SendJSON(int connfd) +{ + int Channel, port_closed; + char sendBuff[1025]; + + port_closed = 0; + memset( sendBuff, '0', sizeof( sendBuff ) ); + + 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; + } + } + + return port_closed; +} void *ServerLoop( void *some_void_ptr ) { - int sockfd = 0; struct sockaddr_in serv_addr; + struct TServerInfo *ServerInfo; + + ServerInfo = (struct TServerInfo *)some_void_ptr; - char sendBuff[1025]; - - sockfd = socket( AF_INET, SOCK_STREAM, 0 ); + ServerInfo->sockfd = 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 ); + serv_addr.sin_port = htons(ServerInfo->Port); - LogMessage( "Listening on port %d\n", Config.ServerPort ); + LogMessage( "Listening on JSON port %d\n", ServerInfo->Port); - if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &( int ) - { - 1}, sizeof( int ) ) < 0 ) - { + if (setsockopt(ServerInfo->sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) + { LogMessage( "setsockopt(SO_REUSEADDR) failed" ); } - if ( bind - ( sockfd, ( struct sockaddr * ) &serv_addr, - sizeof( serv_addr ) ) < 0 ) + if (bind(ServerInfo->sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { LogMessage( "Server failed errno %d\n", errno ); exit( -1 ); } - listen( sockfd, 10 ); + listen(ServerInfo->sockfd, 10); - while ( run ) + while (run) { int SendEveryMS = 1000; int MSPerLoop=100; - int ms, port_closed, connfd; + int ms=0; + int connfd; + + Config.EnableDev=1; - fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) & ~O_NONBLOCK); // Blocking mode so we wait for a connection + fcntl(ServerInfo->sockfd, F_SETFL, fcntl(ServerInfo->sockfd, F_GETFL) & ~O_NONBLOCK); // Blocking mode so we wait for a connection - connfd = accept( sockfd, ( struct sockaddr * ) NULL, NULL ); // Wait for connection + connfd = accept(ServerInfo->sockfd, ( struct sockaddr * ) NULL, NULL ); // Wait for connection - LogMessage( "Connected to client\n" ); + LogMessage( "Connected to client\n"); + ServerInfo->Connected = 1; - fcntl(connfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK); // Non-blocking, so we don't block on receiving any commands from client + fcntl(connfd, F_SETFL, fcntl(ServerInfo->sockfd, F_GETFL) | O_NONBLOCK); // Non-blocking, so we don't block on receiving any commands from client - for ( port_closed = 0; !port_closed; ) + while (ServerInfo->Connected) { - int Channel; char packet[4096]; + int bytecount; - // Listen loop - for (ms=0; ms 0) + while ((bytecount = recv(connfd, packet, sizeof(packet), 0)) > 0) + { + char *line, *saveptr; + + packet[bytecount] = 0; + + // JSON server + line = strtok_r(packet, "\n", &saveptr); + while (line) { - char *line, *saveptr; - - packet[bytecount] = 0; - - line = strtok_r(packet, "\n", &saveptr); - while (line) + ProcessJSONClientLine(connfd, line); + line = strtok_r( NULL, "\n", &saveptr); + } + } + + if (bytecount == 0) + { + // -1 is no more data, 0 means port closed + LogMessage("Disconnected from client\n"); + ServerInfo->Connected = 0; + } + + if (ServerInfo->Connected) + { + // Send to JSON client + if (ms >= SendEveryMS) + { + if (SendJSON(connfd)) { - ProcessClientLine(connfd, line); - line = strtok_r( NULL, "\n", &saveptr); + ServerInfo->Connected = 0; } } - - delay(MSPerLoop); - } - - // Send part - // Build json - - 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; - } } + + delay(MSPerLoop); } - close( connfd ); + close(connfd); } return NULL; diff --git a/ssdv.c b/ssdv.c index 9b805e6..d3ec812 100644 --- a/ssdv.c +++ b/ssdv.c @@ -66,7 +66,6 @@ UploadImagePacket( ssdv_t * s, unsigned int packets ) 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 ); @@ -90,25 +89,22 @@ UploadImagePacket( ssdv_t * s, unsigned int packets ) // 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++ ) + for (PacketIndex = 0; PacketIndex < packets; PacketIndex++) { - base64_encode( s[PacketIndex].SSDV_Packet, 256, &base64_length, - base64_data ); + 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 ); + 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, "]}" ); + 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; @@ -144,10 +140,8 @@ UploadImagePacket( ssdv_t * s, unsigned int packets ) } } -void * -SSDVLoop( void *vars ) +void *SSDVLoop( void *vars ) { - if ( Config.EnableSSDV ) { const int max_packets = 51; @@ -185,11 +179,11 @@ SSDVLoop( void *vars ) if ( j == 50 || ( ( packets == 0 ) && ( j > 0 ) ) ) { - ChannelPrintf( s[0].Channel, 6, 1, "Habitat" ); - + ChannelPrintf(s[0].Channel, 6, 16, "SSDV"); + UploadImagePacket( s, j ); - - ChannelPrintf( s[0].Channel, 6, 1, " " ); + + ChannelPrintf(s[0].Channel, 6, 16, " "); j = 0; diff --git a/ssdv_resend.py b/ssdv_resend.py index 9e8d179..b7c7bf2 100644 --- a/ssdv_resend.py +++ b/ssdv_resend.py @@ -1,103 +1,101 @@ -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) - +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 = '' + + 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 + + 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 len(line) <= 2: + print("No missing packets") + 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="") + sys.stdout.flush() + time.sleep(1) +