V1.8.5 - AFC changes; rename config file from sample if missing

pull/27/head
Dave Akerman 2016-09-27 19:23:41 +00:00
rodzic 7b262838e0
commit 1210ec15cf
11 zmienionych plików z 444 dodań i 474 usunięć

30
*.dpa
Wyświetl plik

@ -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

Wyświetl plik

@ -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
-------------------

0
config.c 100755 → 100644
Wyświetl plik

0
config.h 100755 → 100644
Wyświetl plik

229
gateway.c
Wyświetl plik

@ -14,7 +14,7 @@
#include <sys/shm.h>
#include <errno.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdarg.h>
#include <pthread.h>
#include <curses.h>
#include <math.h>
@ -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");

Wyświetl plik

@ -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

134
global.h
Wyświetl plik

@ -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;

Wyświetl plik

@ -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

222
server.c
Wyświetl plik

@ -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<SendEveryMS; ms+=MSPerLoop)
{
int bytecount;
ms += MSPerLoop;
// Listen part
bytecount = -1;
while ((bytecount = recv(connfd, packet, sizeof(packet), 0)) > 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;

32
ssdv.c
Wyświetl plik

@ -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;

Wyświetl plik

@ -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 <payload_id> [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 <payload_id> [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)