diff --git a/.rej b/.rej
new file mode 100644
index 0000000..1137117
--- /dev/null
+++ b/.rej
@@ -0,0 +1,139 @@
+--- APRSCode.c
++++ APRSCode.c
+@@ -3674,7 +3674,7 @@
+ if (ptr1)
+ *ptr1 = 0;
+
+-// Debugprintf("Duplicate Message supressed %s", Msg);
++// Debugprintf("Duplicate Message suppressed %s", Msg);
+ return TRUE; // Duplicate
+ }
+ }
+--- BPQChat.rc
++++ BPQChat.rc
+@@ -162,7 +162,7 @@
+ WS_VSCROLL
+ DEFPUSHBUTTON "Save Welcome Message",SAVEWELCOME,140,296,91,14,
+ BS_CENTER | BS_VCENTER
+- LTEXT " If the node is not directly connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT",
++ LTEXT " If the node is not directly connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands separated by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT",
+ IDC_STATIC,9,52,355,24
+ END
+
+--- BPQMail.rc
++++ BPQMail.rc
+@@ -1045,7 +1045,7 @@
+ CONTROL "Delete Log and Message Files to Recycle Bin",
+ IDC_DELETETORECYCLE,"Button",BS_AUTOCHECKBOX |
+ BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,5,142,115,20
+- CONTROL "Supress Mailing of Housekeeping Results",
++ CONTROL "Suppress Mailing of Housekeeping Results",
+ IDC_MAINTNOMAIL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
+ BS_MULTILINE | WS_TABSTOP,5,182,115,20
+ CONTROL "Generate Traffic Report",IDC_MAINTTRAFFIC,"Button",
+--- HanksRT.c
++++ HanksRT.c
+@@ -1186,7 +1186,7 @@
+ // Duplicate, so discard, but save time
+
+ DupInfo[i].DupTime = Now;
+- Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s supressed", Call, Msg);
++ Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s suppressed", Call, Msg);
+
+ return TRUE; // Duplicate
+ }
+--- RigControl.c
++++ RigControl.c
+@@ -8385,7 +8385,7 @@
+
+ switch (Msg[0])
+ {
+- case 'f': // Get Freqency
++ case 'f': // Get Frequency
+
+ HLGetFreq(Sock, RIG, sep);
+ return 0;
+--- UZ7HODrv.c
++++ UZ7HODrv.c
+@@ -374,7 +374,7 @@
+ {
+ // Read Freq
+
+- buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Freqency %d\r", AGW->CenterFreq);
++ buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Frequency %d\r", AGW->CenterFreq);
+ return 1;
+ }
+
+@@ -382,7 +382,7 @@
+
+ if (AGW->CenterFreq == 0)
+ {
+- buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Invalid Modem Freqency\r");
++ buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Invalid Modem Frequency\r");
+ return 1;
+ }
+
+--- WinRPRHelper.c
++++ WinRPRHelper.c
+@@ -111,7 +111,7 @@
+
+ if (argc < 3)
+ {
+- printf ("Missing paramters - you need COM port and IP Address and rigctl port of BPQ, eg \r\n"
++ printf ("Missing parameters - you need COM port and IP Address and rigctl port of BPQ, eg \r\n"
+ " WinRPRHelper com10 192.168.1.64:4532\r\n\r\n"
+ "Press any key to exit\r\n");
+
+--- config.c
++++ config.c
+@@ -649,7 +649,7 @@
+ if (LOCATOR[0] == 0 && LocSpecified == 0 && RFOnly == 0)
+ {
+ Consoleprintf("");
+- Consoleprintf("Please enter a LOCATOR statment in your BPQ32.cfg");
++ Consoleprintf("Please enter a LOCATOR statement in your BPQ32.cfg");
+ Consoleprintf("If you really don't want to be on the Node Map you can enter LOCATOR=NONE");
+ Consoleprintf("");
+
+--- kiss.c
++++ kiss.c
+@@ -1485,7 +1485,7 @@
+ }
+ }
+ else
+- Debugprintf("Polled KISS - response from wrong address - Polled %d Reponse %d",
++ Debugprintf("Polled KISS - response from wrong address - Polled %d Response %d",
+ KISS->POLLPOINTER->OURCTRL, (Port->RXMSG[0] & 0xf0));
+
+ goto SeeifMore; // SEE IF ANYTHING ELSE
+--- templatedefs.c
++++ templatedefs.c
+@@ -1165,7 +1165,7 @@
+ "Send Non-delivery Notifications \r\n"
+ "for P and T messages \r\n"
+ " \r\n"
+- "Supress Mailing of \r\n"
++ "Suppress Mailing of \r\n"
+ "Housekeeping Result
\r\n"
+ "Generate Traffic Report
\r\n"
+ "
\r\n"
+@@ -1454,7 +1454,7 @@
+ "
The Nodes to link to box defines which other Chat Nodes should be connected to, or from which "
+ "connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. If the node is not directly "
+ "connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands "
+- "seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT"
++ "separated by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT"
+
+ "
The Callsign of the Chat Node is not defined here - it is obtained from the bpq32.cfg APPLICATION line corresponding to the Chat Appl Number. \r\n"
+ "
\n"
+--- WebMail.c
++++ WebMail.c
+@@ -2020,7 +2020,7 @@
+ "document.getElementById('myform').action = '/WebMail/QuoteOriginal' + '?%s';"
+ " document.getElementById('myform').submit();}"
+ "";
++ "value='Include Original Msg'>";
+
+ char Temp[1024];
+ char ReplyAddr[128];
diff --git a/AGWAPI.c b/AGWAPI.c
index 80c1ce4..462fe86 100644
--- a/AGWAPI.c
+++ b/AGWAPI.c
@@ -128,7 +128,7 @@ int DataSocket_Write(struct AGWSocketConnectionInfo * sockptr, SOCKET sock);
int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr);
int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr);
int SendDataToAppl(int Stream, byte * Buffer, int Length);
-int InternalAGWDecodeFrame(char * msg, char * buffer, int Stamp, int * FrameType, int useLocalTime, int doNodes);
+int InternalAGWDecodeFrame(char * msg, char * buffer, time_t Stamp, int * FrameType, int useLocalTime, int doNodes);
int AGWDataSocket_Disconnect( struct AGWSocketConnectionInfo * sockptr);
int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length);
int ShowApps();
@@ -402,7 +402,7 @@ int SetUpHostSessions()
extern struct DATAMESSAGE * REPLYBUFFER;
extern BOOL AGWActive;
-VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
// DISPLAY AGW Session Status
diff --git a/AGWMoncode.c b/AGWMoncode.c
index 84f4fe3..bd5a7e8 100644
--- a/AGWMoncode.c
+++ b/AGWMoncode.c
@@ -61,7 +61,6 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
#define NODES_SIG 0xFF
-char * strlop(char * buf, char delim);
UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, int msglen);
static UCHAR * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen, int DoNodes);
diff --git a/AISCommon.c b/AISCommon.c
index a193d2f..d33f25a 100644
--- a/AISCommon.c
+++ b/AISCommon.c
@@ -832,6 +832,7 @@ void SaveNavAidDataBase()
char FN[256];
struct NAVAIDRECORD * navptr;
+
if (BPQDirectory[0] == 0)
{
strcpy(FN, "AIS_NavAids.txt");
@@ -2372,6 +2373,8 @@ void ProcessAISNavAidMessage()
NavAidCount++;
ProcessAISNavAidMessage();
+ NavAidDBChanged = 1;
+
return;
diff --git a/APRSCode.c b/APRSCode.c
index b8f02f0..497ea74 100644
--- a/APRSCode.c
+++ b/APRSCode.c
@@ -22,7 +22,6 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
// First Version, November 2011
#pragma data_seg("_BPQDATA")
-
#define _CRT_SECURE_NO_DEPRECATE
#include
@@ -63,7 +62,6 @@ VOID __cdecl Debugprintf(const char * format, ...);
VOID __cdecl Consoleprintf(const char * format, ...);
BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port);
VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
-char * strlop(char * buf, char delim);
int APRSDecodeFrame(char * msg, char * buffer, time_t Stamp, uint64_t Mask); // Unsemaphored DecodeFrame
APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port);
BOOL CheckforDups(char * Call, char * Msg, int Len);
@@ -349,7 +347,7 @@ APRSHEARDRECORD MHTABLE[MAXHEARD] = {0};
APRSHEARDRECORD * MHDATA = &MHTABLE[0];
-static SOCKET sock = (SOCKET)0;
+static SOCKET sock = 0;
//Duplicate suppression Code
@@ -3114,7 +3112,7 @@ VOID APRSISThread(void * Report)
BOOL bcopt=TRUE;
char Buffer[1000];
int InputLen = 1; // Non-zero
- char errmsg[100];
+ char errmsg[300];
char * ptr;
size_t inptr = 0;
char APRSinMsg[1000];
@@ -3675,7 +3673,7 @@ BOOL CheckforDups(char * Call, char * Msg, int Len)
if (ptr1)
*ptr1 = 0;
-// Debugprintf("Duplicate Message supressed %s", Msg);
+// Debugprintf("Duplicate Message suppressed %s", Msg);
return TRUE; // Duplicate
}
}
@@ -7667,7 +7665,7 @@ VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE)
}
- OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer);
+ OutputLen += sprintf(&OutBuffer[OutputLen], "%s", WebTrailer);
HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
sendandcheck(sock, Header, HeaderLen);
@@ -7712,7 +7710,7 @@ VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE)
}
- OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer);
+ OutputLen += sprintf(&OutBuffer[OutputLen], "%s", WebTrailer);
HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
sendandcheck(sock, Header, HeaderLen);
@@ -8114,7 +8112,7 @@ extern char OrigCmdBuffer[81];
BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr);
-VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
// APRS Subcommands. Default for compatibility is APRSMH
@@ -9179,7 +9177,7 @@ void GetSavedAPRSMessages()
if ((file = fopen(FN, "r")) == NULL)
return ;
- while (fgets(Line, 512, file))
+ while (fgets(Line, sizeof(Line), file))
{
Stamp = Line;
From = strlop(Stamp, ' ');
diff --git a/APRSCode.c.orig b/APRSCode.c.orig
new file mode 100644
index 0000000..b4ed594
--- /dev/null
+++ b/APRSCode.c.orig
@@ -0,0 +1,9223 @@
+/*
+Copyright 2001-2022 John Wiseman G8BPQ
+
+This file is part of LinBPQ/BPQ32.
+
+LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+LinBPQ/BPQ32 is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
+*/
+
+// Module to implement APRS "New Paradigm" Digipeater and APRS-IS Gateway
+
+// First Version, November 2011
+
+#pragma data_seg("_BPQDATA")
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+#include
+#include "CHeaders.h"
+#include "bpq32.h"
+#include
+#include "kernelresource.h"
+
+#include "tncinfo.h"
+
+#include "bpqaprs.h"
+
+#ifndef WIN32
+
+#include
+#include
+#include
+
+int sfd;
+struct sockaddr_un my_addr, peer_addr;
+socklen_t peer_addr_size;
+
+
+#endif
+
+
+#define MAXAGE 3600 * 12 // 12 Hours
+#define MAXCALLS 20 // Max Flood, Trace and Digi
+#define GATETIMELIMIT 40 * 60 // Don't gate to RF if station not heard for this time (40 mins)
+
+static BOOL APIENTRY GETSENDNETFRAMEADDR();
+static VOID DoSecTimer();
+static VOID DoMinTimer();
+static int APRSProcessLine(char * buf);
+static BOOL APRSReadConfigFile();
+VOID APRSISThread(void * Report);
+VOID __cdecl Debugprintf(const char * format, ...);
+VOID __cdecl Consoleprintf(const char * format, ...);
+BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port);
+VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
+int APRSDecodeFrame(char * msg, char * buffer, time_t Stamp, uint64_t Mask); // Unsemaphored DecodeFrame
+APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port);
+BOOL CheckforDups(char * Call, char * Msg, int Len);
+VOID ProcessQuery(char * Query);
+VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis);
+VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len);
+VOID SendBeacon(int toPort, char * Msg, BOOL SendISStatus, BOOL SendSOGCOG);
+Dll BOOL APIENTRY PutAPRSMessage(char * Frame, int Len);
+VOID ProcessAPRSISMsg(char * APRSMsg);
+static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
+APRSHEARDRECORD * FindStationInMH(char * call);
+BOOL OpenGPSPort();
+void PollGPSIn();
+int CountLocalStations();
+BOOL SendAPPLAPRSMessage(char * Frame);
+VOID SendAPRSMessage(char * Message, int toPort);
+static VOID TCPConnect(void * unuxed);
+struct STATIONRECORD * DecodeAPRSISMsg(char * msg);
+struct STATIONRECORD * ProcessRFFrame(char * buffer, int len, int * ourMessage);
+VOID APRSSecTimer();
+double myDistance(double laa, double loa, BOOL KM);
+struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFound);
+int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station);
+BOOL KillOldTNC(char * Path);
+
+BOOL ToLOC(double Lat, double Lon , char * Locator);
+BOOL InternalSendAPRSMessage(char * Text, char * Call);
+void UndoTransparency(char * input);
+char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...);
+char * GetStandardPage(char * FN, int * Len);
+VOID WriteMiniDump();
+BOOL ProcessConfig();
+int ProcessAISMessage(char * msg, int len);
+int read_png(unsigned char *bytes);
+VOID sendandcheck(SOCKET sock, const char * Buffer, int Len);
+void SaveAPRSMessage(struct APRSMESSAGE * ptr);
+void ClearSavedMessages();
+void GetSavedAPRSMessages();
+static VOID GPSDConnect(void * unused);
+int CanPortDigi(int Port);
+int FromLOC(char * Locator, double * pLat, double * pLon);
+
+extern int SemHeldByAPI;
+extern int APRSMONDECODE();
+extern struct ConsoleInfo MonWindow;
+extern char VersionString[];
+
+BOOL SaveAPRSMsgs = 0;
+
+BOOL LogAPRSIS = FALSE;
+
+// All data should be initialised to force into shared segment
+
+static char ConfigClassName[]="CONFIG";
+
+extern BPQVECSTRUC * APRSMONVECPTR;
+
+extern int MONDECODE();
+extern VOID * zalloc(int len);
+extern BOOL StartMinimized;
+
+extern char TextVerstring[];
+
+extern HWND hConsWnd;
+extern HKEY REGTREE;
+
+extern char LOCATOR[80];
+extern char LOC[7];
+
+static int SecTimer = 10;
+static int MinTimer = 60;
+
+BOOL APRSApplConnected = FALSE;
+BOOL APRSWeb = FALSE;
+
+void * APPL_Q = 0; // Queue of frames for APRS Appl
+void * APPLTX_Q = 0; // Queue of frames from APRS Appl
+uint64_t APRSPortMask = 0;
+
+char APRSCall[10] = "";
+char APRSDest[10] = "APBPQ1";
+
+char WXCall[10];
+
+UCHAR AXCall[7] = "";
+
+char CallPadded[10] = " ";
+
+char GPSPort[80] = "";
+int GPSSpeed = 0;
+char GPSRelay[80] = "";
+
+BOOL GateLocal = FALSE;
+double GateLocalDistance = 0.0;
+
+int MaxDigisforIS = 7; // Dont send to IS if more digis uued to reach us
+
+char WXFileName[MAX_PATH];
+char WXComment[80];
+BOOL SendWX = FALSE;
+int WXInterval = 30;
+int WXCounter = 29 * 60;
+
+char APRSCall[10];
+char LoppedAPRSCall[10];
+
+BOOL WXPort[MaxBPQPortNo + 1]; // Ports to send WX to
+
+BOOL GPSOK = 0;
+
+char LAT[] = "0000.00N"; // in standard APRS Format
+char LON[] = "00000.00W"; //in standard APRS Format
+
+char HostName[80]; // for BlueNMEA
+int HostPort = 4352;
+
+char GPSDHost[80];
+int GPSDPort = 2947;
+
+
+extern int ADSBPort;
+extern char ADSBHost[];
+
+BOOL BlueNMEAOK = FALSE;
+int BlueNMEATimer = 0;
+
+BOOL GPSDOK = FALSE;
+int GPSDTimer = 0;
+
+
+BOOL GPSSetsLocator = 0; // Update Map Location from GPS
+
+double SOG, COG; // From GPS
+
+double Lat = 0.0;
+double Lon = 0.0;
+
+BOOL PosnSet = FALSE;
+/*
+The null position should be include the \. symbol (unknown/indeterminate
+position). For example, a Position Report for a station with unknown position
+will contain the coordinates …0000.00N\00000.00W.…
+*/
+char * FloodCalls = 0; // Calls to relay using N-n without tracing
+char * TraceCalls = 0; // Calls to relay using N-n with tracing
+char * DigiCalls = 0; // Calls for normal relaying
+
+UCHAR FloodAX[MAXCALLS][7] = {0};
+UCHAR TraceAX[MAXCALLS][7] = {0};
+UCHAR DigiAX[MAXCALLS][7] = {0};
+
+int FloodLen[MAXCALLS];
+int TraceLen[MAXCALLS];
+int DigiLen[MAXCALLS];
+
+int ISPort = 0;
+char ISHost[256] = "";
+int ISPasscode = 0;
+char NodeFilter[1000] = "m/50"; // Filter when the isn't an application
+char ISFilter[1000] = "m/50"; // Current Filter
+char APPLFilter[1000] = ""; // Filter when an Applcation is running
+
+extern BOOL IGateEnabled;
+
+char StatusMsg[256] = ""; // Must be in shared segment
+int StatusMsgLen = 0;
+
+char * BeaconPath[65] = {0};
+
+char CrossPortMap[65][65] = {0};
+char APRSBridgeMap[65][65] = {0};
+
+UCHAR BeaconHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis
+int BeaconHddrLen[65] = {0}; // Actual Length used
+
+UCHAR GatedHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis for messages gated from IS
+int GatedHddrLen[65] = {0}; // Actual Length used
+
+
+char CFGSYMBOL = 'a';
+char CFGSYMSET = 'B';
+
+char SYMBOL = '='; // Unknown Locaton
+char SYMSET = '/';
+
+char * PHG = 0; // Optional PHG (Power-Height-Gain) string for beacon
+
+BOOL TraceDigi = FALSE; // Add Trace to packets relayed on Digi Calls
+BOOL SATGate = FALSE; // Delay Gating to IS directly heard packets
+BOOL RXOnly = FALSE; // Run as RX only IGATE, ie don't gate anything to RF
+
+BOOL DefaultLocalTime = FALSE;
+BOOL DefaultDistKM = FALSE;
+
+int multiple = 0; // Allows multiple copies of LinBPQ/APRS on one machine
+
+extern BOOL needAIS;
+
+extern unsigned long long IconData[]; // Symbols as a png image.
+
+typedef struct _ISDELAY
+{
+ struct _ISDELAY * Next;
+ char * ISMSG;
+ time_t SendTIme;
+} ISDELAY;
+
+ISDELAY * SatISQueue = NULL;
+
+int MaxTraceHops = 2;
+int MaxFloodHops = 2;
+
+int BeaconInterval = 0;
+int MobileBeaconInterval = 0;
+time_t LastMobileBeacon = 0;
+int BeaconCounter = 0;
+int IStatusCounter = 3600; // Used to send ?ISTATUS? Responses
+//int StatusCounter = 0; // Used to send Status Messages
+
+char RunProgram[128] = ""; // Program to start
+
+BOOL APRSISOpen = FALSE;
+BOOL BeacontoIS = TRUE;
+
+int ISDelayTimer = 0; // Time before trying to reopen APRS-IS link
+
+char APRSDESTS[][7] = {"AIR*", "ALL*", "AP*", "BEACON", "CQ*", "GPS*", "DF*", "DGPS*", "DRILL*",
+ "DX*", "ID*", "JAVA*", "MAIL*", "MICE*", "QST*", "QTH*", "RTCM*", "SKY*",
+ "SPACE*", "SPC*", "SYM*", "TEL*", "TEST*", "TLM*", "WX*", "ZIP"};
+
+UCHAR AXDESTS[30][7] = {""};
+int AXDESTLEN[30] = {0};
+
+UCHAR axTCPIP[7];
+UCHAR axRFONLY[7];
+UCHAR axNOGATE[7];
+
+int MessageCount = 0;
+
+struct PortInfo
+{
+ int Index;
+ int ComPort;
+ char PortType[2];
+ BOOL NewVCOM; // Using User Mode Virtual COM Driver
+ int ReopenTimer; // Retry if open failed delay
+ int RTS;
+ int CTS;
+ int DCD;
+ int DTR;
+ int DSR;
+ char Params[20]; // Init Params (eg 9600,n,8)
+ char PortLabel[20];
+ HANDLE hDevice;
+ BOOL Created;
+ BOOL PortEnabled;
+ int FLOWCTRL;
+ int gpsinptr;
+#ifdef WIN32
+ OVERLAPPED Overlapped;
+ OVERLAPPED OverlappedRead;
+#endif
+ char GPSinMsg[160];
+ int GPSTypeFlag; // GPS Source flags
+ BOOL RMCOnly; // Only send RMC msgs to this port
+};
+
+
+
+struct PortInfo InPorts[1] = {0};
+
+// Heard Station info
+
+#define MAXHEARD 1000
+
+int HEARDENTRIES = 0;
+int MAXHEARDENTRIES = 0;
+int MHLEN = sizeof(APRSHEARDRECORD);
+
+// Area is allocated as needed
+
+APRSHEARDRECORD MHTABLE[MAXHEARD] = {0};
+
+APRSHEARDRECORD * MHDATA = &MHTABLE[0];
+
+static SOCKET sock = (SOCKET)0;
+
+//Duplicate suppression Code
+
+#define MAXDUPS 100 // Number to keep
+#define DUPSECONDS 28 // Time to Keep
+
+struct DUPINFO
+{
+ time_t DupTime;
+ int DupLen;
+ char DupUser[8]; // Call in ax.35 format
+ char DupText[100];
+};
+
+struct DUPINFO DupInfo[MAXDUPS];
+
+struct OBJECT
+{
+ struct OBJECT * Next;
+ UCHAR Path[10][7]; // Dest, Source and up to 8 digis
+ int PathLen; // Actual Length used
+ char Message[81];
+ char PortMap[MaxBPQPortNo + 1];
+ int Interval;
+ int Timer;
+};
+
+struct OBJECT * ObjectList; // List of objects to send;
+
+int ObjectCount = 0;
+
+#include
+
+#define M_PI 3.14159265358979323846
+
+int RetryCount = 4;
+int RetryTimer = 45;
+int ExpireTime = 120;
+int TrackExpireTime = 1440;
+BOOL SuppressNullPosn = FALSE;
+BOOL DefaultNoTracks = FALSE;
+
+int MaxStations = 1000;
+
+int SharedMemorySize = 0;
+
+
+RECT Rect, MsgRect, StnRect;
+
+char Key[80];
+
+// function prototypes
+
+VOID RefreshMessages();
+
+// a few global variables
+
+char APRSDir[MAX_PATH] = "BPQAPRS";
+char DF[MAX_PATH];
+
+#define FEND 0xC0 // KISS CONTROL CODES
+#define FESC 0xDB
+#define TFEND 0xDC
+#define TFESC 0xDD
+
+int StationCount = 0;
+
+UCHAR NextSeq = 1;
+
+// Stationrecords are stored in a shared memory segment. based at APRSStationMemory (normally 0x43000000)
+
+// A pointer to the first is placed at the start of this
+
+struct STATIONRECORD ** StationRecords = NULL;
+struct STATIONRECORD * StationRecordPool = NULL;
+struct APRSMESSAGE * MessageRecordPool = NULL;
+
+struct SharedMem * SMEM;
+
+UCHAR * Shared;
+UCHAR * StnRecordBase;
+
+VOID SendObject(struct OBJECT * Object);
+VOID MonitorAPRSIS(char * Msg, int MsgLen, BOOL TX);
+
+#ifndef WIN32
+#define WSAEWOULDBLOCK 11
+#endif
+
+HANDLE hMapFile;
+
+// Logging
+
+static int LogAge = 14;
+
+#ifdef WIN32
+
+int DeleteAPRSLogFiles()
+{
+ WIN32_FIND_DATA ffd;
+
+ char szDir[MAX_PATH];
+ char File[MAX_PATH];
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ DWORD dwError=0;
+ LARGE_INTEGER ft;
+ time_t now = time(NULL);
+ int Age;
+
+ // Prepare string for use with FindFile functions. First, copy the
+ // string to a buffer, then append '\*' to the directory name.
+
+ strcpy(szDir, GetLogDirectory());
+ strcat(szDir, "/logs/APRS*.log");
+
+ // Find the first file in the directory.
+
+ hFind = FindFirstFile(szDir, &ffd);
+
+ if (INVALID_HANDLE_VALUE == hFind)
+ return dwError;
+
+ // Walk directory
+
+ do
+ {
+ if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ OutputDebugString(ffd.cFileName);
+ }
+ else
+ {
+ ft.HighPart = ffd.ftCreationTime.dwHighDateTime;
+ ft.LowPart = ffd.ftCreationTime.dwLowDateTime;
+
+ ft.QuadPart -= 116444736000000000;
+ ft.QuadPart /= 10000000;
+
+ Age = (int)((now - ft.LowPart) / 86400);
+
+ if (Age > LogAge)
+ {
+ sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0);
+ Debugprintf("Deleting %s", File);
+ DeleteFile(File);
+ }
+ }
+ }
+ while (FindNextFile(hFind, &ffd) != 0);
+
+ FindClose(hFind);
+ return dwError;
+}
+
+#else
+
+#include
+
+int APRSFilter(const struct dirent * dir)
+{
+ return (memcmp(dir->d_name, "APRS", 4) == 0 && strstr(dir->d_name, ".log"));
+}
+
+int DeleteAPRSLogFiles()
+{
+ struct dirent **namelist;
+ int n;
+ struct stat STAT;
+ time_t now = time(NULL);
+ int Age = 0, res;
+ char FN[256];
+
+ n = scandir("logs", &namelist, APRSFilter, alphasort);
+
+ if (n < 0)
+ perror("scandir");
+ else
+ {
+ while(n--)
+ {
+ sprintf(FN, "logs/%s", namelist[n]->d_name);
+ if (stat(FN, &STAT) == 0)
+ {
+ Age = (now - STAT.st_mtime) / 86400;
+
+ if (Age > LogAge)
+ {
+ Debugprintf("Deleting %s\n", FN);
+ unlink(FN);
+ }
+ }
+ free(namelist[n]);
+ }
+ free(namelist);
+ }
+ return 0;
+}
+#endif
+
+int APRSWriteLog(char * msg)
+{
+ FILE *file;
+ UCHAR Value[MAX_PATH];
+ time_t T;
+ struct tm * tm;
+
+ if (LogAPRSIS == 0)
+ return 0;
+
+ if (strchr(msg, '\n') == 0)
+ strcat(msg, "\r\n");
+
+ T = time(NULL);
+ tm = gmtime(&T);
+
+ if (GetLogDirectory()[0] == 0)
+ {
+ strcpy(Value, "logs/APRS_");
+ }
+ else
+ {
+ strcpy(Value, GetLogDirectory());
+ strcat(Value, "/");
+ strcat(Value, "logs/APRS_");
+ }
+
+ sprintf(Value, "%s%02d%02d%02d.log", Value,
+ tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday);
+
+ if ((file = fopen(Value, "ab")) == NULL)
+ return FALSE;
+
+ fputs(msg, file);
+ fclose(file);
+ return 0;
+}
+
+
+int ISSend(SOCKET sock, char * Msg, int Len, int flags)
+{
+ int Loops = 0;
+ int Sent;
+
+ MonitorAPRSIS(Msg, Len, TRUE);
+
+ Sent = send(sock, Msg, Len, flags);
+
+ while (Sent != Len && Loops++ < 300) // 10 secs max
+ {
+ if ((Sent == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK))
+ return SOCKET_ERROR;
+
+ if (Sent > 0) // something sent
+ {
+ Len -= Sent;
+ memmove(Msg, &Msg[Sent], Len);
+ }
+
+ Sleep(30);
+ Sent = send(sock, Msg, Len, flags);
+ }
+
+ return Sent;
+}
+
+void * endofStations;
+
+Dll BOOL APIENTRY Init_APRS()
+{
+ int i;
+ char * DCall;
+
+#ifdef WIN32
+ HKEY hKey=0;
+ int retCode, Vallen, Type;
+#else
+ int fd;
+ char RX_SOCK_PATH[] = "BPQAPRSrxsock";
+ char TX_SOCK_PATH[] = "BPQAPRStxsock";
+ char SharedName[256];
+ char * ptr1;
+#endif
+ struct STATIONRECORD * Stn1, * Stn2;
+ struct APRSMESSAGE * Msg1, * Msg2;
+
+ // Clear tables in case a restart
+
+ StationRecords = NULL;
+
+ StationCount = 0;
+ HEARDENTRIES = 0;
+ MAXHEARDENTRIES = 0;
+ MobileBeaconInterval = 0;
+ BeaconInterval = 0;
+
+ DeleteAPRSLogFiles();
+
+ memset(MHTABLE, 0, sizeof(MHTABLE));
+
+ ConvToAX25(MYNODECALL, MYCALL);
+
+ ConvToAX25("TCPIP", axTCPIP);
+ ConvToAX25("RFONLY", axRFONLY);
+ ConvToAX25("NOGATE", axNOGATE);
+
+ memset(&FloodAX[0][0], 0, sizeof(FloodAX));
+ memset(&TraceAX[0][0], 0, sizeof(TraceAX));
+ memset(&DigiAX[0][0], 0, sizeof(DigiAX));
+
+ APRSPortMask = 0;
+
+ memset(BeaconPath, sizeof(BeaconPath), 0);
+
+ memset(&CrossPortMap[0][0], 0, sizeof(CrossPortMap));
+ memset(&APRSBridgeMap[0][0], 0, sizeof(APRSBridgeMap));
+
+ for (i = 1; i <= MaxBPQPortNo; i++)
+ {
+ if (CanPortDigi(i))
+ CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port
+ CrossPortMap[i][0] = TRUE; // and APRS-IS
+ }
+
+ PosnSet = 0;
+ ObjectList = NULL;
+ ObjectCount = 0;
+
+ ISPort = ISHost[0] = ISPasscode = 0;
+
+ if (APRSReadConfigFile() == 0)
+ return FALSE;
+
+ if (APRSCall[0] == 0)
+ {
+ strcpy(APRSCall, MYNODECALL);
+ strlop(APRSCall, ' ');
+ strcpy(LoppedAPRSCall, APRSCall);
+ memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging
+ ConvToAX25(APRSCall, AXCall);
+ }
+
+ if (WXCall[0] == 0)
+ strcpy(WXCall, APRSCall);
+
+ // Caluclate size of Shared Segment
+
+ SharedMemorySize = sizeof(struct STATIONRECORD) * (MaxStations + 4) +
+ sizeof(struct APRSMESSAGE) * (MAXMESSAGES + 4) + 32; // 32 for header
+
+
+#ifndef WIN32
+
+ // Create a Shared Memory Object
+
+ Shared = NULL;
+
+ // Append last bit of current directory to shared name
+
+ ptr1 = BPQDirectory;
+
+ while (strchr(ptr1, '/'))
+ {
+ ptr1 = strchr(ptr1, '/');
+ ptr1++;
+ }
+
+ if (multiple)
+ sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1);
+ else
+ strcpy(SharedName, "/BPQAPRSSharedMem");
+
+ printf("Using Shared Memory %s\n", SharedName);
+
+#ifndef WIN32
+
+ fd = shm_open(SharedName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+ if (fd == -1)
+ {
+ perror("Create Shared Memory");
+ printf("Create APRS Shared Memory Failed\n");
+ }
+ else
+ {
+ if (ftruncate(fd, SharedMemorySize))
+ {
+ perror("Extend Shared Memory");
+ printf("Extend APRS Shared Memory Failed\n");
+ }
+ else
+ {
+ // Map shared memory object
+
+ Shared = mmap((void *)APRSSHAREDMEMORYBASE,
+ SharedMemorySize,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (Shared == MAP_FAILED)
+ {
+ perror("Map Shared Memory");
+ printf("Map APRS Shared Memory Failed\n");
+ Shared = NULL;
+ }
+
+ if (Shared != (void *)APRSSHAREDMEMORYBASE)
+ {
+ printf("Map APRS Shared Memory Allocated at %x\n", Shared);
+ Shared = NULL;
+ }
+
+ }
+ }
+
+#endif
+
+ printf("Map APRS Shared Memory Allocated at %p\n", Shared);
+
+ if (Shared == NULL)
+ {
+ printf("APRS not using shared memory\n");
+ Shared = malloc(SharedMemorySize);
+ printf("APRS Non-Shared Memory Allocated at %x\n", Shared);
+ }
+
+#else
+
+#ifndef LINBPQ
+
+ retCode = RegOpenKeyEx (REGTREE,
+ "SOFTWARE\\G8BPQ\\BPQ32",
+ 0,
+ KEY_QUERY_VALUE,
+ &hKey);
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ Vallen = 4;
+ retCode = RegQueryValueEx(hKey, "IGateEnabled", 0, &Type, (UCHAR *)&IGateEnabled, &Vallen);
+ }
+
+#endif
+
+ // Create Memory Mapping for Station List
+
+ hMapFile = CreateFileMapping(
+ INVALID_HANDLE_VALUE, // use paging file
+ NULL, // default security
+ PAGE_READWRITE, // read/write access
+ 0, // maximum object size (high-order DWORD)
+ SharedMemorySize, // maximum object size (low-order DWORD)
+ "BPQAPRSStationsMappingObject");// name of mapping object
+
+ if (hMapFile == NULL)
+ {
+ Consoleprintf("Could not create file mapping object (%d).\n", GetLastError());
+ return 0;
+ }
+
+ UnmapViewOfFile((void *)APRSSHAREDMEMORYBASE);
+
+
+ Shared = (LPTSTR) MapViewOfFileEx(hMapFile, // handle to map object
+ FILE_MAP_ALL_ACCESS, // read/write permission
+ 0,
+ 0,
+ SharedMemorySize,
+ (void *)APRSSHAREDMEMORYBASE);
+
+ if (Shared == NULL)
+ {
+ Consoleprintf("Could not map view of file (%d).\n", GetLastError());
+ CloseHandle(hMapFile);
+ return 0;
+ }
+
+#endif
+
+ // First record has pointer to table
+
+ memset(Shared, 0, SharedMemorySize);
+
+ StnRecordBase = Shared + 32;
+ SMEM = (struct SharedMem *)Shared;
+
+ SMEM->Version = 1;
+ SMEM->SharedMemLen = SharedMemorySize;
+ SMEM->NeedRefresh = TRUE;
+ SMEM->Arch = sizeof(void *);
+ SMEM->SubVersion = 1;
+
+ Stn1 = (struct STATIONRECORD *)StnRecordBase;
+
+ StationRecords = (struct STATIONRECORD **)Stn1;
+
+ Stn1++;
+
+ StationRecordPool = Stn1;
+
+ for (i = 1; i < MaxStations; i++) // Already have first
+ {
+ Stn2 = Stn1;
+ Stn2++;
+ Stn1->Next = Stn2;
+
+ Stn1 = Stn2;
+ }
+
+ Debugprintf("End of Stations %p", Stn1);
+ endofStations = Stn1;
+
+ Stn1 += 2; // Try to fix corruption of messages.
+
+ // Build Message Record Pool
+
+ Msg1 = (struct APRSMESSAGE *)Stn1;
+
+ MessageRecordPool = Msg1;
+
+ for (i = 1; i < MAXMESSAGES; i++) // Already have first
+ {
+ Msg2 = Msg1;
+ Msg2++;
+ Msg1->Next = Msg2;
+
+ Msg1 = Msg2;
+ }
+
+ if (PosnSet == 0)
+ {
+ SYMBOL = '.';
+ SYMSET = '\\'; // Undefined Posn Symbol
+ }
+ else
+ {
+ // Convert posn to floating degrees
+
+ char LatDeg[3], LonDeg[4];
+ memcpy(LatDeg, LAT, 2);
+ LatDeg[2]=0;
+ Lat=atof(LatDeg) + (atof(LAT+2)/60);
+
+ if (LAT[7] == 'S') Lat=-Lat;
+
+ memcpy(LonDeg, LON, 3);
+ LonDeg[3]=0;
+ Lon=atof(LonDeg) + (atof(LON+3)/60);
+
+ if (LON[8]== 'W') Lon=-Lon;
+
+ SYMBOL = CFGSYMBOL;
+ SYMSET = CFGSYMSET;
+ }
+
+ // First record has control info for APRS Mapping App
+
+ Stn1 = (struct STATIONRECORD *)StnRecordBase;
+ memcpy(Stn1->Callsign, APRSCall, 10);
+ Stn1->Lat = Lat;
+ Stn1->Lon = Lon;
+ Stn1->LastPort = MaxStations;
+
+#ifndef WIN32
+
+ // Open unix socket for messaging app
+
+ sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
+
+ if (sfd == -1)
+ {
+ perror("Socket");
+ }
+ else
+ {
+ u_long param=1;
+ ioctl(sfd, FIONBIO, ¶m); // Set non-blocking
+
+ memset(&my_addr, 0, sizeof(struct sockaddr_un));
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, TX_SOCK_PATH, sizeof(my_addr.sun_path) - 1);
+
+ memset(&peer_addr, 0, sizeof(struct sockaddr_un));
+ peer_addr.sun_family = AF_UNIX;
+ strncpy(peer_addr.sun_path, RX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1);
+
+ unlink(TX_SOCK_PATH);
+
+ if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1)
+ perror("bind");
+ }
+#endif
+
+ // Convert Dest ADDRS to AX.25
+
+ for (i = 0; i < 26; i++)
+ {
+ DCall = &APRSDESTS[i][0];
+ if (strchr(DCall, '*'))
+ AXDESTLEN[i] = (int)strlen(DCall) - 1;
+ else
+ AXDESTLEN[i] = 6;
+
+ ConvToAX25(DCall, &AXDESTS[i][0]);
+ }
+
+ // Process any Object Definitions
+
+ // Setup Heard Data Area
+
+ HEARDENTRIES = 0;
+ MAXHEARDENTRIES = MAXHEARD;
+
+ APRSMONVECPTR->HOSTAPPLFLAGS = 0x80; // Request Monitoring
+
+ if (ISPort && IGateEnabled)
+ {
+ _beginthread(APRSISThread, 0, (VOID *) TRUE);
+ }
+
+ if (GPSPort[0])
+ OpenGPSPort();
+
+ WritetoConsole("APRS Digi/Gateway Enabled\n");
+
+ APRSWeb = TRUE;
+
+ read_png((unsigned char *)IconData);
+
+ // Reload saved messages
+
+ if (SaveAPRSMsgs)
+ GetSavedAPRSMessages();
+
+ // If a Run parameter was supplied, run the program
+
+ if (RunProgram[0] == 0)
+ return TRUE;
+
+ #ifndef WIN32
+ {
+ char * arg_list[] = {NULL, NULL};
+ pid_t child_pid;
+
+ signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children.
+
+ // Fork and Exec program
+
+ printf("Trying to start %s\n", RunProgram);
+
+ arg_list[0] = RunProgram;
+
+ // Duplicate this process.
+
+ child_pid = fork ();
+
+ if (child_pid == -1)
+ {
+ printf ("APRS fork() Failed\n");
+ return 0;
+ }
+
+ if (child_pid == 0)
+ {
+ execvp (arg_list[0], arg_list);
+
+ // The execvp function returns only if an error occurs.
+
+ printf ("Failed to run %s\n", RunProgram);
+ exit(0); // Kill the new process
+ }
+ }
+#else
+ {
+ int n = 0;
+
+ STARTUPINFO SInfo; // pointer to STARTUPINFO
+ PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION
+
+ SInfo.cb=sizeof(SInfo);
+ SInfo.lpReserved=NULL;
+ SInfo.lpDesktop=NULL;
+ SInfo.lpTitle=NULL;
+ SInfo.dwFlags=0;
+ SInfo.cbReserved2=0;
+ SInfo.lpReserved2=NULL;
+
+ while (KillOldTNC(RunProgram) && n++ < 100)
+ {
+ Sleep(100);
+ }
+
+ if (!CreateProcess(RunProgram, NULL, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo))
+ Debugprintf("Failed to Start %s Error %d ", RunProgram, GetLastError());
+ }
+#endif
+
+ return TRUE;
+}
+
+#define SD_RECEIVE 0x00
+#define SD_SEND 0x01
+#define SD_BOTH 0x02
+
+BOOL APRSActive;
+
+VOID APRSClose()
+{
+ APRSActive = FALSE;
+
+ if (sock)
+ {
+ shutdown(sock, SD_BOTH);
+ Sleep(50);
+
+ closesocket(sock);
+ }
+#ifdef WIN32
+ if (InPorts[0].hDevice)
+ CloseHandle(InPorts[0].hDevice);
+#endif
+}
+
+time_t lastSecTimer = 0;
+
+
+Dll VOID APIENTRY Poll_APRS()
+{
+ time_t Now = time(NULL);
+
+ if (lastSecTimer != Now)
+ {
+ lastSecTimer = Now;
+
+ DoSecTimer();
+
+ MinTimer--;
+
+ if (MinTimer == 0)
+ {
+ MinTimer = 60;
+ DoMinTimer();
+ }
+ }
+
+ if (SMEM->ClearRX)
+ {
+ // Clear Received Messages request from GUI
+
+ struct APRSMESSAGE * ptr = SMEM->Messages;
+
+ // Move Message Queue to Free Queue
+
+ if (ptr)
+ {
+ while (ptr->Next) // Find end of chain
+ {
+ ptr = ptr->Next;
+ }
+
+ // ptr is end of chain - chain free pool to it
+
+ ptr->Next = MessageRecordPool;
+
+ MessageRecordPool = SMEM->Messages;
+ MessageCount = 0;
+ }
+
+ SMEM->Messages = NULL;
+ SMEM->ClearRX = 0;
+ SMEM->NeedRefresh = TRUE;
+
+ ClearSavedMessages();
+ }
+
+ if (SMEM->ClearTX)
+ {
+ // Clear Sent Messages )request from GUI
+
+ struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs;
+
+ // Move Message Queue to Free Queue
+
+ if (ptr)
+ {
+ while (ptr->Next) // Find end of chain
+ {
+ ptr = ptr->Next;
+ }
+
+ // ptr is end of chain - chain free pool to it
+
+ ptr->Next = MessageRecordPool;
+
+ MessageRecordPool = SMEM->OutstandingMsgs;
+ MessageCount = 0;
+ }
+
+ SMEM->OutstandingMsgs = NULL;
+ SMEM->ClearTX = 0;
+ SMEM->NeedRefresh = TRUE;
+ }
+#ifdef LINBPQ
+#ifndef WIN32
+ {
+ char Msg[256];
+ int numBytes;
+
+ // Look for messages from App
+
+ numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL);
+
+ if (numBytes > 0)
+ {
+ InternalSendAPRSMessage(&Msg[10], &Msg[0]);
+ }
+ }
+#endif
+#endif
+
+ if (GPSPort[0])
+ PollGPSIn();
+
+ if (APPLTX_Q)
+ {
+ PMSGWITHLEN buffptr = Q_REM(&APPLTX_Q);
+
+ InternalSendAPRSMessage(&buffptr->Data[10], &buffptr->Data[0]);
+ ReleaseBuffer(buffptr);
+ }
+
+ while (APRSMONVECPTR->HOSTTRACEQ)
+ {
+ time_t stamp;
+ int len;
+ BOOL MonitorNODES = FALSE;
+ PMESSAGE monbuff;
+ UCHAR * monchars;
+ MESSAGE * Orig;
+ int Digis = 0;
+ MESSAGE * AdjBuff; // Adjusted for digis
+ BOOL FirstUnused = FALSE;
+ int DigisUsed = 0; // Digis used to reach us
+ DIGIMESSAGE Msg = {0};
+ int Port;
+ unsigned char buffer[1024];
+ char ISMsg[500];
+ char * ptr1;
+ char * Payload;
+ char * ptr3;
+ char * ptr4;
+ BOOL ThirdParty = FALSE;
+ BOOL NoGate = FALSE;
+ APRSHEARDRECORD * MH;
+ char MsgCopy[500];
+ int toPort;
+ struct STATIONRECORD * Station;
+ int ourMessage = 0;
+
+#ifdef WIN32
+ struct _EXCEPTION_POINTERS exinfo;
+ char EXCEPTMSG[80] = "";
+#endif
+ monbuff = Q_REM((VOID **)&APRSMONVECPTR->HOSTTRACEQ);
+
+ monchars = (UCHAR *)monbuff;
+ AdjBuff = Orig = (MESSAGE *)monchars; // Adjusted for digis
+
+ Port = Orig->PORT;
+
+ if (Port & 0x80) // TX
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ if ((APRSPortMask & ((uint64_t)1 << (Port - 1))) == 0)// Port in use for APRS?
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ stamp = monbuff->Timestamp;
+
+ if ((UCHAR)monchars[4] & 0x80) // TX
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ // See if digipeaters present.
+
+ while ((AdjBuff->ORIGIN[6] & 1) == 0 && Digis < 9)
+ {
+ UCHAR * temp = (UCHAR *)AdjBuff;
+ temp += 7;
+ AdjBuff = (MESSAGE *)temp;
+
+ // If we have already digi'ed it or if we sent it,
+ // ignore (Dup Check my fail on slow links)
+
+ if (AdjBuff->ORIGIN[6] & 0x80)
+ {
+ // Used Digi
+
+ if (memcmp(AdjBuff->ORIGIN, AXCall, 7) == 0)
+ {
+ ReleaseBuffer(monbuff);
+ return;
+ }
+ DigisUsed++;
+ }
+
+ if (memcmp(AdjBuff->ORIGIN, axTCPIP, 6) == 0)
+ ThirdParty = TRUE;
+
+ Digis ++;
+
+ if (FirstUnused == FALSE && (AdjBuff->ORIGIN[6] & 0x80) == 0)
+ {
+ // Unused Digi - see if we should digi it
+
+ FirstUnused = Digis;
+ // CheckDigi(buff, AdjBuff->ORIGIN);
+ }
+ }
+
+ if (Digis > 8)
+ {
+ ReleaseBuffer(monbuff);
+ continue; // Corrupt
+ }
+
+ if (Digis)
+ {
+ if (memcmp(AdjBuff->ORIGIN, axNOGATE, 6) == 0
+ || memcmp(AdjBuff->ORIGIN, axRFONLY, 6) == 0
+ || DigisUsed > MaxDigisforIS)
+
+ // Too many digis or Last digis is NOGATE or RFONLY - dont send to IS
+
+ NoGate = TRUE;
+ }
+ if (AdjBuff->CTL != 3 || AdjBuff->PID != 0xf0) // Only UI
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ // Bridge if requested
+
+ for (toPort = 1; toPort <= MaxBPQPortNo; toPort++)
+ {
+ if (APRSBridgeMap[Port][toPort])
+ {
+ MESSAGE * Buffer = GetBuff();
+ struct PORTCONTROL * PORT;
+
+ if (Buffer)
+ {
+ memcpy(Buffer, Orig, Orig->LENGTH);
+ Buffer->PORT = toPort;
+ PORT = GetPortTableEntryFromPortNum(toPort);
+
+ if (PORT)
+ {
+ if (PORT->SmartIDInterval && PORT->SmartIDNeeded == 0)
+ {
+ // Using Smart ID, but none scheduled
+
+ PORT->SmartIDNeeded = time(NULL) + PORT->SmartIDInterval;
+ }
+ PUT_ON_PORT_Q(PORT, Buffer);
+ }
+ else
+ ReleaseBuffer(Buffer);
+ }
+ }
+ }
+
+ // Used to check for dups here but according to "Notes to iGate developers" IS should be sent dups, and dup
+ // check only applied to digi'ing
+
+// if (SATGate == 0)
+// {
+// if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *)))
+// {
+// ReleaseBuffer(monbuff);
+// continue;
+// }
+// }
+ // Decode Frame to TNC2 Monitor Format
+
+ len = APRSDecodeFrame((char *)monchars, buffer, stamp, APRSPortMask);
+
+ if (len == 0)
+ {
+ // Couldn't Decode
+
+ ReleaseBuffer(monbuff);
+ Debugprintf("APRS discarded frame - decode failed\n");
+ continue;
+ }
+
+ buffer[len] = 0;
+
+ memcpy(MsgCopy, buffer, len);
+ MsgCopy[len] = 0;
+
+ // Do internal Decode
+
+#ifdef WIN32
+
+ strcpy(EXCEPTMSG, "ProcessRFFrame");
+
+ __try
+ {
+
+ Station = ProcessRFFrame(MsgCopy, len, &ourMessage);
+ }
+ #include "StdExcept.c"
+
+ }
+#else
+ Station = ProcessRFFrame(MsgCopy, len, &ourMessage);
+#endif
+
+ if (Station == NULL)
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ memcpy(MsgCopy, buffer, len); // Process RF Frame may have changed it
+ MsgCopy[len] = 0;
+
+ buffer[len++] = 10;
+ buffer[len] = 0;
+ ptr1 = &buffer[10]; // Skip Timestamp
+ Payload = strchr(ptr1, ':') + 2; // Start of Payload
+ ptr3 = strchr(ptr1, ' '); // End of addresses
+ *ptr3 = 0;
+
+ // We should send path to IS unchanged, so create IS
+ // message before chopping path. We won't decide if
+ // we will actually send it to IS till later
+
+ len = sprintf(ISMsg, "%s,qAR,%s:%s", ptr1, APRSCall, Payload);
+
+
+ // if digis, remove any unactioned ones
+
+ if (Digis)
+ {
+ ptr4 = strchr(ptr1, '*'); // Last Used Digi
+
+ if (ptr4)
+ {
+ // We need header up to ptr4
+
+ *(ptr4) = 0;
+ }
+ else
+ {
+ // No digis actioned - remove them all
+
+ ptr4 = strchr(ptr1, ','); // End of Dest
+ if (ptr4)
+ *ptr4 = 0;
+ }
+ }
+
+ ptr4 = strchr(ptr1, '>'); // End of Source
+ *ptr4++ = 0;
+
+ MH = UpdateHeard(ptr1, Port);
+
+ MH->Station = Station;
+
+ if (ThirdParty)
+ {
+// Debugprintf("Setting Igate Flag - %s", MsgCopy);
+ MH->IGate = TRUE; // if we've seen msgs to TCPIP, it must be an Igate
+ }
+
+ if (NoGate || RXOnly)
+ goto NoIS;
+
+ // I think all PID F0 UI frames go to APRS-IS,
+ // Except General Queries, Frames Gated from IS to RF, and Messages Addressed to us
+
+ // or should we process Query frames locally ??
+
+ if (Payload[0] == '}')
+ goto NoIS;
+
+ if (Payload[0] == '?')
+ {
+ // General Query
+
+ ProcessQuery(&Payload[1]);
+
+ // ?? Should we pass addressed Queries to IS ??
+
+ goto NoIS;
+ }
+
+ if (Payload[0] == ':' && memcmp(&Payload[1], CallPadded, 9) == 0)
+ {
+ // Message for us
+
+ if (Payload[11] == '?') // Only queries - the node doesnt do messaging
+ ProcessSpecificQuery(&Payload[12], Port, ptr1, ptr4);
+
+ goto NoIS;
+ }
+
+ if (APRSISOpen && CrossPortMap[Port][0]) // No point if not open
+ {
+ // was done above len = sprintf(ISMsg, "%s>%s,qAR,%s:%s", ptr1, ptr4, APRSCall, Payload);
+
+ if (BeacontoIS == 0)
+ {
+ // Don't send anything we've received as an echo
+
+ char SaveCall[7];
+ memcpy(SaveCall, &monbuff->ORIGIN, 7);
+ SaveCall[6] &= 0x7e; // Mask End of address bit
+
+ if (memcmp(SaveCall, AXCall, 7) == 0) // We sent it
+ {
+ // Should we check for being received via digi? - not for now
+
+ goto NoIS;
+ }
+ }
+
+ if (SATGate && (DigisUsed == 0))
+ {
+ // If in Satgate mode delay directly heard to IGate
+
+ ISDELAY * SatISEntry = malloc(sizeof(ISDELAY));
+ SatISEntry->Next = NULL;
+ SatISEntry->ISMSG = _strdup(ISMsg);
+ SatISEntry->SendTIme = time(NULL) + 10; // Delay 10 seconds
+
+ if (SatISQueue)
+ SatISEntry->Next = SatISQueue; // Chain
+
+ SatISQueue = SatISEntry;
+ goto NoIS;
+ }
+
+ ISSend(sock, ISMsg, len, 0);
+
+ ptr1 = strchr(ISMsg, 13);
+ if (ptr1) *ptr1 = 0;
+// Debugprintf(">%s", ISMsg);
+ }
+
+ NoIS:
+
+ // We skipped DUP check for SATGate Mode, so apply it here
+
+ // Now we don't dup check earlier so always check here
+
+// if (SATGate)
+// {
+ if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *))))
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+// }
+
+ // See if it is an APRS frame
+
+ // If MIC-E, we need to process, whatever the destination
+
+ // Now process any dest
+
+/*
+ DEST = &Orig->DEST[0];
+
+ for (i = 0; i < 26; i++)
+ {
+ if (memcmp(DEST, &AXDESTS[i][0], AXDESTLEN[i]) == 0)
+ goto OK;
+ }
+
+ switch(AdjBuff->L2DATA[0])
+ {
+ case '`':
+ case 0x27: // '
+ case 0x1c:
+ case 0x1d: // MIC-E
+
+ break;
+ // default:
+
+ // Not to an APRS Destination
+
+// ReleaseBuffer(monbuff);
+// continue;
+ }
+
+OK:
+*/
+
+ // If there are unused digis, we may need to digi it.
+
+ if (ourMessage)
+ {
+ // A message addressed to us, so no point in digi'ing it
+
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ if (Digis == 0 || FirstUnused == 0)
+ {
+ // No Digis, so finished
+
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ if (memcmp(monbuff->ORIGIN, AXCall, 7) == 0) // We sent it
+ {
+ ReleaseBuffer(monbuff);
+ continue;
+ }
+
+ // Copy frame to a DIGIMessage Struct
+
+ memcpy(&Msg, monbuff, MSGHDDRLEN + 14 + (7 * Digis)); // Header, Dest, Source, Addresses and Digis
+
+ len = Msg.LENGTH - (MSGHDDRLEN + 14) - (7 * Digis); // Payload Length (including CTL and PID
+
+ memcpy(&Msg.CTL, &AdjBuff->CTL, len);
+
+ // Pass to Digi Code
+
+ CheckandDigi(&Msg, Port, FirstUnused, Digis, len); // Digi if necessary
+
+ ReleaseBuffer(monbuff);
+ }
+
+ return;
+}
+
+VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len)
+{
+ UCHAR * Digi = &Msg->DIGIS[--FirstUnused][0];
+ UCHAR * Call;
+ int Index = 0;
+ int SSID;
+
+ // Check ordinary digi first
+
+ Call = &DigiAX[0][0];
+ SSID = Digi[6] & 0x1e;
+
+ while (*Call)
+ {
+ if ((memcmp(Digi, Call, 6) == 0) && ((Call[6] & 0x1e) == SSID))
+ {
+ // Trace Call if enabled
+
+ if (TraceDigi)
+ memcpy(Digi, AXCall, 7);
+
+ // mark as used;
+
+ Digi[6] |= 0x80; // Used bit
+
+ SendtoDigiPorts(Msg, Len, Port);
+ return;
+ }
+ Call += 7;
+ Index++;
+ }
+
+ Call = &TraceAX[0][0];
+ Index = 0;
+
+ while (*Call)
+ {
+ if (memcmp(Digi, Call, TraceLen[Index]) == 0)
+ {
+ // if possible move calls along
+ // insert our call, set used
+ // decrement ssid, and if zero, mark as used;
+
+ SSID = (Digi[6] & 0x1E) >> 1;
+
+ if (SSID == 0)
+ return; // Shouldn't have SSID 0 for Rrace/Flood
+
+ if (SSID > MaxTraceHops)
+ SSID = MaxTraceHops; // Enforce our limit
+
+ SSID--;
+
+ if (SSID ==0) // Finihed with it ?
+ Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits
+ else
+ Digi[6] = (SSID << 1) | 0x60; // Fixed bits
+
+ if (Digis < 8)
+ {
+ memmove(Digi + 7, Digi, (Digis - FirstUnused) * 7);
+ }
+
+ memcpy(Digi, AXCall, 7);
+ Digi[6] |= 0x80;
+
+ SendtoDigiPorts(Msg, Len, Port);
+
+ return;
+ }
+ Call += 7;
+ Index++;
+ }
+
+ Index = 0;
+ Call = &FloodAX[0][0];
+
+ while (*Call)
+ {
+ if (memcmp(Digi, Call, FloodLen[Index]) == 0)
+ {
+ // decrement ssid, and if zero, mark as used;
+
+ SSID = (Digi[6] & 0x1E) >> 1;
+
+ if (SSID == 0)
+ return; // Shouldn't have SSID 0 for Trace/Flood
+
+ if (SSID > MaxFloodHops)
+ SSID = MaxFloodHops; // Enforce our limit
+
+ SSID--;
+
+ if (SSID ==0) // Finihed with it ?
+ Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits
+ else
+ Digi[6] = (SSID << 1) | 0x60; // Fixed bits
+
+ SendtoDigiPorts(Msg, Len, Port);
+
+ return;
+ }
+ Call += 7;
+ Index++;
+ }
+}
+
+
+
+static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port)
+{
+ // Can't use API SENDRAW, as that tries to get the semaphore, which we already have
+ // Len is the Payload Length (from CTL onwards)
+ // The message can contain DIGIS - The payload must be copied forwards if there are less than 8
+
+ // We send to all ports enabled in CrossPortMap
+
+ UCHAR * EndofDigis = &Block->CTL;
+ int i = 0;
+ int toPort;
+
+ while (Block->DIGIS[i][0] && i < 8)
+ {
+ i++;
+ }
+
+ EndofDigis = &Block->DIGIS[i][0];
+ *(EndofDigis -1) |= 1; // Set End of Address Bit
+
+ if (i != 8)
+ memmove(EndofDigis, &Block->CTL, Len);
+
+ Len = Len + (i * 7) + 14; // Include Source, Dest and Digis
+
+// Block->DEST[6] &= 0x7e; // Clear End of Call
+// Block->ORIGIN[6] |= 1; // Set End of Call
+
+// Block->CTL = 3; //UI
+
+ for (toPort = 1; toPort <= MaxBPQPortNo; toPort++)
+ {
+ if (CrossPortMap[Port][toPort])
+ Send_AX((PMESSAGE)Block, Len, toPort);
+ }
+ return;
+
+}
+
+VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port)
+{
+ // Can't use API SENDRAW, as that tries to get the semaphore, which we already have
+
+ // Len is the Payload Length (CTL, PID, Data)
+
+ // The message can contain DIGIS - The payload must be copied forwards if there are less than 8
+
+ UCHAR * EndofDigis = &Block->CTL;
+
+ int i = 0;
+
+ while (Block->DIGIS[i][0] && i < 8)
+ {
+ i++;
+ }
+
+ EndofDigis = &Block->DIGIS[i][0];
+ *(EndofDigis -1) |= 1; // Set End of Address Bit
+
+ if (i != 8)
+ memmove(EndofDigis, &Block->CTL, Len); // Include PID
+
+ Len = Len + (i * 7) + 14; // Include Source, Dest and Digis
+
+ Send_AX((PMESSAGE)Block, Len, Port);
+
+ return;
+
+}
+
+static BOOL APRSReadConfigFile()
+{
+ char * Config;
+ char * ptr1, * ptr2;
+
+ char buf[256],errbuf[256];
+
+ Config = PortConfig[APRSConfigSlot]; // Config from bpq32.cfg
+
+ sprintf(StatusMsg, "BPQ32 Igate V %s", VersionString); // Set Default Status Message
+
+ if (Config)
+ {
+ // Using config from bpq32.cfg
+
+ ptr1 = Config;
+
+ ptr2 = strchr(ptr1, 13);
+ while(ptr2)
+ {
+ memcpy(buf, ptr1, ptr2 - ptr1);
+ buf[ptr2 - ptr1] = 0;
+ ptr1 = ptr2 + 2;
+ ptr2 = strchr(ptr1, 13);
+
+ strcpy(errbuf,buf); // save in case of error
+
+ if (!APRSProcessLine(buf))
+ {
+ WritetoConsole("APRS Bad config record ");
+ strcat(errbuf, "\r\n");
+ WritetoConsole(errbuf);
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL ConvertCalls(char * DigiCalls, UCHAR * AX, int * Lens)
+{
+ int Index = 0;
+ char * ptr;
+ char * Context;
+ UCHAR Work[MAXCALLS][7] = {0};
+ int Len[MAXCALLS] = {0};
+
+ ptr = strtok_s(DigiCalls, ", ", &Context);
+
+ while(ptr)
+ {
+ if (Index == MAXCALLS) return FALSE;
+
+ ConvToAX25(ptr, &Work[Index][0]);
+ Len[Index++] = (int)strlen(ptr);
+ ptr = strtok_s(NULL, ", ", &Context);
+ }
+
+ memcpy(AX, Work, sizeof(Work));
+ memcpy(Lens, Len, sizeof(Len));
+ return TRUE;
+}
+
+
+
+static int APRSProcessLine(char * buf)
+{
+ char * ptr, * p_value;
+
+ ptr = strtok(buf, "= \t\n\r");
+
+ if(ptr == NULL) return (TRUE);
+
+ if(*ptr =='#') return (TRUE); // comment
+
+ if(*ptr ==';') return (TRUE); // comment
+
+
+// OBJECT PATH=APRS,WIDE1-1 PORT=1,IS INTERVAL=30 TEXT=;444.80TRF*111111z4807.60N/09610.63Wr%156 R15m
+
+ if (_stricmp(ptr, "OBJECT") == 0)
+ {
+ char * p_Path, * p_Port, * p_Text;
+ int Interval;
+ struct OBJECT * Object;
+ int Digi = 2;
+ char * Context;
+ int SendTo;
+
+ p_value = strtok(NULL, "=");
+ if (p_value == NULL) return FALSE;
+ if (_stricmp(p_value, "PATH"))
+ return FALSE;
+
+ p_Path = strtok(NULL, "\t\n\r ");
+ if (p_Path == NULL) return FALSE;
+
+ p_value = strtok(NULL, "=");
+ if (p_value == NULL) return FALSE;
+ if (_stricmp(p_value, "PORT"))
+ return FALSE;
+
+ p_Port = strtok(NULL, "\t\n\r ");
+ if (p_Port == NULL) return FALSE;
+
+ p_value = strtok(NULL, "=");
+ if (p_value == NULL) return FALSE;
+ if (_stricmp(p_value, "INTERVAL"))
+ return FALSE;
+
+ p_value = strtok(NULL, " \t");
+ if (p_value == NULL) return FALSE;
+
+ Interval = atoi(p_value);
+
+ if (Interval == 0)
+ return FALSE;
+
+ p_value = strtok(NULL, "=");
+ if (p_value == NULL) return FALSE;
+ if (_stricmp(p_value, "TEXT"))
+ return FALSE;
+
+ p_Text = strtok(NULL, "\n\r");
+ if (p_Text == NULL) return FALSE;
+
+ Object = zalloc(sizeof(struct OBJECT));
+
+ if (Object == NULL)
+ return FALSE;
+
+ Object->Next = ObjectList;
+ ObjectList = Object;
+
+ if (Interval < 10)
+ Interval = 10;
+
+ Object->Interval = Interval;
+ Object->Timer = (ObjectCount++) * 10 + 30; // Spread them out;
+
+ // Convert Path to AX.25
+
+ ConvToAX25(APRSCall, &Object->Path[1][0]);
+
+ ptr = strtok_s(p_Path, ",\t\n\r", &Context);
+
+ if (_stricmp(ptr, "APRS") == 0) // First is Dest
+ ConvToAX25(APRSDest, &Object->Path[0][0]);
+ else if (_stricmp(ptr, "APRS-0") == 0)
+ ConvToAX25("APRS", &Object->Path[0][0]);
+ else
+ ConvToAX25(ptr, &Object->Path[0][0]);
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ while (ptr)
+ {
+ ConvToAX25(ptr, &Object->Path[Digi++][0]);
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+
+ Object->PathLen = Digi * 7;
+
+ // Process Port List
+
+ ptr = strtok_s(p_Port, ",", &Context);
+
+ while (ptr)
+ {
+ SendTo = atoi(ptr); // this gives zero for IS
+
+ if (SendTo > MaxBPQPortNo)
+ return FALSE;
+
+ Object->PortMap[SendTo] = TRUE;
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+
+ if (strlen(p_Text) > 80)
+ p_Text[80] = 0;
+
+ strcpy(Object->Message, p_Text);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "STATUSMSG") == 0)
+ {
+ p_value = strtok(NULL, ";\t\n\r");
+ memcpy(StatusMsg, p_value, 128); // Just in case too long
+ StatusMsgLen = (int)strlen(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "WXFileName") == 0)
+ {
+ p_value = strtok(NULL, ";\t\n\r");
+ strcpy(WXFileName, p_value);
+ SendWX = TRUE;
+ return TRUE;
+ }
+ if (_stricmp(ptr, "WXComment") == 0)
+ {
+ p_value = strtok(NULL, ";\t\n\r");
+
+ if (p_value == NULL)
+ return TRUE;
+
+ if (strlen(p_value) > 79)
+ p_value[80] = 0;
+
+ strcpy(WXComment, p_value);
+ return TRUE;
+ }
+
+
+ if (_stricmp(ptr, "ISFILTER") == 0)
+ {
+ p_value = strtok(NULL, ";\t\n\r");
+ strcpy(ISFilter, p_value);
+ strcpy(NodeFilter, ISFilter);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "ReplaceDigiCalls") == 0)
+ {
+ TraceDigi = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "Multiple") == 0)
+ {
+ multiple = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "SATGate") == 0)
+ {
+ SATGate = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "RXOnly") == 0)
+ {
+ RXOnly = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "DISTKM") == 0)
+ {
+ DefaultDistKM = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "LOCALTIME") == 0)
+ {
+ DefaultLocalTime = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "LOGAPRSIS") == 0)
+ {
+ LogAPRSIS = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "SaveAPRSMsgs") == 0)
+ {
+ SaveAPRSMsgs = TRUE;
+ return TRUE;
+ }
+
+ p_value = strtok(NULL, " \t\n\r");
+
+ if (p_value == NULL)
+ return FALSE;
+
+ if (_stricmp(ptr, "APRSCALL") == 0)
+ {
+ strcpy(APRSCall, p_value);
+ strcpy(LoppedAPRSCall, p_value);
+ memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging
+
+ // Convert to ax.25
+
+ return ConvToAX25(APRSCall, AXCall);
+ }
+
+ if (_stricmp(ptr, "WXCALL") == 0)
+ {
+ strcpy(WXCall, p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "APRSPATH") == 0)
+ {
+ int Digi = 2;
+ int Port;
+ char * Context;
+
+ p_value = strtok_s(p_value, "=\t\n\r", &Context);
+
+ Port = atoi(p_value);
+
+ if (GetPortTableEntryFromPortNum(Port) == NULL)
+ return FALSE;
+
+ APRSPortMask |= (uint64_t)1 << (Port - 1);
+
+ if (Context == NULL || Context[0] == 0)
+ return TRUE; // No dest - a receive-only port
+
+ BeaconPath[Port] = _strdup(_strupr(Context));
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ if (ptr == NULL)
+ return FALSE;
+
+ ConvToAX25(APRSCall, &BeaconHeader[Port][1][0]);
+
+ if (_stricmp(ptr, "APRS") == 0) // First is Dest
+ ConvToAX25(APRSDest, &BeaconHeader[Port][0][0]);
+ else if (_stricmp(ptr, "APRS-0") == 0)
+ ConvToAX25("APRS", &BeaconHeader[Port][0][0]);
+ else
+ ConvToAX25(ptr, &BeaconHeader[Port][0][0]);
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ while (ptr)
+ {
+ ConvToAX25(ptr, &BeaconHeader[Port][Digi++][0]);
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+
+ BeaconHddrLen[Port] = Digi * 7;
+
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GATEDPATH") == 0)
+ {
+ int Digi = 2;
+ int Port;
+ char * Context;
+
+ p_value = strtok_s(p_value, "=\t\n\r", &Context);
+
+ Port = atoi(p_value);
+
+ if (GetPortTableEntryFromPortNum(Port) == NULL)
+ return FALSE;
+
+// APRSPortMask |= 1 << (Port - 1);
+
+ if (Context == NULL || Context[0] == 0)
+ return TRUE; // No dest - a receive-only port
+
+ BeaconPath[Port] = _strdup(_strupr(Context));
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ if (ptr == NULL)
+ return FALSE;
+
+ ConvToAX25(APRSCall, &GatedHeader[Port][1][0]);
+
+ if (_stricmp(ptr, "APRS") == 0) // First is Dest
+ ConvToAX25(APRSDest, &GatedHeader[Port][0][0]);
+ else if (_stricmp(ptr, "APRS-0") == 0)
+ ConvToAX25("APRS", &GatedHeader[Port][0][0]);
+ else
+ ConvToAX25(ptr, &GatedHeader[Port][0][0]);
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ while (ptr)
+ {
+ ConvToAX25(ptr, &GatedHeader[Port][Digi++][0]);
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+
+ GatedHddrLen[Port] = Digi * 7;
+
+ return TRUE;
+ }
+
+
+ if (_stricmp(ptr, "DIGIMAP") == 0)
+ {
+ int DigiTo;
+ int Port;
+ char * Context;
+
+ p_value = strtok_s(p_value, "=\t\n\r", &Context);
+
+ Port = atoi(p_value);
+
+ if (GetPortTableEntryFromPortNum(Port) == NULL)
+ return FALSE;
+
+ // Check that port can digi (SCS Pactor can't set digi'd bit in calls)
+
+ if (CanPortDigi(Port) == 0)
+ return FALSE;
+
+
+ CrossPortMap[Port][Port] = FALSE; // Cancel Default mapping
+ CrossPortMap[Port][0] = FALSE; // Cancel Default APRSIS
+
+ if (Context == NULL || Context[0] == 0)
+ return TRUE;
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ while (ptr)
+ {
+ DigiTo = atoi(ptr); // this gives zero for IS
+
+ if (DigiTo && GetPortTableEntryFromPortNum(DigiTo) == NULL)
+ return FALSE;
+
+ CrossPortMap[Port][DigiTo] = TRUE;
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+
+ return TRUE;
+ }
+ if (_stricmp(ptr, "BRIDGE") == 0)
+ {
+ int DigiTo;
+ int Port;
+ char * Context;
+
+ p_value = strtok_s(p_value, "=\t\n\r", &Context);
+
+ Port = atoi(p_value);
+
+ if (GetPortTableEntryFromPortNum(Port) == NULL)
+ return FALSE;
+
+ if (Context == NULL)
+ return FALSE;
+
+ ptr = strtok_s(NULL, ",\t\n\r", &Context);
+
+ while (ptr)
+ {
+ DigiTo = atoi(ptr); // this gives zero for IS
+
+ if (DigiTo > MaxBPQPortNo)
+ return FALSE;
+
+ APRSBridgeMap[Port][DigiTo] = TRUE;
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+
+ return TRUE;
+ }
+
+
+ if (_stricmp(ptr, "BeaconInterval") == 0)
+ {
+ BeaconInterval = atoi(p_value);
+
+ if (BeaconInterval < 5)
+ BeaconInterval = 5;
+
+ if (BeaconInterval)
+ BeaconCounter = 30; // Send first after 30 secs
+
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "MobileBeaconInterval") == 0)
+ {
+ MobileBeaconInterval = atoi(p_value) * 60;
+ return TRUE;
+ }
+ if (_stricmp(ptr, "MobileBeaconIntervalSecs") == 0)
+ {
+ MobileBeaconInterval = atoi(p_value);
+ if (MobileBeaconInterval < 10)
+ MobileBeaconInterval = 10;
+
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "BeacontoIS") == 0)
+ {
+ BeacontoIS = atoi(p_value);
+ return TRUE;
+ }
+
+
+ if (_stricmp(ptr, "TRACECALLS") == 0)
+ {
+ TraceCalls = _strdup(_strupr(p_value));
+ ConvertCalls(TraceCalls, &TraceAX[0][0], &TraceLen[0]);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "FLOODCALLS") == 0)
+ {
+ FloodCalls = _strdup(_strupr(p_value));
+ ConvertCalls(FloodCalls, &FloodAX[0][0], &FloodLen[0]);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "DIGICALLS") == 0)
+ {
+ char AllCalls[1024];
+
+ DigiCalls = _strdup(_strupr(p_value));
+ strcpy(AllCalls, APRSCall);
+ strcat(AllCalls, ",");
+ strcat(AllCalls, DigiCalls);
+ ConvertCalls(AllCalls, &DigiAX[0][0], &DigiLen[0]);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "MaxStations") == 0)
+ {
+ MaxStations = atoi(p_value);
+
+ if (MaxStations > 10000)
+ MaxStations = 10000;
+
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "MaxAge") == 0)
+ {
+ ExpireTime = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GPSPort") == 0)
+ {
+ if (strcmp(p_value, "0") != 0)
+ strcpy(GPSPort, p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GPSSpeed") == 0)
+ {
+ GPSSpeed = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GPSRelay") == 0)
+ {
+ if (strlen(p_value) > 79)
+ return FALSE;
+
+ strcpy(GPSRelay, p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "BlueNMEA") == 0 || _stricmp(ptr, "TCPHost") == 0 || _stricmp(ptr, "AISHost") == 0)
+ {
+ if (strlen(p_value) > 70)
+ return FALSE;
+
+ strcpy(HostName, p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "TCPPort") == 0 || _stricmp(ptr, "AISPort") == 0)
+ {
+ HostPort = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GPSDHost") == 0)
+ {
+ if (strlen(p_value) > 70)
+ return FALSE;
+
+ strcpy(GPSDHost, p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GPSDPort") == 0)
+ {
+ GPSDPort = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "ADSBHost") == 0)
+ {
+ if (strlen(p_value) > 70)
+ return FALSE;
+
+ strcpy(ADSBHost, p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "ADSBPort") == 0)
+ {
+ ADSBPort = atoi(p_value);
+ return TRUE;
+ }
+
+
+
+ if (_stricmp(ptr, "GPSSetsLocator") == 0)
+ {
+ GPSSetsLocator = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "LAT") == 0)
+ {
+ if (strlen(p_value) != 8)
+ return FALSE;
+
+ memcpy(LAT, _strupr(p_value), 8);
+ PosnSet = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "LON") == 0)
+ {
+ if (strlen(p_value) != 9)
+ return FALSE;
+
+ memcpy(LON, _strupr(p_value), 9);
+ PosnSet = TRUE;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "SYMBOL") == 0)
+ {
+ if (p_value[0] > ' ' && p_value[0] < 0x7f)
+ CFGSYMBOL = p_value[0];
+
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "SYMSET") == 0)
+ {
+ CFGSYMSET = p_value[0];
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "PHG") == 0)
+ {
+ PHG = _strdup(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "MaxTraceHops") == 0)
+ {
+ MaxTraceHops = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "MaxFloodHops") == 0)
+ {
+ MaxFloodHops = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "ISHOST") == 0)
+ {
+ strncpy(ISHost, p_value, 250);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "ISPORT") == 0)
+ {
+ ISPort = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "ISPASSCODE") == 0)
+ {
+ ISPasscode = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "MaxDigisforIS") == 0)
+ {
+ MaxDigisforIS = atoi(p_value);
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "GateLocalDistance") == 0)
+ {
+ GateLocalDistance = atoi(p_value);
+ if (GateLocalDistance > 0.0)
+ GateLocal = TRUE;
+
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "WXInterval") == 0)
+ {
+ WXInterval = atoi(p_value);
+ WXCounter = (WXInterval - 1) * 60;
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "WXPortList") == 0)
+ {
+ char ParamCopy[80];
+ char * Context;
+ int Port;
+ char * ptr;
+ int index = 0;
+
+ for (index = 0; index < MaxBPQPortNo; index++)
+ WXPort[index] = FALSE;
+
+ if (strlen(p_value) > 79)
+ p_value[80] = 0;
+
+ strcpy(ParamCopy, p_value);
+
+ ptr = strtok_s(ParamCopy, " ,\t\n\r", &Context);
+
+ while (ptr)
+ {
+ Port = atoi(ptr); // this gives zero for IS
+
+ WXPort[Port] = TRUE;
+
+ ptr = strtok_s(NULL, " ,\t\n\r", &Context);
+ }
+ return TRUE;
+ }
+
+ if (_stricmp(ptr, "Run") == 0)
+ {
+ strcpy(RunProgram, p_value);
+ return TRUE;
+ }
+
+ //
+ // Bad line
+ //
+ return (FALSE);
+}
+
+VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated);
+
+
+VOID SendAPRSMessage(char * Message, int toPort)
+{
+ SendAPRSMessageEx(Message, toPort, APRSCall, 0);
+}
+
+// Ex allows setting source call (For WX Messages)
+
+
+VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated)
+{
+ int Port;
+ DIGIMESSAGE Msg;
+
+ int Len;
+
+ // toPort = -1 means all tadio ports. 0 = IS
+
+ if (toPort == -1)
+ {
+ for (Port = 1; Port <= MaxBPQPortNo; Port++)
+ {
+ if (Gated && GatedHddrLen[Port])
+ memcpy(Msg.DEST, &GatedHeader[Port][0][0], 10 * 7);
+ else if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined
+ memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7);
+ else
+ continue;
+
+ Msg.DEST[6] |= 0x80; // set Command Bit
+
+ ConvToAX25(FromCall, Msg.ORIGIN);
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+ Len = sprintf(Msg.L2DATA, "%s", Message);
+ Send_AX_Datagram(&Msg, Len + 2, Port);
+ }
+
+ return;
+ }
+
+ if (toPort == 0 && APRSISOpen)
+ {
+ char ISMsg[300];
+
+ Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", FromCall, APRSDest, Message);
+ ISSend(sock, ISMsg, Len, 0);
+ }
+
+ if (toPort == 0)
+ return;
+
+ if (Gated && GatedHddrLen[toPort])
+ memcpy(Msg.DEST, &GatedHeader[toPort][0][0], 10 * 7);
+ else if (BeaconHddrLen[toPort]) // Only send to ports with a DEST defined
+ memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7);
+ else
+ return;
+
+ Msg.DEST[6] |= 0x80; // set Command Bit
+
+ ConvToAX25(FromCall, Msg.ORIGIN);
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+ Len = sprintf(Msg.L2DATA, "%s", Message);
+ Send_AX_Datagram(&Msg, Len + 2, toPort);
+
+ return;
+}
+
+
+VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis)
+{
+ if (_memicmp(Query, "APRSS", 5) == 0)
+ {
+ char Message[255];
+
+ sprintf(Message, ":%-9s:%s", Origin, StatusMsg);
+ SendAPRSMessage(Message, Port);
+
+ return;
+ }
+
+ if (_memicmp(Query, "APRST", 5) == 0 || _memicmp(Query, "PING?", 5) == 0)
+ {
+ // Trace Route
+ //:KH2ZV :?APRST :N8UR :KH2Z>APRS,DIGI1,WIDE*:
+ //:G8BPQ-14 :Path - G8BPQ-14>APU25N
+
+ char Message[255];
+
+ sprintf(Message, ":%-9s:Path - %s>%s", Origin, Origin, DestPlusDigis);
+ SendAPRSMessage(Message, Port);
+
+ return;
+ }
+}
+
+VOID ProcessQuery(char * Query)
+{
+ if (memcmp(Query, "IGATE?", 6) == 0)
+ {
+ IStatusCounter = (rand() & 31) + 5; // 5 - 36 secs delay
+ return;
+ }
+
+ if (memcmp(Query, "APRS?", 5) == 0)
+ {
+ BeaconCounter = (rand() & 31) + 5; // 5 - 36 secs delay
+ return;
+ }
+}
+Dll VOID APIENTRY APISendBeacon()
+{
+ BeaconCounter = 2;
+}
+
+typedef struct _BeaconParams
+{
+ int toPort;
+ char * BeaconText;
+ BOOL SendStatus;
+ BOOL SendSOGCOG;
+} Params;
+
+
+Params BeaconParams;
+
+void SendBeaconThread(void * Params);
+
+VOID SendBeacon(int toPort, char * BeaconText, BOOL SendStatus, BOOL SendSOGCOG)
+{
+ // Send to IS if needed then start a thread to send to radio ports
+
+ if (PosnSet == FALSE)
+ return;
+
+ if (APRSISOpen && toPort == 0 && BeacontoIS)
+ {
+ char SOGCOG[10] = "";
+ char ISMsg[300];
+ int Len;
+
+ Debugprintf("Sending APRS Beacon to APRS-IS");
+
+ if (SendSOGCOG | (COG != 0.0))
+ sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG);
+
+ if (PHG) // Send PHG instead of SOG COG
+ Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest,
+ (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, PHG, BeaconText);
+ else
+ Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest,
+ (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText);
+
+ ISSend(sock, ISMsg, Len, 0);
+ Debugprintf(">%s", ISMsg);
+ }
+
+ BeaconParams.toPort = toPort;
+ BeaconParams.BeaconText = BeaconText;
+ BeaconParams.SendStatus = SendStatus;
+ BeaconParams.SendSOGCOG = SendSOGCOG;
+
+ _beginthread(SendBeaconThread, 0, (VOID *) &BeaconParams);
+}
+
+void SendBeaconThread(void * Param)
+{
+ // runs as a thread so we can sleep() between calls
+
+ // Params are passed via a param block
+
+ Params * BeaconParams = (Params *)Param;
+
+ int toPort = BeaconParams->toPort;
+ char * BeaconText = BeaconParams->BeaconText;
+ BOOL SendStatus = BeaconParams->SendStatus;
+ BOOL SendSOGCOG = BeaconParams->SendSOGCOG;
+
+ int Port;
+ DIGIMESSAGE Msg;
+ int Len;
+ char SOGCOG[256] = "";
+ struct STATIONRECORD * Station;
+ struct PORTCONTROL * PORT;
+
+ if (PosnSet == FALSE)
+ return;
+
+ if (PHG) // Send PHG instead of SOG COG
+ strcpy(SOGCOG, PHG);
+ else
+ if (SendSOGCOG | (COG != 0.0))
+ sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG);
+
+ BeaconCounter = BeaconInterval * 60;
+
+ if (ISPort && IGateEnabled)
+ Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!',
+ LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText);
+ else
+ Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!',
+ LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText);
+
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+
+ // Add to dup check list, so we won't digi it if we hear it back
+ // Should we drop it if we've sent it recently ??
+
+ if (CheckforDups(APRSCall, Msg.L2DATA, Len - (19 + sizeof(void *))))
+ return;
+
+ // Add to our station list
+
+ Station = FindStation(APRSCall, TRUE);
+
+ if (Station == NULL)
+ return;
+
+
+ strcpy(Station->Path, "APBPQ1");
+ strcpy(Station->LastPacket, Msg.L2DATA);
+// Station->LastPort = Port;
+
+ DecodeAPRSPayload(Msg.L2DATA, Station);
+ Station->TimeLastUpdated = time(NULL);
+
+ if (toPort)
+ {
+ if (BeaconHddrLen[toPort] == 0)
+ return;
+
+ Debugprintf("Sending APRS Beacon to port %d", toPort);
+
+ memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); // Clear unused digis
+ Msg.DEST[6] |= 0x80; // set Command Bit
+
+ GetSemaphore(&Semaphore, 12);
+ Send_AX_Datagram(&Msg, Len + 2, toPort);
+ FreeSemaphore(&Semaphore);
+
+ return;
+ }
+
+ for (Port = 1; Port <= MaxBPQPortNo; Port++) // Check all ports
+ {
+ if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined
+ {
+ Debugprintf("Sending APRS Beacon to port %d", Port);
+
+ if (ISPort && IGateEnabled)
+ Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!',
+ LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText);
+ else
+ Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!',
+ LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText);
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+
+ memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7);
+ Msg.DEST[6] |= 0x80; // set Command Bit
+
+ GetSemaphore(&Semaphore, 12);
+ Send_AX_Datagram(&Msg, Len + 2, Port);
+ FreeSemaphore(&Semaphore);
+
+ // if Port has interlock set pause before next
+
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ // Just pause for all ports
+
+// if (PORT && PORT->PORTINTERLOCK)
+ Sleep(20000);
+ }
+ }
+ return ;
+}
+
+VOID SendObject(struct OBJECT * Object)
+{
+ int Port;
+ DIGIMESSAGE Msg;
+ int Len;
+
+ // Add to dup list in case we get it back
+
+ CheckforDups(APRSCall, Object->Message, (int)strlen(Object->Message));
+
+ for (Port = 1; Port <= MaxBPQPortNo; Port++)
+ {
+ if (Object->PortMap[Port])
+ {
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+ Len = sprintf(Msg.L2DATA, "%s", Object->Message);
+ memcpy(Msg.DEST, &Object->Path[0][0], Object->PathLen + 1);
+ Msg.DEST[6] |= 0x80; // set Command Bit
+
+ Send_AX_Datagram(&Msg, Len + 2, Port);
+ }
+ }
+
+ // Also send to APRS-IS if connected
+
+ if (APRSISOpen && Object->PortMap[0])
+ {
+ char ISMsg[300];
+ Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", APRSCall, APRSDest, Object->Message);
+ ISSend(sock, ISMsg, Len, 0);
+ }
+}
+
+
+/*
+VOID SendStatus(char * StatusText)
+{
+ int Port;
+ DIGIMESSAGE Msg;
+ int Len;
+
+ if (APRSISOpen)
+ {
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+
+ Len = sprintf(Msg.L2DATA, ">%s", StatusText);
+
+ for (Port = 1; Port <= NUMBEROFPORTS; Port++)
+ {
+ if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined
+ {
+ memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7);
+ Send_AX_Datagram(&Msg, Len + 2, Port);
+ }
+ }
+
+ Len = sprintf(Msg.L2DATA, "%s>%s,TCPIP*:>%s\r\n", APRSCall, APRSDest, StatusText);
+ ISSend(sock, Msg.L2DATA, Len, 0);
+// Debugprintf(">%s", Msg.L2DATA);
+ }
+}
+
+
+*/
+VOID SendIStatus()
+{
+ int Port;
+ DIGIMESSAGE Msg;
+ int Len;
+
+ IStatusCounter = 3600; // One per hour
+
+ if (APRSISOpen && BeacontoIS && RXOnly == 0)
+ {
+ Msg.PID = 0xf0;
+ Msg.CTL = 3;
+
+ Len = sprintf(Msg.L2DATA, "%s,TCPIP*:%s", Msg.L2DATA);
+ }
+
+}
+
+
+VOID DoSecTimer()
+{
+ struct OBJECT * Object = ObjectList;
+
+ while (Object)
+ {
+ Object->Timer--;
+
+ if (Object->Timer == 0)
+ {
+ Object->Timer = 60 * Object->Interval;
+ SendObject(Object);
+ }
+ Object = Object->Next;
+ }
+
+ // Check SatGate Mode delay Q
+
+ if (SatISQueue)
+ {
+ time_t NOW = time(NULL);
+ ISDELAY * SatISEntry = SatISQueue;
+ ISDELAY * Prev = NULL;
+
+ while (SatISEntry)
+ {
+ if (SatISEntry->SendTIme < NOW)
+ {
+ // Send it
+
+ ISSend(sock, SatISEntry->ISMSG, (int)strlen(SatISEntry->ISMSG), 0);
+ free(SatISEntry->ISMSG);
+
+ if (Prev)
+ Prev->Next = SatISEntry->Next;
+ else
+ SatISQueue = SatISEntry->Next;
+
+ free(SatISEntry);
+ return; // unlinkely to get 2 in sam esecond and doesn;t matter if we delay a bit more
+ }
+
+ Prev = SatISEntry;
+ SatISEntry = SatISEntry->Next;
+ }
+ }
+
+ if (ISPort && APRSISOpen == 0 && IGateEnabled)
+ {
+ ISDelayTimer++;
+
+ if (ISDelayTimer > 60)
+ {
+ ISDelayTimer = 0;
+ _beginthread(APRSISThread, 0, (VOID *) TRUE);
+ }
+ }
+
+ if (HostName[0])
+ {
+ if (BlueNMEAOK == 0)
+ {
+ BlueNMEATimer++;
+ if (BlueNMEATimer > 15)
+ {
+ BlueNMEATimer = 0;
+ _beginthread(TCPConnect, 0, 0);
+ }
+ }
+ }
+
+ if (GPSDHost[0])
+ {
+ if (GPSDOK == 0)
+ {
+ GPSDTimer++;
+ if (GPSDTimer > 15)
+ {
+ GPSDTimer = 0;
+ _beginthread(GPSDConnect, 0, 0);
+ }
+ }
+ }
+
+ if (BeaconCounter)
+ {
+ BeaconCounter--;
+
+ if (BeaconCounter == 0)
+ {
+ BeaconCounter = BeaconInterval * 60;
+ SendBeacon(0, StatusMsg, TRUE, FALSE);
+ }
+ }
+
+ if (IStatusCounter)
+ {
+ IStatusCounter--;
+
+ if (IStatusCounter == 0)
+ {
+ SendIStatus();
+ }
+ }
+
+ if (GPSOK)
+ {
+ GPSOK--;
+
+ if (GPSOK == 0)
+#ifdef LINBPQ
+ Debugprintf("GPS Lost");
+#else
+ SetDlgItemText(hConsWnd, IDC_GPS, "No GPS");
+#endif
+ }
+
+ APRSSecTimer(); // Code from APRS APPL
+}
+
+int CountPool()
+{
+ struct STATIONRECORD * ptr = StationRecordPool;
+ int n = 0;
+
+ while (ptr)
+ {
+ n++;
+ ptr = ptr->Next;
+ }
+ return n;
+}
+
+static VOID DoMinTimer()
+{
+ struct STATIONRECORD * ptr = *StationRecords;
+ struct STATIONRECORD * last = NULL;
+ time_t AgeLimit = time(NULL ) - (ExpireTime * 60);
+ int i = 0;
+
+ // Remove old records
+
+ while (ptr)
+ {
+ if (ptr->TimeLastUpdated < AgeLimit)
+ {
+ StationCount--;
+
+ if (last)
+ {
+ last->Next = ptr->Next;
+
+ // Put on front of free chain
+
+ ptr->Next = StationRecordPool;
+ StationRecordPool = ptr;
+
+ ptr = last->Next;
+ }
+ else
+ {
+ // First in list
+
+ *StationRecords = ptr->Next;
+
+ // Put on front of free chain
+
+ ptr->Next = StationRecordPool;
+ StationRecordPool = ptr;
+
+ if (*StationRecords)
+ {
+ ptr = *StationRecords;
+ }
+ else
+ {
+ ptr = NULL;
+ }
+ }
+ }
+ else
+ {
+ last = ptr;
+ ptr = ptr->Next;
+ }
+ }
+}
+
+char APRSMsg[300];
+
+int ISHostIndex = 0;
+char RealISHost[256];
+
+VOID APRSISThread(void * Report)
+{
+ // Receive from core server
+
+ char Signon[500];
+ unsigned char work[4];
+
+ struct sockaddr_in sinx;
+ int addrlen=sizeof(sinx);
+ struct addrinfo hints, *res = 0, *saveres;
+ size_t len;
+ int err;
+ u_long param=1;
+ BOOL bcopt=TRUE;
+ char Buffer[1000];
+ int InputLen = 1; // Non-zero
+ char errmsg[100];
+ char * ptr;
+ size_t inptr = 0;
+ char APRSinMsg[1000];
+ char PortString[20];
+ char serv[256];
+
+ Debugprintf("BPQ32 APRS IS Thread");
+#ifndef LINBPQ
+ SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Connecting");
+#endif
+
+ if (ISFilter[0])
+ sprintf(Signon, "user %s pass %d vers BPQ32 %s filter %s\r\n",
+ APRSCall, ISPasscode, TextVerstring, ISFilter);
+ else
+ sprintf(Signon, "user %s pass %d vers BPQ32 %s\r\n",
+ APRSCall, ISPasscode, TextVerstring);
+
+
+ sprintf(PortString, "%d", ISPort);
+
+ // get host info, make socket, and connect it
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
+ hints.ai_socktype = SOCK_STREAM;
+ getaddrinfo(ISHost, PortString, &hints, &res);
+
+ InputLen = sprintf(errmsg, "Connecting to APRS Host %s\r\n", ISHost);
+ MonitorAPRSIS(errmsg, InputLen, FALSE);
+
+ if (!res)
+ {
+ err = WSAGetLastError();
+ InputLen = sprintf(errmsg, "APRS IS Resolve %s Failed Error %d\r\n", ISHost, err);
+ MonitorAPRSIS(errmsg, InputLen, FALSE);
+
+ return; // Resolve failed
+
+ }
+
+ // Step thorough the list of hosts
+
+ saveres = res; // Save for free
+
+ if (res->ai_next) // More than one
+ {
+ int n = ISHostIndex;
+
+ while (n && res->ai_next)
+ {
+ res = res->ai_next;
+ n--;
+ }
+
+ if (n)
+ {
+ // We have run off the end of the list
+
+ ISHostIndex = 0; // Back to start
+ res = saveres;
+ }
+ else
+ ISHostIndex++;
+
+ }
+
+ getnameinfo(res->ai_addr, (int)res->ai_addrlen, RealISHost, 256, serv, 256, 0);
+
+ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+
+ if (sock == INVALID_SOCKET)
+ return;
+
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);
+
+ memcpy(work, res->ai_addr->sa_data, 4);
+
+ Debugprintf("Trying APRSIS Host %d.%d.%d.%d (%d) %s", work[0], work[1], work[2], work[3], ISHostIndex, RealISHost);
+
+ if (connect(sock, res->ai_addr, (int)res->ai_addrlen))
+ {
+ err=WSAGetLastError();
+
+ //
+ // Connect failed
+ //
+
+#ifndef LINBPQ
+ MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connect Failed");
+#else
+ printf("APRS Igate connect failed\n");
+#endif
+ err=WSAGetLastError();
+ InputLen = sprintf(errmsg, "Connect Failed %s af %d Error %d \r\n", RealISHost, res->ai_family, err);
+ MonitorAPRSIS(errmsg, InputLen, FALSE);
+
+ freeaddrinfo(res);
+ return;
+ }
+
+ freeaddrinfo(saveres);
+
+ APRSISOpen = TRUE;
+
+#ifndef LINBPQ
+ MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connected");
+#endif
+
+ InputLen=recv(sock, Buffer, 500, 0);
+
+ if (InputLen > 0)
+ {
+ Buffer[InputLen] = 0;
+ Debugprintf(Buffer);
+ MonitorAPRSIS(Buffer, InputLen, FALSE);
+ }
+
+ ISSend(sock, Signon, (int)strlen(Signon), 0);
+/*
+ InputLen=recv(sock, Buffer, 500, 0);
+
+ if (InputLen > 0)
+ {
+ Buffer[InputLen] = 0;
+ Debugprintf(Buffer);
+ MonitorAPRSIS(Buffer, InputLen, FALSE);
+ }
+
+ InputLen=recv(sock, Buffer, 500, 0);
+
+ if (InputLen > 0)
+ {
+ Buffer[InputLen] = 0;
+ Debugprintf(Buffer);
+ MonitorAPRSIS(Buffer, InputLen, FALSE);
+ }
+*/
+ while (InputLen > 0 && IGateEnabled)
+ {
+ InputLen = recv(sock, &APRSinMsg[inptr], (int)(500 - inptr), 0);
+
+ if (InputLen > 0)
+ {
+ inptr += InputLen;
+
+ ptr = memchr(APRSinMsg, 0x0a, inptr);
+
+ while (ptr != NULL)
+ {
+ ptr++; // include lf
+ len = ptr-(char *)APRSinMsg;
+
+ inptr -= len; // bytes left
+
+ // UIView server has a null before crlf
+
+ if (*(ptr - 3) == 0)
+ {
+ *(ptr - 3) = 13;
+ *(ptr - 2) = 10;
+ *(ptr - 1) = 0;
+
+ len --;
+ }
+
+ if (len > 10 && len < 300) // Ignore if way too long or too short
+ {
+ memcpy(&APRSMsg, APRSinMsg, len);
+ MonitorAPRSIS(APRSMsg, (int)len, FALSE);
+ if (APRSMsg[len - 2] == 13)
+ APRSMsg[len - 2] = 0;
+ else
+ APRSMsg[len - 1] = 0;
+
+// Debugprintf("%s", APRSMsg);
+
+ ProcessAPRSISMsg(APRSMsg);
+ }
+
+ if (inptr > 0)
+ {
+ memmove(APRSinMsg, ptr, inptr);
+ ptr = memchr(APRSinMsg, 0x0a, inptr);
+ }
+ else
+ ptr = 0;
+
+ if (inptr < 0)
+ break;
+ }
+ }
+ }
+
+ closesocket(sock);
+
+ APRSISOpen = FALSE;
+
+ Debugprintf("BPQ32 APRS IS Thread Exited");
+
+#ifndef LINBPQ
+ if (IGateEnabled)
+ SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disconnected");
+ else
+ SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disabled");
+#endif
+ ISDelayTimer = 30; // Retry pretty quickly
+ return;
+}
+
+VOID ProcessAPRSISMsg(char * APRSMsg)
+{
+ char * Payload;
+ char * Source;
+ char * Dest;
+ char IGateCall[10] = " ";
+ char * ptr;
+ char Message[255];
+ PAPRSHEARDRECORD MH;
+ time_t NOW = time(NULL);
+ char ISCopy[1024];
+ struct STATIONRECORD * Station = NULL;
+#ifdef WIN32
+ struct _EXCEPTION_POINTERS exinfo;
+ char EXCEPTMSG[80] = "";
+#endif
+
+ if (APRSMsg[0] == '#') // Comment
+ return;
+
+ // if APRS Appl is atttached, queue message to it
+
+ strcpy(ISCopy, APRSMsg);
+
+ GetSemaphore(&Semaphore, 12);
+
+#ifdef WIN32
+
+ strcpy(EXCEPTMSG, "ProcessAPRSISMsg");
+
+ __try
+ {
+
+ Station = DecodeAPRSISMsg(ISCopy);
+
+ }
+ #include "StdExcept.c"
+ Debugprintf(APRSMsg);
+ }
+#else
+ Station = DecodeAPRSISMsg(ISCopy);
+#endif
+
+ FreeSemaphore(&Semaphore);
+
+//}WB4APR-14>APRS,RELAY,TCPIP,G9RXG*::G3NRWVVVV:Hi Ian{001
+//KE7XO-2>hg,TCPIP*,qAC,T2USASW::G8BPQ-14 :Path - G8BPQ-14>APU25N
+//IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data
+
+ Payload = strchr(APRSMsg, ':');
+
+ // Get call of originating Igate
+
+ ptr = Payload;
+
+ if (Payload == NULL)
+ return;
+
+ *(Payload++) = 0;
+
+ while (ptr[0] != ',')
+ ptr--;
+
+ ptr++;
+
+ if (strlen(ptr) > 9)
+ return;
+
+ memcpy(IGateCall, ptr, (int)strlen(ptr));
+
+ if (strstr(APRSMsg, ",qAS,") == 0) // Findu generates invalid q construct
+ {
+ MH = FindStationInMH(IGateCall);
+ if (MH)
+ {
+// Debugprintf("Setting Igate Flag - %s:%s", APRSMsg, Payload);
+ MH->IGate = TRUE; // If we have seen this station on RF, set it as an Igate
+ }
+ }
+ Source = APRSMsg;
+ Dest = strchr(APRSMsg, '>');
+
+ if (Dest == NULL)
+ return;
+
+ *(Dest++) = 0; // Termainate Source
+ ptr = strchr(Dest, ',');
+
+ if (ptr)
+ *ptr = 0;
+
+ MH = UpdateHeard(Source, 0);
+
+ MH->Station = Station;
+
+ // See if we should gate to RF.
+
+ // Have we heard dest recently? (use the message dest (not ax.25 dest) - does this mean we only gate Messages?
+ // Not if it is an Igate (it will get a copy direct)
+ // Have we recently sent a message from this call - if so, we gate the next Position
+
+/*
+
+ From http://www.aprs-is.net/IGateDetails.aspx
+
+ Gate message packets and associated posits to RF if all of the following are true:
+
+ the receiving station has been heard within range within a predefined time period (range defined
+ as digi hops, distance, or both).
+
+ the sending station has not been heard via RF within a predefined time period (packets gated
+ from the Internet by other stations are excluded from this test).
+
+ the sending station does not have TCPXX, NOGATE, or RFONLY in the header.
+
+ the receiving station has not been heard via the Internet within a predefined time period.
+
+ A station is said to be heard via the Internet if packets from the station contain TCPIP* or
+ TCPXX* in the header or if gated (3rd-party) packets are seen on RF gated by the station
+ and containing TCPIP or TCPXX in the 3rd-party header (in other words, the station is seen on RF
+ as being an IGate).
+
+*/
+
+ if (Payload[0] == ':') // Message
+ {
+ char MsgDest[10];
+ APRSHEARDRECORD * STN;
+
+ if (strlen(Payload) > 100) // I don't think any valid APRS msgs are more than this
+ return;
+
+ memcpy(MsgDest, &Payload[1], 9);
+ MsgDest[9] = 0;
+
+ if (strcmp(MsgDest, CallPadded) == 0) // to us
+ return;
+
+ // Check that the sending station has not been heard via RF recently
+
+ if (MH->rfPort && (NOW - MH->MHTIME) < GATETIMELIMIT)
+ return;
+
+ STN = FindStationInMH(MsgDest);
+
+ // Shouldn't we check DUP list, in case we have digi'ed this message directly?
+
+ if (CheckforDups(Source, Payload, (int)strlen(Payload)))
+ return;
+
+ // has the receiving station has been heard on RF and is not an IGate
+
+ if (STN && STN->rfPort && !STN->IGate && (NOW - STN->MHTIME) < GATETIMELIMIT)
+ {
+ sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload);
+
+ GetSemaphore(&Semaphore, 12);
+ SendAPRSMessageEx(Message, STN->rfPort, APRSCall, 1); // Set gated to IS flag
+ FreeSemaphore(&Semaphore);
+
+ MessageCount++;
+ MH->LASTMSG = NOW;
+
+ return;
+ }
+ }
+
+ // Not a message. If it is a position report gate if have sent a message recently
+
+ if (Payload[0] == '!' || Payload[0] == '/' || Payload[0] == '=' || Payload[0] == '@') // Posn Reports
+ {
+ if ((NOW - MH->LASTMSG) < 900 && MH->rfPort)
+ {
+ sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload);
+
+ GetSemaphore(&Semaphore, 12);
+ SendAPRSMessageEx(Message, MH->rfPort, APRSCall, 1); // Set gated to IS flag
+ FreeSemaphore(&Semaphore);
+
+ return;
+ }
+ }
+
+ // If Gate Local to RF is defined, and station is in range, Gate it
+
+ if (GateLocal && Station)
+ {
+ if (Station->Object)
+ Station = Station->Object; // If Object Report, base distance on Object, not station
+
+ if (Station->Lat != 0.0 && Station->Lon != 0.0 && myDistance(Station->Lat, Station->Lon, 0) < GateLocalDistance)
+ {
+ sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload);
+ GetSemaphore(&Semaphore, 12);
+ SendAPRSMessage(Message, -1); // Send to all APRS Ports
+ FreeSemaphore(&Semaphore);
+
+ return;
+ }
+ }
+}
+
+APRSHEARDRECORD * FindStationInMH(char * Call)
+{
+ APRSHEARDRECORD * MH = MHDATA;
+ int i;
+
+ // We keep call in ascii format, as that is what we get from APRS-IS, and we have it in that form
+
+ for (i = 0; i < HEARDENTRIES; i++)
+ {
+ if (memcmp(Call, MH->MHCALL, 9) == 0)
+ return MH;
+
+ MH++;
+ }
+
+ return NULL;
+}
+
+APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port)
+{
+ APRSHEARDRECORD * MH = MHDATA;
+ APRSHEARDRECORD * MHBASE = MH;
+ int i;
+ time_t NOW = time(NULL);
+ time_t OLDEST = NOW - MAXAGE;
+ char CallPadded[10] = " ";
+ BOOL SaveIGate = FALSE;
+ time_t SaveLastMsg = 0;
+ int SaveheardViaIS = 0;
+
+ // We keep call in ascii format, space padded, as that is what we get from APRS-IS, and we have it in that form
+
+ // Make Sure Space Padded
+
+ memcpy(CallPadded, Call, (int)strlen(Call));
+
+ for (i = 0; i < MAXHEARDENTRIES; i++)
+ {
+ if (memcmp(CallPadded, MH->MHCALL, 10) == 0)
+ {
+ // if from APRS-IS, only update if record hasn't been heard via RF
+
+ if (Port == 0)
+ MH->heardViaIS = 1; // Flag heard via IS
+
+ if (Port == 0 && MH->rfPort)
+ return MH; // Don't update RF with IS
+
+ if (Port == MH->rfPort)
+ {
+ SaveIGate = MH->IGate;
+ SaveLastMsg = MH->LASTMSG;
+ SaveheardViaIS = MH->heardViaIS;
+ goto DoMove;
+ }
+ }
+
+ if (MH->MHCALL[0] == 0 || MH->MHTIME < OLDEST) // Spare entry
+ goto DoMove;
+
+ MH++;
+ }
+
+ // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP
+
+ i = MAXHEARDENTRIES - 1;
+
+ // Move others down and add at front
+DoMove:
+ if (i != 0) // First
+ memmove(MHBASE + 1, MHBASE, i * sizeof(APRSHEARDRECORD));
+
+ if (i >= HEARDENTRIES)
+ {
+ char Status[80];
+
+ HEARDENTRIES = i + 1;
+
+ sprintf(Status, "IGATE Stats: Msgs %d Local Stns %d", MessageCount , CountLocalStations());
+#ifndef LINBPQ
+ SetDlgItemText(hConsWnd, IGATESTATS, Status);
+#endif
+ }
+
+ memcpy (MHBASE->MHCALL, CallPadded, 10);
+ MHBASE->rfPort = Port;
+ MHBASE->MHTIME = NOW;
+ MHBASE->IGate = SaveIGate;
+ MHBASE->LASTMSG = SaveLastMsg;
+ MHBASE->heardViaIS = SaveheardViaIS;
+
+ return MHBASE;
+}
+
+int CountLocalStations()
+{
+ APRSHEARDRECORD * MH = MHDATA;
+ int i, n = 0;
+
+ for (i = 0; i < HEARDENTRIES; i++)
+ {
+ if (MH->rfPort) // DOn't count IS Stations
+ n++;
+
+ MH++;
+ }
+ return n;
+}
+
+
+BOOL CheckforDups(char * Call, char * Msg, int Len)
+{
+ // Primitive duplicate suppression - see if same call and text reeived in last few seconds
+
+ time_t Now = time(NULL);
+ time_t DupCheck = Now - DUPSECONDS;
+ int i, saveindex = -1;
+ char * ptr1;
+
+ if (Len < 1)
+ return TRUE;
+
+ for (i = 0; i < MAXDUPS; i++)
+ {
+ if (DupInfo[i].DupTime < DupCheck)
+ {
+ // too old - use first if we need to save it
+
+ if (saveindex == -1)
+ {
+ saveindex = i;
+ }
+
+ if (DupInfo[i].DupTime == 0) // Off end of used area
+ break;
+
+ continue;
+ }
+
+ if ((Len == DupInfo[i].DupLen || (DupInfo[i].DupLen == 99 && Len > 99)) && memcmp(Call, DupInfo[i].DupUser, 7) == 0 && (memcmp(Msg, DupInfo[i].DupText, DupInfo[i].DupLen) == 0))
+ {
+ // Duplicate, so discard
+
+ Msg[Len] = 0;
+ ptr1 = strchr(Msg, 13);
+ if (ptr1)
+ *ptr1 = 0;
+
+// Debugprintf("Duplicate Message supressed %s", Msg);
+ return TRUE; // Duplicate
+ }
+ }
+
+ // Not in list
+
+ if (saveindex == -1) // List is full
+ saveindex = MAXDUPS - 1; // Stick on end
+
+ DupInfo[saveindex].DupTime = Now;
+ memcpy(DupInfo[saveindex].DupUser, Call, 7);
+
+ if (Len > 99) Len = 99;
+
+ DupInfo[saveindex].DupLen = Len;
+ memcpy(DupInfo[saveindex].DupText, Msg, Len);
+
+ return FALSE;
+}
+
+char * FormatAPRSMH(APRSHEARDRECORD * MH)
+ {
+ // Called from CMD.ASM
+
+ struct tm * TM;
+ static char MHLine[50];
+ time_t szClock = MH->MHTIME;
+
+ szClock = (time(NULL) - szClock);
+ TM = gmtime(&szClock);
+
+ sprintf(MHLine, "%-10s %d %.2d:%.2d:%.2d:%.2d %s\r",
+ MH->MHCALL, MH->rfPort, TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, (MH->IGate) ? "IGATE" : "");
+
+ return MHLine;
+ }
+
+// GPS Handling Code
+
+void SelectSource(BOOL Recovering);
+void DecodeRMC(char * msg, size_t len);
+
+void PollGPSIn();
+
+
+UINT GPSType = 0xffff; // Source of Postion info - 1 = Phillips 2 = AIT1000. ffff = not posn message
+
+int RecoveryTimer; // Serial Port recovery
+
+double PI = 3.1415926535;
+double P2 = 3.1415926535 / 180;
+
+double Latitude, Longtitude, SOG, COG, LatIncrement, LongIncrement;
+double LastSOG = -1.0;
+
+BOOL Check0183CheckSum(char * msg, size_t len)
+{
+ BOOL retcode=TRUE;
+ char * ptr;
+ UCHAR sum,xsum1,xsum2;
+
+ sum=0;
+ ptr=++msg; // Skip $
+
+loop:
+
+ if (*(ptr)=='*') goto eom;
+
+ sum ^=*(ptr++);
+
+ len--;
+
+ if (len > 0) goto loop;
+
+ return TRUE; // No Checksum
+
+eom:
+ _strupr(ptr);
+
+ xsum1=*(++ptr);
+ xsum1-=0x30;
+ if (xsum1 > 9) xsum1-=7;
+
+ xsum2=*(++ptr);
+ xsum2-=0x30;
+ if (xsum2 > 9) xsum2-=7;
+
+ xsum1=xsum1<<4;
+ xsum1+=xsum2;
+
+ return (xsum1==sum);
+}
+
+BOOL OpenGPSPort()
+{
+ struct PortInfo * portptr = &InPorts[0];
+
+ // open COMM device
+
+ if (strlen(GPSPort) < 4)
+ {
+ int port = atoi(GPSPort);
+#ifdef WIN32
+ sprintf(GPSPort, "COM%d", port);
+#else
+ sprintf(GPSPort, "com%d", port);
+#endif
+ }
+
+ portptr->hDevice = OpenCOMPort(GPSPort, GPSSpeed, TRUE, TRUE, FALSE, 0);
+
+ if (portptr->hDevice == 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void PollGPSIn()
+{
+ size_t len;
+ char GPSMsg[2000] = "$GPRMC,061213.000,A,5151.5021,N,00056.8388,E,0.15,324.11,190414,,,A*6F";
+ char * ptr;
+ struct PortInfo * portptr;
+
+ portptr = &InPorts[0];
+
+ if (!portptr->hDevice)
+ return;
+
+ getgpsin:
+
+// Comm Error - probably lost USB Port. Try closing and reopening after a delay
+
+// if (RecoveryTimer == 0)
+// {
+// RecoveryTimer = 100; // 10 Secs
+// return;
+// }
+// }
+
+ if (portptr->gpsinptr == 160)
+ portptr->gpsinptr = 0;
+
+ len = ReadCOMBlock(portptr->hDevice, &portptr->GPSinMsg[portptr->gpsinptr],
+ 160 - portptr->gpsinptr);
+
+ if (len > 0)
+ {
+ portptr->gpsinptr += (int)len;
+
+ ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr);
+
+ while (ptr != NULL)
+ {
+ ptr++; // include lf
+ len=ptr-(char *)&portptr->GPSinMsg;
+ memcpy(&GPSMsg,portptr->GPSinMsg,len);
+
+ GPSMsg[len] = 0;
+
+ if (Check0183CheckSum(GPSMsg, len))
+ if (memcmp(&GPSMsg[3], "RMC", 3) == 0)
+ DecodeRMC(GPSMsg, len);
+
+ portptr->gpsinptr -= (int)len; // bytes left
+
+ if (portptr->gpsinptr > 0 && *ptr == 0)
+ {
+ *ptr++;
+ portptr->gpsinptr--;
+ }
+
+ if (portptr->gpsinptr > 0)
+ {
+ memmove(portptr->GPSinMsg,ptr, portptr->gpsinptr);
+ ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr);
+ }
+ else
+ ptr=0;
+ }
+
+ goto getgpsin;
+ }
+ return;
+}
+
+
+void ClosePorts()
+{
+ if (InPorts[0].hDevice)
+ {
+ CloseCOMPort(InPorts[0].hDevice);
+ InPorts[0].hDevice=0;
+ }
+
+ return;
+}
+
+void DecodeRMC(char * msg, size_t len)
+{
+ char * ptr1;
+ char * ptr2;
+ char TimHH[3], TimMM[3], TimSS[3];
+ char OurSog[5], OurCog[4];
+ char LatDeg[3], LonDeg[4];
+ char NewLat[10] = "", NewLon[10] = "";
+ struct STATIONRECORD * Stn1;
+
+ char Day[3];
+
+ ptr1 = &msg[7];
+
+ len-=7;
+
+ ptr2=(char *)memchr(ptr1,',',15);
+
+ if (ptr2 == 0) return; // Duff
+
+ *(ptr2++)=0;
+
+ memcpy(TimHH,ptr1,2);
+ memcpy(TimMM,ptr1+2,2);
+ memcpy(TimSS,ptr1+4,2);
+ TimHH[2]=0;
+ TimMM[2]=0;
+ TimSS[2]=0;
+
+ ptr1=ptr2;
+
+ if (*(ptr1) != 'A') // ' Data Not Valid
+ {
+#ifndef LINBPQ
+ SetDlgItemText(hConsWnd, IDC_GPS, "No GPS Fix");
+#endif
+ return;
+ }
+
+ ptr1+=2;
+
+ ptr2=(char *)memchr(ptr1,',',15);
+
+ if (ptr2 == 0) return; // Duff
+
+ *(ptr2++)=0;
+
+ memcpy(NewLat, ptr1, 7);
+ memcpy(LatDeg, ptr1, 2);
+ LatDeg[2]=0;
+ Lat=atof(LatDeg) + (atof(ptr1+2)/60);
+
+ if (*(ptr1+7) > '4') if (NewLat[6] < '9') NewLat[6]++;
+
+ ptr1=ptr2;
+
+ NewLat[7] = (*ptr1);
+ if ((*ptr1) == 'S') Lat=-Lat;
+
+ ptr1+=2;
+
+ ptr2=(char *)memchr(ptr1,',',15);
+
+ if (ptr2 == 0) return; // Duff
+ *(ptr2++)=0;
+
+ memcpy(NewLon, ptr1, 8);
+
+ memcpy(LonDeg,ptr1,3);
+ LonDeg[3]=0;
+ Lon=atof(LonDeg) + (atof(ptr1+3)/60);
+
+ if (*(ptr1+8) > '4') if (NewLon[7] < '9') NewLon[7]++;
+
+ ptr1=ptr2;
+
+ NewLon[8] = (*ptr1);
+ if ((*ptr1) == 'W') Lon=-Lon;
+
+ // Now have a valid posn, so stop sending Undefined LOC Sysbol
+
+ SYMBOL = CFGSYMBOL;
+ SYMSET = CFGSYMSET;
+
+ PosnSet = TRUE;
+
+ Stn1 = (struct STATIONRECORD *)StnRecordBase; // Pass to App
+ Stn1->Lat = Lat;
+ Stn1->Lon = Lon;
+
+ if (GPSOK == 0)
+ {
+#ifdef LINBPQ
+ Debugprintf("GPS OK");
+ printf("GPS OK\n");
+#else
+ SetDlgItemText(hConsWnd, IDC_GPS, "GPS OK");
+#endif
+ }
+
+ GPSOK = 30;
+
+ ptr1+=2;
+
+ ptr2 = (char *)memchr(ptr1,',',30);
+
+ if (ptr2 == 0) return; // Duff
+
+ *(ptr2++)=0;
+
+ memcpy(OurSog, ptr1, 4);
+ OurSog[4] = 0;
+
+ ptr1=ptr2;
+
+ ptr2 = (char *)memchr(ptr1,',',15);
+
+ if (ptr2 == 0) return; // Duff
+
+ *(ptr2++)=0;
+
+ memcpy(OurCog, ptr1, 3);
+ OurCog[3] = 0;
+
+ memcpy(Day,ptr2,2);
+ Day[2]=0;
+
+ SOG = atof(OurSog);
+ COG = atof(OurCog);
+
+ if (strcmp(NewLat, LAT) || strcmp(NewLon, LON))
+ {
+ if (MobileBeaconInterval)
+ {
+ time_t NOW = time(NULL);
+
+ if ((NOW - LastMobileBeacon) > MobileBeaconInterval)
+ {
+ LastMobileBeacon = NOW;
+ SendBeacon(0, StatusMsg, FALSE, TRUE);
+ }
+ }
+ if (GPSSetsLocator)
+ {
+ ToLOC(Lat, Lon, LOC);
+ sprintf(LOCATOR, "%f:%f", Lat, Lon);
+ }
+ }
+
+ strcpy(LAT, NewLat);
+ strcpy(LON, NewLon);
+}
+
+Dll VOID APIENTRY APRSConnect(char * Call, char * Filter)
+{
+ // Request APRS Data from Switch (called by APRS Applications)
+
+ APRSApplConnected = TRUE;
+ APRSWeb = TRUE;
+
+ strcpy(APPLFilter, Filter);
+
+ if (APPLFilter[0])
+ {
+ // This is called in APPL context so must queue the message
+
+ char Msg[2000];
+ PMSGWITHLEN buffptr;
+
+ sprintf(Msg, "filter %s", Filter);
+
+ if (strlen(Msg) > 240)
+ Msg[240] = 0;
+
+
+ GetSemaphore(&Semaphore, 11);
+
+ buffptr = GetBuff();
+
+ if (buffptr)
+ {
+ buffptr->Len = 0;
+ strcpy(&buffptr->Data[0], "SERVER");
+ strcpy(&buffptr->Data[10], Msg);
+ C_Q_ADD(&APPLTX_Q, buffptr);
+ }
+
+ buffptr = GetBuff();
+
+ if (buffptr)
+ {
+ buffptr->Len = 0;
+ strcpy(&buffptr->Data[0], "SERVER");
+ strcpy(&buffptr->Data[10], "filter?");
+ C_Q_ADD(&APPLTX_Q, buffptr);
+ }
+ FreeSemaphore(&Semaphore);
+ }
+ strcpy(Call, CallPadded);
+}
+
+Dll VOID APIENTRY APRSDisconnect()
+{
+ // Stop requesting APRS Data from Switch (called by APRS Applications)
+
+ char Msg[2000];
+ PMSGWITHLEN buffptr;
+
+ strcpy(ISFilter, NodeFilter);
+ sprintf(Msg, "filter %s", NodeFilter);
+
+ APRSApplConnected = FALSE;
+ APRSWeb = FALSE;
+
+ GetSemaphore(&Semaphore, 11);
+
+ buffptr = GetBuff();
+
+ if (buffptr)
+ {
+ buffptr->Len = 0;
+ strcpy(&buffptr->Data[0], "SERVER");
+ strcpy(&buffptr->Data[10], Msg);
+ C_Q_ADD(&APPLTX_Q, buffptr);
+ }
+
+ buffptr = GetBuff();
+
+ if (buffptr)
+ {
+ buffptr->Len = 0;
+ strcpy(&buffptr->Data[0], "SERVER");
+ strcpy(&buffptr->Data[10], "filter?");
+ C_Q_ADD(&APPLTX_Q, buffptr);
+ }
+
+ while (APPL_Q)
+ {
+ buffptr = Q_REM(&APPL_Q);
+ ReleaseBuffer(buffptr);
+ }
+
+ FreeSemaphore(&Semaphore);
+}
+
+
+Dll char * APIENTRY APRSGetStatusMsgPtr()
+{
+ return StatusMsg;
+}
+
+
+
+Dll BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call)
+{
+ // Request APRS Data from Switch (called by APRS Applications)
+
+ void ** buffptr;
+#ifdef bpq32
+ struct _EXCEPTION_POINTERS exinfo;
+#endif
+
+ GetSemaphore(&Semaphore, 10);
+ {
+ if (APPL_Q)
+ {
+ buffptr = Q_REM(&APPL_Q);
+
+ memcpy(Call, (char *)&buffptr[2], 12);
+ strcpy(Frame, (char *)&buffptr[5]);
+
+ ReleaseBuffer(buffptr);
+ FreeSemaphore(&Semaphore);
+ return TRUE;
+ }
+ }
+
+ FreeSemaphore(&Semaphore);
+
+ return FALSE;
+}
+
+Dll BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port)
+{
+ // Called from BPQAPRS App
+ // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context)
+
+ PMSGWITHLEN buffptr;
+
+ GetSemaphore(&Semaphore, 11);
+
+ buffptr = GetBuff();
+
+ if (buffptr)
+ {
+ buffptr->Len = ++Len; // Len doesn't include Null
+ memcpy(&buffptr->Data[0], Frame, Len);
+ C_Q_ADD(&APPLTX_Q, buffptr);
+ }
+
+// buffptr-> = Port; // Pass to SendAPRSMessage();
+
+ FreeSemaphore(&Semaphore);
+
+ return TRUE;
+}
+
+Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall)
+{
+ // Called from BPQAPRS App or BPQMail
+ // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context)
+
+ PMSGWITHLEN buffptr;
+
+ if (APRSActive == 0)
+ return FALSE;
+
+ GetSemaphore(&Semaphore, 11);
+
+ buffptr = GetBuff();
+
+ if (buffptr)
+ {
+ buffptr->Len = 0;
+ memcpy(&buffptr->Data[0], ToCall, 9);
+ buffptr->Data[9] = 0;
+ strcpy(&buffptr->Data[10], Text);
+ C_Q_ADD(&APPLTX_Q, buffptr);
+ }
+
+ FreeSemaphore(&Semaphore);
+
+ return TRUE;
+}
+
+Dll BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon)
+{
+ *PLat = Lat;
+ *PLon = Lon;
+
+ return GPSOK;
+}
+
+Dll BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon)
+{
+ strcpy(PLat, LAT);
+ strcpy(PLon, LON);
+
+ return GPSOK;
+}
+
+// Code to support getting GPS from Andriod Device running BlueNMEA
+
+
+#define SD_BOTH 0x02
+
+static VOID ProcessReceivedData(SOCKET TCPSock)
+{
+ char UDPMsg[8192];
+ char Buffer[65536];
+
+ int len = recv(TCPSock, Buffer, 65500, 0);
+
+ char * ptr;
+ char * Lastptr;
+
+ if (len <= 0)
+ {
+ closesocket(TCPSock);
+ BlueNMEAOK = FALSE;
+ return;
+ }
+
+ ptr = Lastptr = Buffer;
+ Buffer[len] = 0;
+
+ while (len > 0)
+ {
+ ptr = strchr(Lastptr, 10);
+
+ if (ptr)
+ {
+ size_t Len = ptr - Lastptr -1;
+
+ if (Len > 8100)
+ return;
+
+ memcpy(UDPMsg, Lastptr, Len);
+ UDPMsg[Len++] = 13;
+ UDPMsg[Len++] = 10;
+ UDPMsg[Len] = 0;
+
+ if (!Check0183CheckSum(UDPMsg, Len))
+ {
+ Debugprintf("Checksum Error %s", UDPMsg);
+ }
+ else
+ {
+ if (memcmp(&UDPMsg[3], "RMC", 3) == 0)
+ DecodeRMC(UDPMsg, Len);
+
+ else if (memcmp(UDPMsg, "!AIVDM", 6) == 0)
+ ProcessAISMessage(UDPMsg, Len);
+
+ }
+ Lastptr = ptr + 1;
+ len -= (int)Len;
+ }
+ else
+ return;
+ }
+}
+
+static VOID TCPConnect(void * unused)
+{
+ int err, ret;
+ u_long param=1;
+ BOOL bcopt=TRUE;
+ fd_set readfs;
+ fd_set errorfs;
+ struct timeval timeout;
+ struct sockaddr_in destaddr;
+ SOCKET TCPSock;
+
+ if (HostName[0] == 0)
+ return;
+
+ destaddr.sin_addr.s_addr = inet_addr(HostName);
+ destaddr.sin_family = AF_INET;
+ destaddr.sin_port = htons(HostPort);
+
+ if (destaddr.sin_addr.s_addr == INADDR_NONE)
+ {
+ // Resolve name to address
+
+ struct hostent * HostEnt = gethostbyname(HostName);
+
+ if (!HostEnt)
+ return; // Resolve failed
+
+ memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
+ }
+
+ TCPSock = socket(AF_INET,SOCK_STREAM,0);
+
+ if (TCPSock == INVALID_SOCKET)
+ {
+ return;
+ }
+
+ setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4);
+
+ BlueNMEAOK = TRUE; // So we don't try to reconnect while waiting
+
+ if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0)
+ {
+ //
+ // Connected successful
+ //
+
+ ioctl(TCPSock, FIONBIO, ¶m);
+ }
+ else
+ {
+ err=WSAGetLastError();
+#ifdef LINBPQ
+ printf("Connect Failed for AIS socket - error code = %d\n", err);
+#else
+ Debugprintf("Connect Failed for AIS socket - error code = %d", err);
+#endif
+ closesocket(TCPSock);
+ BlueNMEAOK = FALSE;
+
+ return;
+ }
+
+ BlueNMEAOK = TRUE;
+
+ while (TRUE)
+ {
+ FD_ZERO(&readfs);
+ FD_ZERO(&errorfs);
+
+ FD_SET(TCPSock,&readfs);
+ FD_SET(TCPSock,&errorfs);
+
+ timeout.tv_sec = 900;
+ timeout.tv_usec = 0; // We should get messages more frequently that this
+
+ ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout);
+
+ if (ret == SOCKET_ERROR)
+ {
+ goto Lost;
+ }
+ if (ret > 0)
+ {
+ // See what happened
+
+ if (FD_ISSET(TCPSock, &readfs))
+ {
+ ProcessReceivedData(TCPSock);
+ }
+
+ if (FD_ISSET(TCPSock, &errorfs))
+ {
+Lost:
+#ifdef LINBPQ
+ printf("AIS Connection lost\n");
+#endif
+ closesocket(TCPSock);
+ BlueNMEAOK = FALSE;;
+ return;
+ }
+ }
+ else
+ {
+ // 15 mins without data. Shouldn't happen
+
+ shutdown(TCPSock, SD_BOTH);
+ Sleep(100);
+
+ closesocket(TCPSock);
+ BlueNMEAOK = FALSE;
+ return;
+ }
+ }
+}
+
+int GPSDAlerted = 0;
+
+static VOID GPSDConnect(void * unused)
+{
+ int err, ret;
+ u_long param=1;
+ BOOL bcopt=TRUE;
+ fd_set readfs;
+ fd_set errorfs;
+ struct timeval timeout;
+ struct sockaddr_in destaddr;
+ SOCKET TCPSock;
+
+ if (GPSDHost[0] == 0)
+ return;
+
+ destaddr.sin_addr.s_addr = inet_addr(GPSDHost);
+ destaddr.sin_family = AF_INET;
+ destaddr.sin_port = htons(GPSDPort);
+
+ TCPSock = socket(AF_INET,SOCK_STREAM,0);
+
+ if (TCPSock == INVALID_SOCKET)
+ {
+ return;
+ }
+
+ setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4);
+
+ GPSDOK = TRUE; // So we don't try to reconnect while waiting
+
+ if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0)
+ {
+ //
+ // Connected successful
+ //
+
+#ifdef LINBPQ
+ printf("GPSD Connected\n");
+#else
+ Debugprintf("GPSD Connected");
+#endif
+ GPSDAlerted = 0;
+ ioctl(TCPSock, FIONBIO, ¶m);
+
+ // Request data
+
+ send(TCPSock, "?WATCH={\"enable\":true,\"nmea\":true}\r\n", 36, 0);
+ }
+ else
+ {
+ err=WSAGetLastError();
+ if (GPSDAlerted == 0)
+#ifdef LINBPQ
+ printf("GPSD Connect Failed - error code = %d\n", err);
+#else
+ Debugprintf("GPSD Connect Failed - error code = %d", err);
+#endif
+ GPSDAlerted = 1;
+ closesocket(TCPSock);
+ GPSDOK = FALSE;
+
+ return;
+ }
+
+ while (TRUE)
+ {
+ FD_ZERO(&readfs);
+ FD_ZERO(&errorfs);
+
+ FD_SET(TCPSock,&readfs);
+ FD_SET(TCPSock,&errorfs);
+
+ timeout.tv_sec = 900;
+ timeout.tv_usec = 0; // We should get messages more frequently that this
+
+ ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout);
+
+ if (ret == SOCKET_ERROR)
+ {
+ goto Lost;
+ }
+ if (ret > 0)
+ {
+ // See what happened
+
+ if (FD_ISSET(TCPSock, &readfs))
+ {
+ char Buffer[65536];
+ int len = recv(TCPSock, Buffer, 65500, 0);
+ char TCPMsg[8192];
+
+ char * ptr;
+ char * Lastptr;
+
+ if (len == 0)
+ {
+ closesocket(TCPSock);
+ GPSDOK = FALSE;;
+ return;
+ }
+
+ if (len < 9000)
+ {
+ Buffer[len] = 0;
+
+ ptr = Lastptr = Buffer;
+ Buffer[len] = 0;
+
+ while (len > 0)
+ {
+ ptr = strchr(Lastptr, 10);
+
+ if (ptr)
+ {
+ size_t Len = ptr - Lastptr -1;
+
+ if (Len > 8100)
+ return;
+
+ memcpy(TCPMsg, Lastptr, Len);
+ TCPMsg[Len++] = 13;
+ TCPMsg[Len++] = 10;
+ TCPMsg[Len] = 0;
+
+ if (!Check0183CheckSum(TCPMsg, Len))
+ {
+ Debugprintf("Checksum Error %s", TCPMsg);
+ }
+ else
+ {
+ if (memcmp(&TCPMsg[3], "RMC", 3) == 0)
+ DecodeRMC(TCPMsg, Len);
+ }
+ Lastptr = ptr + 1;
+ len -= (int)Len;
+ }
+ else
+ return;
+ }
+ }
+ }
+
+ if (FD_ISSET(TCPSock, &errorfs))
+ {
+Lost:
+#ifdef LINBPQ
+ printf("GPSD Connection lost\n");
+#endif
+ closesocket(TCPSock);
+ GPSDOK = FALSE;;
+ return;
+ }
+ }
+ else
+ {
+ // 15 mins without data. Shouldn't happen
+
+ shutdown(TCPSock, SD_BOTH);
+ Sleep(100);
+
+ closesocket(TCPSock);
+ GPSDOK = FALSE;
+ return;
+ }
+ }
+}
+
+
+
+
+
+// Code Moved from APRS Application
+
+//
+// APRS Mapping and Messaging App for BPQ32 Switch.
+//
+
+
+VOID APIENTRY APRSConnect(char * Call, char * Filter);
+VOID APIENTRY APRSDisconnect();
+BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call);
+BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port);
+BOOL APIENTRY PutAPRSMessage(char * Frame, int Len);
+BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon);
+BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon);
+VOID APIENTRY APISendBeacon();
+
+
+int NewLine(HWND hWnd);
+VOID ProcessBuff(HWND hWnd, MESSAGE * buff,int len,int stamp);
+int TogglePort(HWND hWnd, int Item, int mask);
+VOID SendFrame(UCHAR * buff, int txlen);
+int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
+int KissDecode(UCHAR * inbuff, int len);
+//void UpdateStation(char * Call, char * Path, char * Comment, double V_Lat, double V_Lon, double V_SOG, double V_COG, int iconRow, int iconCol);
+VOID FindStationsByPixel(int MouseX, int MouseY);
+void RefreshStation(struct STATIONRECORD * ptr);
+void RefreshStationList();
+void RefreshStationMap();
+BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station);
+VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station);
+BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y);
+VOID APRSPoll();
+VOID OSMThread();
+VOID ResolveThread();
+VOID RefreshTile(char * FN, int Zoom, int x, int y);
+int ProcessMessage(char * Payload, struct STATIONRECORD * Station);
+VOID APRSSecTimer();
+double myBearing(double laa, double loa);
+
+BOOL CreatePipeThread();
+
+VOID SendWeatherBeacon();
+VOID DecodeWXPortList();
+
+
+VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX)
+{
+ UCHAR * ptr = strchr(WX, '_');
+ char Type;
+ int Val;
+
+ if (ptr == 0)
+ return;
+
+ sockptr->WindDirn = atoi(++ptr);
+ ptr += 4;
+ sockptr->WindSpeed = atoi(ptr);
+ ptr += 3;
+WXLoop:
+
+ Type = *(ptr++);
+
+ if (*ptr =='.') // Missing Value
+ {
+ while (*ptr == '.')
+ ptr++;
+
+ goto WXLoop;
+ }
+
+ Val = atoi(ptr);
+
+ switch (Type)
+ {
+ case 'c': // = wind direction (in degrees).
+
+ sockptr->WindDirn = Val;
+ break;
+
+ case 's': // = sustained one-minute wind speed (in mph).
+
+ sockptr->WindSpeed = Val;
+ break;
+
+ case 'g': // = gust (peak wind speed in mph in the last 5 minutes).
+
+ sockptr->WindGust = Val;
+ break;
+
+ case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99.
+
+ sockptr->Temp = Val;
+ break;
+
+ case 'r': // = rainfall (in hundredths of an inch) in the last hour.
+
+ sockptr->RainLastHour = Val;
+ break;
+
+ case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours.
+
+ sockptr->RainLastDay = Val;
+ break;
+
+ case 'P': // = rainfall (in hundredths of an inch) since midnight.
+
+ sockptr->RainToday = Val;
+ break;
+
+ case 'h': // = humidity (in %. 00 = 100%).
+
+ sockptr->Humidity = Val;
+ break;
+
+ case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal).
+
+ sockptr->Pressure = Val;
+ break;
+
+ default:
+
+ return;
+ }
+ while(isdigit(*ptr))
+ {
+ ptr++;
+ }
+
+ if (*ptr != ' ')
+ goto WXLoop;
+}
+
+static char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n";
+//char Header[] = "Accept: */*\r\nHost: tile.openstreetmap.org\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n";
+
+char APRSMsg[300];
+
+Dll struct STATIONRECORD * APIENTRY APPLFindStation(char * Call, BOOL AddIfNotFount)
+{
+ // Called from APRS Appl
+
+ struct STATIONRECORD * Stn;
+
+ GetSemaphore(&Semaphore, 12);
+ Stn = FindStation(Call, AddIfNotFount) ;
+ FreeSemaphore(&Semaphore);
+
+ return Stn;
+}
+
+Dll struct APRSMESSAGE * APIENTRY APRSGetMessageBuffer()
+{
+ struct APRSMESSAGE * ptr = MessageRecordPool;
+
+ if (ptr == NULL)
+ {
+ // try getting oldest
+
+ ptr = SMEM->Messages;
+
+ if (ptr)
+ {
+ SMEM->Messages = ptr->Next;
+ memset(ptr, 0, sizeof(struct APRSMESSAGE));
+ }
+ return ptr;
+ }
+
+ if (ptr)
+ {
+ MessageRecordPool = ptr->Next; // Unchain
+ MessageCount++;
+
+ ptr->Next = NULL;
+
+ memset(ptr, 0, sizeof(struct APRSMESSAGE));
+ }
+
+ return ptr;
+}
+
+
+struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount)
+{
+ int i = 0;
+ struct STATIONRECORD * find;
+ struct STATIONRECORD * ptr;
+ struct STATIONRECORD * last = NULL;
+ int sum = 0;
+
+ if (APRSActive == 0 || StationRecords == 0)
+ return FALSE;
+
+ if (strlen(Call) > 9)
+ {
+ Debugprintf("APRS Call too long %s", Call);
+ Call[9] = 0;
+ }
+
+ find = *StationRecords;
+ while(find)
+ {
+ if (strlen(find->Callsign) > 9)
+ {
+ Debugprintf("APRS Call in Station List too long %s", find->Callsign);
+ find->Callsign[9] = 0;
+ }
+
+ if (strcmp(find->Callsign, Call) == 0)
+ return find;
+
+ last = find;
+ find = find->Next;
+ i++;
+ }
+
+ // Not found - add on end
+
+ if (AddIfNotFount)
+ {
+ // Get first from station record pool
+
+ ptr = StationRecordPool;
+
+ if (ptr)
+ {
+ StationRecordPool = ptr->Next; // Unchain
+ StationCount++;
+ }
+ else
+ {
+ // Get First from Stations
+
+ ptr = *StationRecords;
+
+ if (ptr)
+ *StationRecords = ptr->Next;
+ }
+
+ if (ptr == NULL)
+ return NULL;
+
+ memset(ptr, 0, sizeof(struct STATIONRECORD));
+
+// EnterCriticalSection(&Crit);
+
+ if (*StationRecords == NULL)
+ *StationRecords = ptr;
+ else
+ last->Next = ptr;
+
+// LeaveCriticalSection(&Crit);
+
+ // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount);
+
+ strcpy(ptr->Callsign, Call);
+ ptr->TimeLastUpdated = ptr->TimeAdded = time(NULL);
+ ptr->Index = i;
+ ptr->NoTracks = DefaultNoTracks;
+
+ for (i = 0; i < 9; i++)
+ sum += Call[i];
+
+ sum %= 20;
+
+ ptr->TrackColour = sum;
+ ptr->Moved = TRUE;
+
+ return ptr;
+ }
+ else
+ return NULL;
+}
+
+struct STATIONRECORD * ProcessRFFrame(char * Msg, int len, int * ourMessage)
+{
+ char * Payload;
+ char * Path = NULL;
+ char * Comment = NULL;
+ char * Callsign;
+ char * ptr;
+ int Port = 0;
+
+ struct STATIONRECORD * Station = NULL;
+
+ Msg[len - 1] = 0;
+
+// Debugprintf("RF Frame %s", Msg);
+
+ Msg += 10; // Skip Timestamp
+
+ Payload = strchr(Msg, ':'); // End of Address String
+
+ if (Payload == NULL)
+ {
+ Debugprintf("Invalid Msg %s", Msg);
+ return Station;
+ }
+
+ ptr = strstr(Msg, "Port=");
+
+ if (ptr)
+ Port = atoi(&ptr[5]);
+
+ Payload++;
+
+ if (*Payload != 0x0d)
+ return Station;
+
+ *Payload++ = 0;
+
+ Callsign = Msg;
+
+ Path = strchr(Msg, '>');
+
+ if (Path == NULL)
+ {
+ Debugprintf("Invalid Header %s", Msg);
+ return Station;
+ }
+
+ *Path++ = 0;
+
+ ptr = strchr(Path, ' ');
+
+ if (ptr)
+ *ptr = 0;
+
+ // Look up station - create a new one if not found
+
+ if (strcmp(Callsign, "AIS") == 0)
+ {
+ if (needAIS)
+ {
+ Payload += 3;
+ ProcessAISMessage(Payload, strlen(Payload));
+ }
+ else
+ Debugprintf(Payload);
+
+ return 0;
+ }
+
+ Station = FindStation(Callsign, TRUE);
+
+ strcpy(Station->Path, Path);
+ strcpy(Station->LastPacket, Payload);
+ Station->LastPort = Port;
+
+ *ourMessage = DecodeAPRSPayload(Payload, Station);
+ Station->TimeLastUpdated = time(NULL);
+
+ return Station;
+}
+
+
+/*
+2E0AYY>APU25N,TCPIP*,qAC,AHUBSWE2:=5105.18N/00108.19E-Paul in Folkestone Kent {UIV32N}
+G0AVP-12>APT310,MB7UC*,WIDE3-2,qAR,G3PWJ:!5047.19N\00108.45Wk074/000/Paul mobile
+G0CJM-12>CQ,TCPIP*,qAC,AHUBSWE2:=/3&Rio94sg
+M0HFC>APRS,WIDE2-1,qAR,G0MNI:!5342.83N/00013.79W# Humber Fortress ARC Look us up on QRZ
+G8WVW-3>APTT4,WIDE1-1,WIDE2-1,qAS,G8WVW:T#063,123,036,000,000,000,00000000
+*/
+
+
+struct STATIONRECORD * DecodeAPRSISMsg(char * Msg)
+{
+ char * Payload;
+ char * Path = NULL;
+ char * Comment = NULL;
+ char * Callsign;
+ struct STATIONRECORD * Station = NULL;
+
+// Debugprintf(Msg);
+
+ Payload = strchr(Msg, ':'); // End of Address String
+
+ if (Payload == NULL)
+ {
+ Debugprintf("Invalid Msg %s", Msg);
+ return Station;
+ }
+
+ *Payload++ = 0;
+
+ Callsign = Msg;
+
+ Path = strchr(Msg, '>');
+
+ if (Path == NULL)
+ {
+ Debugprintf("Invalid Msg %s", Msg);
+ return Station;
+ }
+
+ *Path++ = 0;
+
+ // Look up station - create a new one if not found
+
+ if (strlen(Callsign) > 11)
+ {
+ Debugprintf("Invalid Msg %s", Msg);
+ return Station;
+ }
+
+ Station = FindStation(Callsign, TRUE);
+
+ strcpy(Station->Path, Path);
+ strcpy(Station->LastPacket, Payload);
+ Station->LastPort = 0;
+
+ DecodeAPRSPayload(Payload, Station);
+ Station->TimeLastUpdated = time(NULL);
+
+ return Station;
+}
+
+double Cube91 = 91.0 * 91.0 * 91.0;
+double Square91 = 91.0 * 91.0;
+
+BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station)
+{
+ UCHAR SymChar;
+ char SymSet;
+ char NS;
+ char EW;
+ double NewLat, NewLon;
+ char LatDeg[3], LonDeg[4];
+ char save;
+
+ // Compressed has first character not a digit (it is symbol table)
+
+ // /YYYYXXXX$csT
+
+ if (Payload[0] == '!')
+ return FALSE; // Ultimeter 2000 Weather Station
+
+ if (!isdigit(*Payload))
+ {
+ int C, S;
+
+ SymSet = *Payload;
+ SymChar = Payload[9];
+
+ NewLat = 90.0 - ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 +
+ (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 380926.0;
+
+ Payload += 4;
+
+ NewLon = -180.0 + ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 +
+ (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 190463.0;
+
+ C = Payload[6] - 33;
+
+ if (C >= 0 && C < 90 )
+ {
+ S = Payload[7] - 33;
+
+ Station->Course = C * 4;
+ Station->Speed = (pow(1.08, S) - 1) * 1.15077945; // MPH;
+ }
+
+
+
+ }
+ else
+ {
+ // Standard format ddmm.mmN/dddmm.mmE?
+
+ NS = Payload[7] & 0xdf; // Mask Lower Case Bit
+ EW = Payload[17] & 0xdf;
+
+ SymSet = Payload[8];
+ SymChar = Payload[18];
+
+ if (SymChar == '_') // WX
+ {
+ if (strlen(Payload) > 30)
+ strcpy(Station->LastWXPacket, Payload);
+ }
+
+ memcpy(LatDeg, Payload,2);
+ LatDeg[2]=0;
+ NewLat = atof(LatDeg) + (atof(Payload+2) / 60);
+
+ if (NS == 'S')
+ NewLat = -NewLat;
+ else
+ if (NS != 'N')
+ return FALSE;
+
+ memcpy(LonDeg,Payload + 9, 3);
+
+ if (SymChar != '_' && Payload[22] == '/') // not if WX
+ {
+ Station->Course = atoi(Payload + 19);
+ Station->Speed = atoi(Payload + 23);
+ }
+
+ LonDeg[3]=0;
+
+ save = Payload[17];
+ Payload[17] = 0;
+ NewLon = atof(LonDeg) + (atof(Payload+12) / 60);
+ Payload[17] = save;
+
+ if (EW == 'W')
+ NewLon = -NewLon;
+ else
+ if (EW != 'E')
+ return FALSE;
+ }
+
+ Station->Symbol = SymChar;
+
+ if (SymChar > ' ' && SymChar < 0x7f)
+ SymChar -= '!';
+ else
+ SymChar = 0;
+
+ Station->IconOverlay = 0;
+
+ if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z'))
+ {
+ SymChar += 96;
+ Station->IconOverlay = SymSet;
+ }
+ else
+ if (SymSet == '\\')
+ SymChar += 96;
+
+ Station->iconRow = SymChar >> 4;
+ Station->iconCol = SymChar & 15;
+
+ if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180)
+ return TRUE;
+
+ if (Station->Lat != NewLat || Station->Lon != NewLon)
+ {
+ time_t NOW = time(NULL);
+ time_t Age = NOW - Station->TimeLastTracked;
+
+ if (Age > 15) // Don't update too often
+ {
+ // Add to track
+
+ Station->TimeLastTracked = NOW;
+
+// if (memcmp(Station->Callsign, "ISS ", 4) == 0)
+// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket);
+
+ Station->LatTrack[Station->Trackptr] = NewLat;
+ Station->LonTrack[Station->Trackptr] = NewLon;
+ Station->TrackTime[Station->Trackptr] = NOW;
+
+ Station->Trackptr++;
+ Station->Moved = TRUE;
+
+ if (Station->Trackptr == TRACKPOINTS)
+ Station->Trackptr = 0;
+ }
+
+ Station->Lat = NewLat;
+ Station->Lon = NewLon;
+ Station->Approx = 0;
+ }
+
+
+ return TRUE;
+}
+
+int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station)
+{
+ char * TimeStamp;
+ char * ObjName;
+ char ObjState;
+ struct STATIONRECORD * Object;
+ BOOL Item = FALSE;
+ char * ptr;
+ char * Callsign;
+ char * Path;
+ char * Msg;
+ char * context;
+ struct STATIONRECORD * TPStation;
+
+ Station->Object = NULL;
+
+ if (strcmp(Station->Callsign, "LA1ZDA-2") == 0)
+ {
+ int i = 1;
+ }
+ switch(*Payload)
+ {
+ case '`':
+ case 0x27: // '
+ case 0x1c:
+ case 0x1d: // MIC-E
+
+ Decode_MIC_E_Packet(Payload, Station);
+ return 0;
+
+ case '$': // NMEA
+ Debugprintf(Payload);
+ break;
+
+ case ')': // Item
+
+// Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload);
+
+ Item = TRUE;
+ ObjName = ptr = Payload + 1;
+
+ while (TRUE)
+ {
+ ObjState = *ptr;
+ if (ObjState == 0)
+ return 0; // Corrupt
+
+ if (ObjState == '!' || ObjState == '_') // Item Terminator
+ break;
+
+ ptr++;
+ }
+
+ *ptr = 0; // Terminate Name
+
+ Object = FindStation(ObjName, TRUE);
+ Object->ObjState = *ptr++ = ObjState;
+
+ strcpy(Object->Path, Station->Callsign);
+ strcat(Object->Path, ">");
+ if (Object == Station)
+ {
+ char Temp[256];
+ strcpy(Temp, Station->Path);
+ strcat(Object->Path, Temp);
+ Debugprintf("item is station %s", Payload);
+ }
+ else
+ strcat(Object->Path, Station->Path);
+
+ strcpy(Object->LastPacket, Payload);
+
+ if (ObjState != '_') // Deleted Objects may have odd positions
+ DecodeLocationString(ptr, Object);
+
+ Object->TimeLastUpdated = time(NULL);
+ Station->Object = Object;
+ return 0;
+
+
+ case ';': // Object
+
+ ObjName = Payload + 1;
+ ObjState = Payload[10]; // * Live, _Killed
+
+ Payload[10] = 0;
+ Object = FindStation(ObjName, TRUE);
+ Object->ObjState = Payload[10] = ObjState;
+
+ strcpy(Object->Path, Station->Callsign);
+ strcat(Object->Path, ">");
+ if (Object == Station)
+ {
+ char Temp[256];
+ strcpy(Temp, Station->Path);
+ strcat(Object->Path, Temp);
+ Debugprintf("Object is station %s", Payload);
+ }
+ else
+ strcat(Object->Path, Station->Path);
+
+
+ strcpy(Object->LastPacket, Payload);
+
+ TimeStamp = Payload + 11;
+
+ if (ObjState != '_') // Deleted Objects may have odd positions
+ DecodeLocationString(Payload + 18, Object);
+
+ Object->TimeLastUpdated = time(NULL);
+ Object->LastPort = Station->LastPort;
+ Station->Object = Object;
+ return 0;
+
+ case '@':
+ case '/': // Timestamp, No Messaging
+
+ TimeStamp = ++Payload;
+ Payload += 6;
+
+ case '=':
+ case '!':
+
+ Payload++;
+
+ DecodeLocationString(Payload, Station);
+
+ return 0;
+
+ case '>': // Status
+
+ strcpy(Station->Status, &Payload[1]);
+
+ case '<': // Capabilities
+ case '_': // Weather
+ case 'T': // Telemetry
+
+ break;
+
+ case ':': // Message
+
+ return ProcessMessage(Payload, Station);
+
+ case '}': // Third Party Header
+
+ // Process Payload as a new message
+
+ // }GM7HHB-9>APDR12,TCPIP,MM1AVR*:=5556.62N/00303.55W>204/000/A=000213 http://www.dstartv.com
+
+ Callsign = Msg = &Payload[1];
+ Path = strchr(Msg, '>');
+
+ if (Path == NULL)
+ return 0;
+
+ *Path++ = 0;
+
+ Payload = strchr(Path, ':');
+
+ if (Payload == NULL)
+ return 0;
+
+ *(Payload++) = 0;
+
+ // Check Dup Filter
+
+ if (CheckforDups(Callsign, Payload, (int)strlen(Payload)))
+ return 0;
+
+ // Look up station - create a new one if not found
+
+ TPStation = FindStation(Callsign, TRUE);
+
+ strcpy(TPStation->Path, Path);
+ strcpy(TPStation->LastPacket, Payload);
+ TPStation->LastPort = 0; // Heard on RF, but info is from IS
+
+ DecodeAPRSPayload(Payload, TPStation);
+ TPStation->TimeLastUpdated = time(NULL);
+
+ return 0;
+
+ default:
+
+ // Non - APRS Message. If Payload contains a valid 6 char locator derive a position from it
+
+ if (Station->Lat != 0.0 || Station->Lon != 0.0)
+ return 0; // already have position
+
+ ptr = strtok_s(Payload, ",[](){} \n", &context);
+
+ while (ptr && ptr[0])
+ {
+ if (strlen(ptr) == 6) // could be locator
+ {
+ double Lat = 0.0, Lon = 0.0;
+
+ if (FromLOC(ptr, &Lat, &Lon))
+ {
+ if (Lat != 0.0 && Lon != 0.0)
+ {
+ // Randomise in locator square.
+
+ Lat = Lat + ((rand() / 24.0) / RAND_MAX);
+ Lon = Lon + ((rand() / 12.0) / RAND_MAX);
+ Station->Lat = Lat;
+ Station->Lon = Lon;
+ Station->Approx = 1;
+ Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload);
+ }
+ }
+ }
+
+ ptr = strtok_s(NULL, ",[](){} \n", &context);
+ }
+
+ return 0;
+ }
+ return 0;
+}
+
+// Convert MIC-E Char to Lat Digit (offset by 0x30)
+// 0123456789 @ABCDEFGHIJKLMNOPQRSTUVWXYZ
+char MicELat[] = "0123456789???????0123456789 ???0123456789 " ;
+
+char MicECode[]= "0000000000???????111111111110???22222222222" ;
+
+
+VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station)
+{
+ // Info is encoded in the Dest Addr (in Station->Path) as well as Payload.
+ // See APRS Spec for full details
+
+ char Lat[10]; // DDMMHH
+ char LatDeg[3];
+ char * ptr;
+ char c;
+ int i, n;
+ int LonDeg, LonMin;
+ BOOL LonOffset = FALSE;
+ char NS = 'S';
+ char EW = 'E';
+ UCHAR SymChar, SymSet;
+ double NewLat, NewLon;
+ int SP, DC, SE; // Course/Speed Encoded
+ int Course, Speed;
+
+ // Make sure packet is long enough to have an valid address
+
+ if (strlen(Payload) < 9)
+ return;
+
+ ptr = &Station->Path[0];
+
+ for (i = 0; i < 6; i++)
+ {
+ n = (*(ptr++)) - 0x30;
+ c = MicELat[n];
+
+ if (c == '?') // Illegal
+ return;
+
+ if (c == ' ')
+ c = '0'; // Limited Precision
+
+ Lat[i] = c;
+
+ }
+
+ Lat[6] = 0;
+
+ if (Station->Path[3] > 'O')
+ NS = 'N';
+
+ if (Station->Path[5] > 'O')
+ EW = 'W';
+
+ if (Station->Path[4] > 'O')
+ LonOffset = TRUE;
+
+ n = Payload[1] - 28; // Lon Degrees S9PU0T,WIDE1-1,WIDE2-2,qAR,WB9TLH-15:`rB0oII>/]"6W}44
+
+ if (LonOffset)
+ n += 100;
+
+ if (n > 179 && n < 190)
+ n -= 80;
+ else
+ if (n > 189 && n < 200)
+ n -= 190;
+
+ LonDeg = n;
+
+/*
+ To decode the longitude degrees value:
+1. subtract 28 from the d+28 value to obtain d.
+2. if the longitude offset is +100 degrees, add 100 to d.
+3. subtract 80 if 180 ˜ d ˜ 189
+(i.e. the longitude is in the range 100–109 degrees).
+4. or, subtract 190 if 190 ˜ d ˜ 199.
+(i.e. the longitude is in the range 0–9 degrees).
+*/
+
+ n = Payload[2] - 28; // Lon Mins
+
+ if (n > 59)
+ n -= 60;
+
+ LonMin = n;
+
+ n = Payload[3] - 28; // Lon Mins/100;
+
+//1. subtract 28 from the m+28 value to obtain m.
+//2. subtract 60 if m ™ 60.
+//(i.e. the longitude minutes is in the range 0–9).
+
+
+ memcpy(LatDeg, Lat, 2);
+ LatDeg[2]=0;
+
+ NewLat = atof(LatDeg) + (atof(Lat+2) / 6000.0);
+
+ if (NS == 'S')
+ NewLat = -NewLat;
+
+ NewLon = LonDeg + LonMin / 60.0 + n / 6000.0;
+
+ if (EW == 'W') // West
+ NewLon = -NewLon;
+
+
+ SP = Payload[4] - 28;
+ DC = Payload[5] - 28;
+ SE = Payload[6] - 28; // Course 100 and 10 degs
+
+ Speed = DC / 10; // Quotient = Speed Units
+ Course = DC - (Speed * 10); // Remainder = Course Deg/100
+
+ Course = SE + (Course * 100);
+
+ Speed += SP * 10;
+
+ if (Speed >= 800)
+ Speed -= 800;
+
+ if (Course >= 400)
+ Course -= 400;
+
+ Station->Course = Course;
+ Station->Speed = Speed * 1.15077945; // MPH
+
+// Debugprintf("MIC-E Course/Speed %s %d %d", Station->Callsign, Course, Speed);
+
+ SymChar = Payload[7]; // Symbol
+ SymSet = Payload[8]; // Symbol
+
+ SymChar -= '!';
+
+ Station->IconOverlay = 0;
+
+ if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z'))
+ {
+ SymChar += 96;
+ Station->IconOverlay = SymSet;
+ }
+ else
+ if (SymSet == '\\')
+ SymChar += 96;
+
+ Station->iconRow = SymChar >> 4;
+ Station->iconCol = SymChar & 15;
+
+ if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180)
+ return;
+
+ if (Station->Lat != NewLat || Station->Lon != NewLon)
+ {
+ time_t NOW = time(NULL);
+ time_t Age = NOW - Station->TimeLastUpdated;
+
+ if (Age > 15) // Don't update too often
+ {
+ // Add to track
+
+// if (memcmp(Station->Callsign, "ISS ", 4) == 0)
+// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket);
+
+ Station->LatTrack[Station->Trackptr] = NewLat;
+ Station->LonTrack[Station->Trackptr] = NewLon;
+ Station->TrackTime[Station->Trackptr] = NOW;
+
+ Station->Trackptr++;
+ Station->Moved = TRUE;
+
+ if (Station->Trackptr == TRACKPOINTS)
+ Station->Trackptr = 0;
+ }
+
+ Station->Lat = NewLat;
+ Station->Lon = NewLon;
+ Station->Approx = 0;
+ }
+
+ return;
+
+}
+
+/*
+
+INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+// This processes messages from controls on the tab subpages
+ int Command;
+
+// int retCode, disp;
+// char Key[80];
+// HKEY hKey;
+// BOOL OK;
+// OPENFILENAME ofn;
+// char Digis[100];
+
+ int Port = PortNum[CurrentPage];
+
+ switch (message)
+ {
+ case WM_NOTIFY:
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case TCN_SELCHANGE:
+ OnSelChanged(hDlg);
+ return TRUE;
+ // More cases on WM_NOTIFY switch.
+ case NM_CHAR:
+ return TRUE;
+ }
+
+ break;
+ case WM_INITDIALOG:
+ OnChildDialogInit( hDlg);
+ return (INT_PTR)TRUE;
+
+ case WM_CTLCOLORDLG:
+
+ return (LONG)bgBrush;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ HDC hdcStatic = (HDC)wParam;
+ SetTextColor(hdcStatic, RGB(0, 0, 0));
+ SetBkMode(hdcStatic, TRANSPARENT);
+ return (LONG)bgBrush;
+ }
+
+
+ case WM_COMMAND:
+
+ Command = LOWORD(wParam);
+
+ if (Command == 2002)
+ return TRUE;
+
+ switch (Command)
+ {
+/* case IDC_FILE:
+
+ memset(&ofn, 0, sizeof (OPENFILENAME));
+ ofn.lStructSize = sizeof (OPENFILENAME);
+ ofn.hwndOwner = hDlg;
+ ofn.lpstrFile = &FN[Port][0];
+ ofn.nMaxFile = 250;
+ ofn.lpstrTitle = "File to send as beacon";
+ ofn.lpstrInitialDir = GetBPQDirectory();
+
+ if (GetOpenFileName(&ofn))
+ SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]);
+
+ break;
+
+
+ case IDOK:
+
+ GetDlgItemText(hDlg, IDC_UIDEST, &UIDEST[Port][0], 10);
+
+ if (UIDigi[Port])
+ {
+ free(UIDigi[Port]);
+ UIDigi[Port] = NULL;
+ }
+
+ if (UIDigiAX[Port])
+ {
+ free(UIDigiAX[Port]);
+ UIDigiAX[Port] = NULL;
+ }
+
+ GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99);
+
+ UIDigi[Port] = _strdup(Digis);
+
+ GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255);
+ GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000);
+
+ Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE);
+
+ MinCounter[Port] = Interval[Port];
+
+ SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE);
+
+ sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]);
+
+ retCode = RegCreateKeyEx(REGTREE,
+ Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp);
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIDEST[Port][0], strlen(&UIDEST[Port][0]));
+ retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0]));
+ retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0]));
+ retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4);
+ retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4);
+ retCode = RegSetValueEx(hKey, "Enabled", 0, REG_DWORD,(BYTE *)&UIEnabled[Port], 4);
+ retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, strlen(Digis));
+
+ RegCloseKey(hKey);
+ }
+
+ SetupUI(Port);
+
+ return (INT_PTR)TRUE;
+
+
+ case IDCANCEL:
+
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+
+ case ID_TEST:
+
+ SendBeacon(Port);
+ return TRUE;
+
+
+
+
+ }
+ break;
+
+ }
+ return (INT_PTR)FALSE;
+}
+
+
+
+
+VOID WINAPI OnTabbedDialogInit(HWND hDlg)
+{
+ DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR));
+ DWORD dwDlgBase = GetDialogBaseUnits();
+ int cxMargin = LOWORD(dwDlgBase) / 4;
+ int cyMargin = HIWORD(dwDlgBase) / 8;
+
+ TC_ITEM tie;
+ RECT rcTab;
+
+ int i, pos, tab = 0;
+ INITCOMMONCONTROLSEX init;
+
+ char PortNo[60];
+ struct _EXTPORTDATA * PORTVEC;
+
+ hwndDlg = hDlg; // Save Window Handle
+
+ // Save a pointer to the DLGHDR structure.
+
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr);
+
+ // Create the tab control.
+
+
+ init.dwICC = ICC_STANDARD_CLASSES;
+ init.dwSize=sizeof(init);
+ i=InitCommonControlsEx(&init);
+
+ pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 0, 0, 100, 100, hwndDlg, NULL, hInst, NULL);
+
+ if (pHdr->hwndTab == NULL) {
+
+ // handle error
+
+ }
+
+ // Add a tab for each of the child dialog boxes.
+
+ tie.mask = TCIF_TEXT | TCIF_IMAGE;
+
+ tie.iImage = -1;
+
+ for (i = 1; i <= GetNumberofPorts(); i++)
+ {
+ // Only allow UI on ax.25 ports
+
+ PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntry(i);
+
+ if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL
+ if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR
+ continue;
+
+ sprintf(PortNo, "Port %2d", GetPortNumber(i));
+ PortNum[tab] = GetPortNumber(i);
+
+ tie.pszText = PortNo;
+ TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie);
+
+ pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE");
+
+ }
+
+ PageCount = tab;
+
+ // Determine the bounding rectangle for all child dialog boxes.
+
+ SetRectEmpty(&rcTab);
+
+ for (i = 0; i < PageCount; i++)
+ {
+ if (pHdr->apRes[i]->cx > rcTab.right)
+ rcTab.right = pHdr->apRes[i]->cx;
+
+ if (pHdr->apRes[i]->cy > rcTab.bottom)
+ rcTab.bottom = pHdr->apRes[i]->cy;
+
+ }
+
+ MapDialogRect(hwndDlg, &rcTab);
+
+// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4;
+
+// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8;
+
+ // Calculate how large to make the tab control, so
+
+ // the display area can accomodate all the child dialog boxes.
+
+ TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);
+
+ OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top);
+
+ // Calculate the display rectangle.
+
+ CopyRect(&pHdr->rcDisplay, &rcTab);
+
+ TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay);
+
+ // Set the size and position of the tab control, buttons,
+
+ // and dialog box.
+
+ SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER);
+
+ // Move the Buttons to bottom of page
+
+ pos=rcTab.left+cxMargin;
+
+
+ // Size the dialog box.
+
+ SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME),
+ rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION),
+ SWP_NOMOVE | SWP_NOZORDER);
+
+ // Simulate selection of the first item.
+
+ OnSelChanged(hwndDlg);
+
+}
+
+// DoLockDlgRes - loads and locks a dialog template resource.
+
+// Returns a pointer to the locked resource.
+
+// lpszResName - name of the resource
+
+DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName)
+{
+ HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);
+ HGLOBAL hglb = LoadResource(hInst, hrsrc);
+
+ return (DLGTEMPLATE *) LockResource(hglb);
+}
+
+//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page.
+
+// OnSelChanged - processes the TCN_SELCHANGE notification.
+
+// hwndDlg - handle of the parent dialog box
+
+VOID WINAPI OnSelChanged(HWND hwndDlg)
+{
+ char PortDesc[40];
+ int Port;
+
+ DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab);
+
+ // Destroy the current child dialog box, if any.
+
+ if (pHdr->hwndDisplay != NULL)
+
+ DestroyWindow(pHdr->hwndDisplay);
+
+ // Create the new child dialog box.
+
+ pHdr->hwndDisplay = CreateDialogIndirect(hInst, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc);
+
+ hwndDisplay = pHdr->hwndDisplay; // Save
+
+ Port = PortNum[CurrentPage];
+ // Fill in the controls
+
+ GetPortDescription(PortNum[CurrentPage], PortDesc);
+
+ SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc);
+
+// CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]);
+
+// SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE);
+
+ SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIDEST[Port][0]);
+ SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIDigi[Port]);
+
+
+
+// SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]);
+// SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]);
+
+ ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL);
+
+}
+
+//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area.
+
+// OnChildDialogInit - Positions the child dialog box to fall
+
+// within the display area of the tab control.
+
+VOID WINAPI OnChildDialogInit(HWND hwndDlg)
+{
+ HWND hwndParent = GetParent(hwndDlg);
+ DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA);
+
+ SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE);
+}
+
+
+*/
+
+
+/*
+VOID ProcessMessage(char * Payload, struct STATIONRECORD * Station)
+{
+ char MsgDest[10];
+ struct APRSMESSAGE * Message;
+ struct APRSMESSAGE * ptr = Messages;
+ char * TextPtr = &Payload[11];
+ char * SeqPtr;
+ int n = 0;
+ char FromCall[10] = " ";
+ struct tm * TM;
+ time_t NOW;
+
+ memcpy(FromCall, Station->Callsign, (int)strlen(Station->Callsign));
+ memcpy(MsgDest, &Payload[1], 9);
+ MsgDest[9] = 0;
+
+ SeqPtr = strchr(TextPtr, '{');
+
+ if (SeqPtr)
+ {
+ *(SeqPtr++) = 0;
+ if(strlen(SeqPtr) > 6)
+ SeqPtr[7] = 0;
+ }
+
+ if (_memicmp(TextPtr, "ack", 3) == 0)
+ {
+ // Message Ack. See if for one of our messages
+
+ ptr = OutstandingMsgs;
+
+ if (ptr == 0)
+ return;
+
+ do
+ {
+ if (strcmp(ptr->FromCall, MsgDest) == 0
+ && strcmp(ptr->ToCall, FromCall) == 0
+ && strcmp(ptr->Seq, &TextPtr[3]) == 0)
+ {
+ // Message is acked
+
+ ptr->Retries = 0;
+ ptr->Acked = TRUE;
+// if (hMsgsOut)
+// UpdateTXMessageLine(hMsgsOut, n, ptr);
+
+ return;
+ }
+ ptr = ptr->Next;
+ n++;
+
+ } while (ptr);
+
+ return;
+ }
+
+ Message = malloc(sizeof(struct APRSMESSAGE));
+ memset(Message, 0, sizeof(struct APRSMESSAGE));
+ strcpy(Message->FromCall, Station->Callsign);
+ strcpy(Message->ToCall, MsgDest);
+
+ if (SeqPtr)
+ {
+ strcpy(Message->Seq, SeqPtr);
+
+ // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message
+
+ if (SeqPtr[2] == '}')
+ {
+ struct APRSMESSAGE * ptr1;
+ int nn = 0;
+
+ strcpy(Station->LastRXSeq, SeqPtr);
+
+ ptr1 = OutstandingMsgs;
+
+ while (ptr1)
+ {
+ if (strcmp(ptr1->FromCall, MsgDest) == 0
+ && strcmp(ptr1->ToCall, FromCall) == 0
+ && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0)
+ {
+ // Message is acked
+
+ ptr1->Acked = TRUE;
+ ptr1->Retries = 0;
+// if (hMsgsOut)
+// UpdateTXMessageLine(hMsgsOut, nn, ptr);
+
+ break;
+ }
+ ptr1 = ptr1->Next;
+ nn++;
+ }
+ }
+ else
+ {
+ // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanger
+
+ Station->SimpleNumericSeq = TRUE;
+ }
+ }
+
+ if (strlen(TextPtr) > 100)
+ TextPtr[100] = 0;
+
+ strcpy(Message->Text, TextPtr);
+
+ NOW = time(NULL);
+
+ if (DefaultLocalTime)
+ TM = localtime(&NOW);
+ else
+ TM = gmtime(&NOW);
+
+ sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min);
+
+ if (_stricmp(MsgDest, APRSCall) == 0 && SeqPtr) // ack it if it has a sequence
+ {
+ // For us - send an Ack
+
+ char ack[30];
+
+ int n = sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq);
+ PutAPRSMessage(ack, n);
+ }
+
+ if (ptr == NULL)
+ {
+ Messages = Message;
+ }
+ else
+ {
+ n++;
+ while(ptr->Next)
+ {
+ ptr = ptr->Next;
+ n++;
+ }
+ ptr->Next = Message;
+ }
+
+ if (strcmp(MsgDest, APRSCall) == 0) // to me?
+ {
+ }
+}
+
+*/
+
+VOID APRSSecTimer()
+{
+
+ // Check Message Retries
+
+ struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs;
+ int n = 0;
+
+ if (SendWX)
+ SendWeatherBeacon();
+
+
+ while (ptr)
+ {
+ if (ptr->Acked == FALSE)
+ {
+ if (ptr->Retries)
+ {
+ ptr->RetryTimer--;
+
+ if (ptr->RetryTimer == 0)
+ {
+ ptr->Retries--;
+
+ if (ptr->Retries)
+ {
+ // Send Again
+
+ char Msg[255];
+ APRSHEARDRECORD * STN;
+
+ sprintf(Msg, ":%-9s:%s{%s", ptr->ToCall, ptr->Text, ptr->Seq);
+
+ STN = FindStationInMH(ptr->ToCall);
+
+ if (STN)
+ SendAPRSMessage(Msg, STN->rfPort);
+ else
+ {
+ if (memcmp(ptr->ToCall, "SERVER ", 9))
+ SendAPRSMessage(Msg, -1); // All RF ports unless to SERVER
+ SendAPRSMessage(Msg, 0); // IS
+ }
+ ptr->RetryTimer = RetryTimer;
+ }
+ }
+ }
+ }
+
+ ptr = ptr->Next;
+ n++;
+ }
+}
+
+double radians(double Degrees)
+{
+ return M_PI * Degrees / 180;
+}
+double degrees(double Radians)
+{
+ return Radians * 180 / M_PI;
+}
+
+double Distance(double laa, double loa, double lah, double loh, BOOL KM)
+{
+
+/*
+
+'Great Circle Calculations.
+
+'dif = longitute home - longitute away
+
+
+' (this should be within -180 to +180 degrees)
+' (Hint: This number should be non-zero, programs should check for
+' this and make dif=0.0001 as a minimum)
+'lah = latitude of home
+'laa = latitude of away
+
+'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif))
+'distance = dis / 180 * pi * ERAD
+'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis)))
+
+'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians
+*/
+
+ loh = radians(loh); lah = radians(lah);
+ loa = radians(loa); laa = radians(laa);
+
+ loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945;
+
+ if (KM)
+ loh *= 1.60934;
+
+ return loh;
+}
+
+
+double myDistance(double laa, double loa, BOOL KM)
+{
+ double lah, loh;
+
+ GetAPRSLatLon(&lah, &loh);
+
+ return Distance(laa, loa, lah, loh, KM);
+}
+
+/*
+
+'Great Circle Calculations.
+
+'dif = longitute home - longitute away
+
+
+' (this should be within -180 to +180 degrees)
+' (Hint: This number should be non-zero, programs should check for
+' this and make dif=0.0001 as a minimum)
+'lah = latitude of home
+'laa = latitude of away
+
+'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif))
+'distance = dis / 180 * pi * ERAD
+'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis)))
+
+'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians
+
+
+ loh = radians(loh); lah = radians(lah);
+ loa = radians(loa); laa = radians(laa);
+
+ loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945;
+
+ if (KM)
+ loh *= 1.60934;
+
+ return loh;
+}
+*/
+
+double Bearing(double lat2, double lon2, double lat1, double lon1)
+{
+ double dlat, dlon, TC1;
+
+ lat1 = radians(lat1);
+ lat2 = radians(lat2);
+ lon1 = radians(lon1);
+ lon2 = radians(lon2);
+
+ dlat = lat2 - lat1;
+ dlon = lon2 - lon1;
+
+ if (dlat == 0 || dlon == 0) return 0;
+
+ TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2)));
+ TC1 = degrees(TC1);
+
+ if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270;
+
+ if (dlat > 0)
+ {
+ if (dlon > 0) return -TC1;
+ if (dlon < 0) return 360 - TC1;
+ return 0;
+ }
+
+ if (dlat < 0)
+ {
+ if (dlon > 0) return TC1 = 180 - TC1;
+ if (dlon < 0) return TC1 = 180 - TC1; // 'ok?
+ return 180;
+ }
+
+ return 0;
+}
+
+
+double myBearing(double lat2, double lon2)
+{
+ double lat1, lon1;
+
+ GetAPRSLatLon(&lat1, &lon1);
+
+ return Bearing(lat2, lon2, lat1, lon1);
+}
+/*
+
+
+
+
+ lat1 = radians(lat1);
+ lat2 = radians(lat2);
+ lon1 = radians(lon1);
+ lon2 = radians(lon2);
+
+ dlat = lat2 - lat1;
+ dlon = lon2 - lon1;
+
+ if (dlat == 0 || dlon == 0) return 0;
+
+ TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2)));
+ TC1 = degrees(TC1);
+
+ if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270;
+
+ if (dlat > 0)
+ {
+ if (dlon > 0) return -TC1;
+ if (dlon < 0) return 360 - TC1;
+ return 0;
+ }
+
+ if (dlat < 0)
+ {
+ if (dlon > 0) return TC1 = 180 - TC1;
+ if (dlon < 0) return TC1 = 180 - TC1; // 'ok?
+ return 180;
+ }
+
+ return 0;
+}
+*/
+
+// Weather Data
+
+static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+VOID SendWeatherBeacon()
+{
+ char Msg[256];
+ char DD[3]="";
+ char HH[3]="";
+ char MM[3]="";
+ char Lat[10], Lon[10];
+ size_t Len;
+ int index;
+ char WXMessage[1024];
+ char * WXptr;
+ char * WXend;
+ time_t WXTime;
+ time_t now = time(NULL);
+ FILE * hFile;
+ struct tm * TM;
+ struct stat STAT;
+
+ WXCounter++;
+
+ if (WXCounter < WXInterval * 60)
+ return;
+
+ WXCounter = 0;
+
+// Debugprintf("BPQAPRS - Trying to open WX file %s", WXFileName);
+
+ if (stat(WXFileName, &STAT))
+ {
+ Debugprintf("APRS WX File %s stat() failed %d", WXFileName, GetLastError());
+ return;
+ }
+
+ WXTime = (now - STAT.st_mtime) /60; // Minutes
+
+ if (WXTime > (3 * WXInterval))
+ {
+ Debugprintf("APRS Send WX File %s too old - %d minutes", WXFileName, WXTime);
+ return;
+ }
+
+ hFile = fopen(WXFileName, "rb");
+
+ if (hFile)
+ Len = fread(WXMessage, 1, 1024, hFile);
+ else
+ {
+ Debugprintf("APRS WX File %s open() failed %d", WXFileName, GetLastError());
+ return;
+ }
+
+
+ if (Len < 30)
+ {
+ Debugprintf("BPQAPRS - WX file %s is too short - %d Chars", WXFileName, Len);
+ fclose(hFile);
+ return;
+ }
+
+ WXMessage[Len] = 0;
+
+ // see if wview format
+
+//04-09-13, 2245
+//TempIn 23
+//TempEx 18
+//WindHi 0
+//WindAv 0
+//WindDr 200
+//BarmPs 30167
+//HumdIn 56
+//HumdEx 100
+//RnFall 0.00
+//DailyRnFall 0.00
+
+ if (strstr(WXMessage, "TempIn"))
+ {
+ int Wind = 0;
+ int Gust = 0;
+ int Temp = 0;
+ int Winddir = 0;
+ int Humidity = 0;
+ int Raintoday = 0;
+ int Rain24hrs = 0;
+ int Pressure = 0;
+
+ char * ptr;
+
+ ptr = strstr(WXMessage, "TempEx");
+ if (ptr)
+ Temp = (int)(atof(ptr + 7) * 1.8) + 32;
+
+ ptr = strstr(WXMessage, "WindHi");
+ if (ptr)
+ Gust = atoi(ptr + 7);
+
+ ptr = strstr(WXMessage, "WindAv");
+ if (ptr)
+ Wind = atoi(ptr + 7);
+
+ ptr = strstr(WXMessage, "WindDr");
+ if (ptr)
+ Winddir = atoi(ptr + 7);
+
+ ptr = strstr(WXMessage, "BarmPs");
+ if (ptr)
+ Pressure = (int)(atof(ptr + 7) * 0.338638866667); // Inches to 1/10 millbars
+
+ ptr = strstr(WXMessage, "HumdEx");
+ if (ptr)
+ Humidity = atoi(ptr + 7);
+
+ ptr = strstr(WXMessage, "RnFall");
+ if (ptr)
+ Rain24hrs = (int)(atof(ptr + 7) * 100.0);
+
+ ptr = strstr(WXMessage, "DailyRnFall");
+ if (ptr)
+ Raintoday = (int)(atof(ptr + 12) * 100.0);
+
+ if (Humidity > 99)
+ Humidity = 99;
+
+ sprintf(WXMessage, "%03d/%03dg%03dt%03dr%03dP%03dp%03dh%02db%05d",
+ Winddir, Wind, Gust, Temp, 0, Raintoday, Rain24hrs, Humidity, Pressure);
+
+ }
+
+ WXptr = strchr(WXMessage, 10);
+
+ if (WXptr)
+ {
+ WXend = strchr(++WXptr, 13);
+ if (WXend == 0)
+ WXend = strchr(WXptr, 10);
+ if (WXend)
+ *WXend = 0;
+ }
+ else
+ WXptr = &WXMessage[0];
+
+ // Get DDHHMM from Filetime
+
+ TM = gmtime(&STAT.st_mtime);
+
+ sprintf(DD, "%02d", TM->tm_mday);
+ sprintf(HH, "%02d", TM->tm_hour);
+ sprintf(MM, "%02d", TM->tm_min);
+
+ GetAPRSLatLonString(Lat, Lon);
+
+ Len = sprintf(Msg, "@%s%s%sz%s/%s_%s%s", DD, HH, MM, Lat, Lon, WXptr, WXComment);
+
+ Debugprintf(Msg);
+
+ for (index = 0; index < MaxBPQPortNo; index++)
+ {
+ if (WXPort[index])
+ SendAPRSMessageEx(Msg, index, WXCall, FALSE);
+ }
+
+ fclose(hFile);
+}
+
+
+/*
+Jan 22 2012 14:10
+123/005g011t031r000P000p000h00b10161
+
+/MITWXN Mitchell IN weather Station N9LYA-3 {UIV32}
+< previous
+
+@221452z3844.42N/08628.33W_203/006g007t032r000P000p000h00b10171
+Complete Weather Report Format — with Lat/Long position, no Timestamp
+! or = Lat Sym Table ID Long Symbol Code _ Wind Directn/ Speed Weather Data APRS Software WX Unit uuuu
+ 1 8 1 9 1 7 n 1 2-4
+Examples
+!4903.50N/07201.75W_220/004g005t077r000p000P000h50b09900wRSW
+!4903.50N/07201.75W_220/004g005t077r000p000P000h50b.....wRSW
+
+*/
+
+// Web Server Code
+
+// The actual HTTP socket code is in bpq32.dll. Any requests for APRS data are passed in
+// using a Named Pipe. The request looks exactly like one from a local socket, and the respone is
+// a fully pormatted HTTP packet
+
+
+#define InputBufferLen 1000
+
+
+#define MaxSessions 100
+
+
+HANDLE PipeHandle;
+
+int HTTPPort = 80;
+BOOL IPV6 = TRUE;
+
+#define MAX_PENDING_CONNECTS 5
+
+BOOL OpenSockets6();
+
+char HTDocs[MAX_PATH] = "HTML";
+char SpecialDocs[MAX_PATH] = "Special Pages";
+
+char SymbolText[192][20] = {
+
+"Police Stn", "No Symbol", "Digi", "Phone", "DX Cluster", "HF Gateway", "Plane sm", "Mob Sat Stn",
+"WheelChair", "Snowmobile", "Red Cross", "Boy Scout", "Home", "X", "Red Dot", "Circle (0)",
+"Circle (1)", "Circle (2)", "Circle (3)", "Circle (4)", "Circle (5)", "Circle (6)", "Circle (7)", "Circle (8)",
+"Circle (9)", "Fire", "Campground", "Motorcycle", "Rail Eng.", "Car", "File svr", "HC Future",
+
+"Aid Stn", "BBS", "Canoe", "No Symbol", "Eyeball", "Tractor", "Grid Squ.", "Hotel",
+"Tcp/ip", "No Symbol", "School", "Usr Log-ON", "MacAPRS", "NTS Stn", "Balloon", "Police",
+"TBD", "Rec Veh'le", "Shuttle", "SSTV", "Bus", "ATV", "WX Service", "Helo",
+"Yacht", "WinAPRS", "Jogger", "Triangle", "PBBS", "Plane lrge", "WX Station", "Dish Ant.",
+
+"Ambulance", "Bike", "ICP", "Fire Station", "Horse", "Fire Truck", "Glider", "Hospital",
+"IOTA", "Jeep", "Truck", "Laptop", "Mic-E Rptr", "Node", "EOC", "Rover",
+"Grid squ.", "Antenna", "Power Boat", "Truck Stop", "Truck 18wh", "Van", "Water Stn", "XAPRS",
+"Yagi", "Shelter", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "", "",
+
+"Emergency", "No Symbol", "No. Digi", "Bank", "No Symbol", "No. Diam'd", "Crash site", "Cloudy",
+"MEO", "Snow", "Church", "Girl Scout", "Home (HF)", "UnknownPos", "Destination", "No. Circle",
+"No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol",
+"Petrol Stn", "Hail", "Park", "Gale Fl", "No Symbol", "No. Car", "Info Kiosk", "Hurricane",
+
+"No. Box", "Snow blwng", "Coast G'rd", "Drizzle", "Smoke", "Fr'ze Rain", "Snow Shwr", "Haze",
+"Rain Shwr", "Lightning", "Kenwood", "Lighthouse", "No Symbol", "Nav Buoy", "Rocket", "Parking ",
+"Quake", "Restaurant", "Sat/Pacsat", "T'storm", "Sunny", "VORTAC", "No. WXS", "Pharmacy",
+"No Symbol", "No Symbol", "Wall Cloud", "No Symbol", "No Symbol", "No. Plane", "No. WX Stn", "Rain",
+
+"No. Diamond", "Dust blwng", "No. CivDef", "DX Spot", "Sleet", "Funnel Cld", "Gale", "HAM store",
+"No. Blk Box", "WorkZone", "SUV", "Area Locns", "Milepost", "No. Triang", "Circle sm", "Part Cloud",
+"No Symbol", "Restrooms", "No. Boat", "Tornado", "No. Truck", "No. Van", "Flooding", "No Symbol",
+"Sky Warn", "No Symbol", "Fog", "No Symbol", "No Symbol", "No Symbol", "", ""};
+
+// All Calls (8 per line)
+
+//
");
+ HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
+ send(sockptr->sock, Header, HeaderLen, 0);
+ send(sockptr->sock, OutBuffer, OutputLen, 0);
+
+ return;
+ }
+ }
+
+ URL = &MsgPtr[4];
+
+ ptr = strstr(URL, " HTTP");
+
+ if (ptr)
+ *ptr = 0;
+
+ if (_stricmp(URL, "/APRS") == 0)
+ {
+ // Return APRS Index Page
+
+ OutputLen = sprintf(OutBuffer, APRSIndexPage, "");
+ HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
+ send(sockptr->sock, Header, HeaderLen, 0);
+ send(sockptr->sock, OutBuffer, OutputLen, 0);
+
+ return;
+ }
+
+
+ if (_memicmp(URL, "/aprs/msgs/entermsg", 19) == 0 || _memicmp(URL, "/aprs/entermsg", 14) == 0)
+ {
+ char * To = strchr(URL, '=');
+
+ if (LOCAL == FALSE && COOKIE == FALSE)
+ {
+ // Send Not Authorized
+
+ OutputLen = sprintf(OutBuffer, APRSIndexPage, " Not authorized - please return to Node Menu and sign in");
+ HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(OutputLen + strlen(Tail)));
+ send(sock, Header, HeaderLen, 0);
+ send(sock, OutBuffer, OutputLen, 0);
+ send(sock, Tail, (int)strlen(Tail), 0);
+ return;
+ }
+
+
+ if (To)
+ {
+ To++;
+ UndoTransparency(To);
+ strlop(To, '&');
+ }
+ else
+ To = "";
+
+ OutputLen = sprintf(OutBuffer, SendMsgPage, To);
+ HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
+ send(sockptr->sock, Header, HeaderLen, 0);
+ send(sockptr->sock, OutBuffer, OutputLen, 0);
+
+ return;
+
+ }
+
+ else if (_memicmp(URL, "/aprs/msgs", 10) == 0)
+ {
+ // Return Messages Received Page
+
+ struct APRSMESSAGE * ptr = SMEM->Messages;
+ int n = 0;
+ char BaseCall[10];
+ char BaseFrom[10];
+ char * MsgCall = LoppedAPRSCall;
+ BOOL OnlyMine = TRUE;
+ BOOL AllSSID = TRUE;
+ BOOL OnlySeq = FALSE;
+ BOOL ShowBulls = TRUE;
+
+ // Parse parameters
+
+ // ?call=g8bpq&bulls=true&seqonly=true&onlymine=true
+
+ char * params = strchr(URL, '?');
+
+ if (params)
+ {
+ char * param, * context;
+
+ param = strtok_s(++params, "&", &context);
+
+ while (param)
+ {
+ char * val = strlop(param, '=');
+
+ if (val)
+ {
+ strlop(val, ' ');
+ if (_stricmp(param, "call") == 0)
+ MsgCall = _strupr(val);
+ else if (_stricmp(param, "bulls") == 0)
+ ShowBulls = !_stricmp(val, "true");
+ else if (_stricmp(param, "onlyseq") == 0)
+ OnlySeq = !_stricmp(val, "true");
+ else if (_stricmp(param, "onlymine") == 0)
+ OnlyMine = !_stricmp(val, "true");
+ else if (_stricmp(param, "AllSSID") == 0)
+ AllSSID = !_stricmp(val, "true");
+ }
+ param = strtok_s(NULL,"&", &context);
+ }
+ }
+ if (AllSSID)
+ {
+ memcpy(BaseCall, MsgCall, 10);
+ strlop(BaseCall, '-');
+ }
+
+ OutputLen = sprintf(OutBuffer, WebHeader, MsgCall, MsgCall);
+
+ while (ptr)
+ {
+ char ToLopped[11] = "";
+ memcpy(ToLopped, ptr->ToCall, 10);
+ strlop(ToLopped, ' ');
+
+ if (memcmp(ToLopped, "BLN", 3) == 0)
+ if (ShowBulls == TRUE)
+ goto wantit;
+
+ if (strcmp(ToLopped, MsgCall) == 0) // to me?
+ goto wantit;
+
+ if (strcmp(ptr->FromCall, MsgCall) == 0) // to me?
+ goto wantit;
+
+ if (AllSSID)
+ {
+ memcpy(BaseFrom, ToLopped, 10);
+ strlop(BaseFrom, '-');
+
+ if (strcmp(BaseFrom, BaseCall) == 0)
+ goto wantit;
+
+ memcpy(BaseFrom, ptr->FromCall, 10);
+ strlop(BaseFrom, '-');
+
+ if (strcmp(BaseFrom, BaseCall) == 0)
+ goto wantit;
+
+ }
+
+ if (OnlyMine == FALSE) // Want All
+ if (OnlySeq == FALSE || ptr->Seq[0] != 0)
+ goto wantit;
+
+ // ignore
+
+ ptr = ptr->Next;
+ continue;
+ wantit:
+ OutputLen += sprintf(&OutBuffer[OutputLen], WebLine,
+ ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time,
+ ptr->FromCall, ptr->ToCall, ptr->Text);
+
+ ptr = ptr->Next;
+
+ if (OutputLen > 99000)
+ break;
+
+ }
+
+ OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer);
+
+ HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
+ sendandcheck(sock, Header, HeaderLen);
+ sendandcheck(sock, OutBuffer, OutputLen);
+
+ return;
+
+ }
+
+ else if (_memicmp(URL, "/aprs/txmsgs", 12) == 0)
+ {
+ // Return Messages Received Page
+
+ struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs;
+ char * MsgCall = LoppedAPRSCall;
+
+ char Retries[10];
+
+
+ OutputLen = sprintf(OutBuffer, WebTXHeader, MsgCall, MsgCall);
+
+ while (ptr)
+ {
+ char ToLopped[11] = "";
+
+ if (ptr->Acked)
+ strcpy(Retries, "A");
+ else if (ptr->Retries == 0)
+ strcpy(Retries, "F");
+ else
+ sprintf(Retries, "%d", ptr->Retries);
+
+ memcpy(ToLopped, ptr->ToCall, 10);
+ strlop(ToLopped, ' ');
+
+ OutputLen += sprintf(&OutBuffer[OutputLen], WebTXLine,
+ ptr->ToCall, ptr->Seq, ptr->Time, Retries, ptr->Text);
+ ptr = ptr->Next;
+
+ if (OutputLen > 99000)
+ break;
+
+ }
+
+ OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer);
+
+ HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen);
+ sendandcheck(sock, Header, HeaderLen);
+ sendandcheck(sock, OutBuffer, OutputLen);
+
+ return;
+
+ }
+
+
+ if (_memicmp(URL, "/aprs/find.cgi?call=", 20) == 0)
+ {
+ // return Station details
+
+ char * Call = &URL[20];
+ BOOL RFOnly, WX, Mobile, Object = FALSE;
+ struct STATIONRECORD * stn;
+ char * Referrer = strstr(ptr + 1, "Referer:");
+
+ // Undo any % transparency in call
+
+ char * ptr1 = Call;
+ char * ptr2 = Key;
+ char c;
+
+ c = *(ptr1++);
+
+ while (c)
+ {
+ if (c == '%')
+ {
+ int n;
+ int m = *(ptr1++) - '0';
+ if (m > 9) m = m - 7;
+ n = *(ptr1++) - '0';
+ if (n > 9) n = n - 7;
+
+ *(ptr2++) = m * 16 + n;
+ }
+ else if (c == '+')
+ *(ptr2++) = ' ';
+ else
+ *(ptr2++) = c;
+
+ c = *(ptr1++);
+ }
+
+ *(ptr2++) = 0;
+
+ if (Referrer)
+ {
+ ptr = strchr(Referrer, 13);
+ if (ptr)
+ {
+ *ptr = 0;
+ RFOnly = !(strstr(Referrer, "rf") == NULL);
+ WX = !(strstr(Referrer, "wx") == NULL);
+ Mobile = !(strstr(Referrer, "mobile") == NULL);
+ Object = !(strstr(Referrer, "obj") == NULL);
+
+ if (WX)
+ strcpy(URL, "/aprs/infowx_call.html");
+ else if (Mobile)
+ strcpy(URL, "/aprs/infomobile_call.html");
+ else if (Object)
+ strcpy(URL, "/aprs/infoobj_call.html");
+ else
+ strcpy(URL, "/aprs/info_call.html");
+ }
+ }
+
+ if (Object)
+ {
+ // Name is space padded, and could have embedded spaces
+
+ int Keylen = (int)strlen(Key);
+
+ if (Keylen < 9)
+ memset(&Key[Keylen], 32, 9 - Keylen);
+ }
+
+ stn = FindStation(Key, FALSE);
+
+ if (stn == NULL)
+ strcpy(URL, "/aprs/noinfo.html");
+ else
+ sockptr->SelCall = stn;
+ }
+
+
+ strcpy(sockptr->Callsign, Key);
+
+ APRSSendMessageFile(sockptr, URL);
+
+ return;
+}
+
+// Code for handling APRS messages within BPQ32/LinBPQ instead of GUI
+
+
+int ProcessMessage(char * Payload, struct STATIONRECORD * Station)
+{
+ char MsgDest[10];
+ struct APRSMESSAGE * Message;
+ struct APRSMESSAGE * ptr = SMEM->Messages;
+ char * TextPtr = &Payload[11];
+ char * SeqPtr;
+ int n = 0;
+ char FromCall[10] = " ";
+ struct tm * TM;
+ time_t NOW;
+ char noSeq[] = "";
+ int ourMessage = 0;
+
+ memcpy(FromCall, Station->Callsign, strlen(Station->Callsign));
+ memcpy(MsgDest, &Payload[1], 9);
+ MsgDest[9] = 0;
+
+ if (strcmp(MsgDest, CallPadded) == 0) // to me?
+ {
+ SMEM->NeedRefresh = 255; // Flag to control Msg popup
+ ourMessage = 1;
+ }
+ else
+ SMEM->NeedRefresh = 1;
+
+ SeqPtr = strchr(TextPtr, '{');
+
+ if (SeqPtr)
+ {
+ *(SeqPtr++) = 0;
+ if(strlen(SeqPtr) > 6)
+ SeqPtr[7] = 0;
+ }
+ else
+ SeqPtr = noSeq;
+
+ if (_memicmp(TextPtr, "ack", 3) == 0)
+ {
+ // Message Ack. See if for one of our messages
+
+ ptr = SMEM->OutstandingMsgs;
+
+ if (ptr == 0)
+ return ourMessage;
+
+ do
+ {
+ if (strcmp(ptr->FromCall, MsgDest) == 0
+ && strcmp(ptr->ToCall, FromCall) == 0
+ && strcmp(ptr->Seq, &TextPtr[3]) == 0)
+ {
+ // Message is acked
+
+ ptr->Retries = 0;
+ ptr->Acked = TRUE;
+
+ return ourMessage;
+ }
+ ptr = ptr->Next;
+ n++;
+
+ } while (ptr);
+
+ return ourMessage;
+ }
+
+ // See if we already have this message
+
+ ptr = SMEM->Messages;
+
+ while(ptr)
+ {
+ if (strcmp(ptr->ToCall, MsgDest) == 0
+ && strcmp(ptr->FromCall, FromCall) == 0
+ && strcmp(ptr->Seq, SeqPtr) == 0
+ && strcmp(ptr->Text, TextPtr) == 0)
+
+ // Duplicate
+
+ return ourMessage;
+
+ ptr = ptr->Next;
+ }
+
+ Message = APRSGetMessageBuffer();
+
+ if (Message == NULL)
+ return ourMessage;
+
+ memset(Message, 0, sizeof(struct APRSMESSAGE));
+ memset(Message->FromCall, ' ', 9);
+ memcpy(Message->FromCall, Station->Callsign, strlen(Station->Callsign));
+ strcpy(Message->ToCall, MsgDest);
+
+ if (SeqPtr)
+ {
+ strcpy(Message->Seq, SeqPtr);
+
+ // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message
+
+ if (SeqPtr[2] == '}')
+ {
+ struct APRSMESSAGE * ptr1;
+ int nn = 0;
+
+ strcpy(Station->LastRXSeq, SeqPtr);
+
+ ptr1 = SMEM->OutstandingMsgs;
+
+ while (ptr1)
+ {
+ if (strcmp(ptr1->FromCall, MsgDest) == 0
+ && strcmp(ptr1->ToCall, FromCall) == 0
+ && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0)
+ {
+ // Message is acked
+
+ ptr1->Acked = TRUE;
+ ptr1->Retries = 0;
+
+ break;
+ }
+ ptr1 = ptr1->Next;
+ nn++;
+ }
+ }
+ else
+ {
+ // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanges
+
+ Station->SimpleNumericSeq = TRUE;
+ }
+ }
+
+ if (strlen(TextPtr) > 100)
+ TextPtr[100] = 0;
+
+ strcpy(Message->Text, TextPtr);
+
+ NOW = time(NULL);
+
+ if (DefaultLocalTime)
+ TM = localtime(&NOW);
+ else
+ TM = gmtime(&NOW);
+
+ sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min);
+
+ if (_stricmp(MsgDest, CallPadded) == 0 && SeqPtr) // ack it if it has a sequence
+ {
+ // For us - send an Ack
+
+ char ack[30];
+ APRSHEARDRECORD * STN;
+
+ sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq);
+
+ if (memcmp(Message->FromCall, "SERVER ", 9) == 0)
+ {
+ SendAPRSMessage(ack, 0); // IS
+ }
+ else
+ {
+ STN = FindStationInMH(Message->ToCall);
+
+ if (STN)
+ SendAPRSMessage(ack, STN->rfPort);
+ else
+ {
+ SendAPRSMessage(ack, -1); // All RF ports
+ SendAPRSMessage(ack, 0); // IS
+ }
+ }
+ }
+
+ if (SaveAPRSMsgs)
+ SaveAPRSMessage(Message);
+
+ ptr = SMEM->Messages;
+
+ if (ptr == NULL)
+ {
+ SMEM->Messages = Message;
+ }
+ else
+ {
+ n++;
+ while(ptr->Next)
+ {
+ ptr = ptr->Next;
+ n++;
+ }
+ ptr->Next = Message;
+ }
+
+ return ourMessage;
+}
+
+BOOL InternalSendAPRSMessage(char * Text, char * Call)
+{
+ char Msg[255];
+ size_t len = strlen(Call);
+ APRSHEARDRECORD * STN;
+ struct tm * TM;
+ time_t NOW;
+
+ struct APRSMESSAGE * Message;
+ struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs;
+
+ Message = APRSGetMessageBuffer();
+
+ if (Message == NULL)
+ return FALSE;
+
+ memset(Message, 0, sizeof(struct APRSMESSAGE));
+ strcpy(Message->FromCall, CallPadded);
+
+ memset(Message->ToCall, ' ', 9);
+ memcpy(Message->ToCall, Call, len);
+
+ Message->ToStation = FindStation(Call, TRUE);
+
+ if (Message->ToStation == NULL)
+ return FALSE;
+
+ SMEM->NeedRefresh = TRUE;
+
+ if (Message->ToStation->LastRXSeq[0]) // Have we received a Reply-Ack message from him?
+ sprintf(Message->Seq, "%02X}%c%c", ++Message->ToStation->NextSeq, Message->ToStation->LastRXSeq[0], Message->ToStation->LastRXSeq[1]);
+ else
+ {
+ if (Message->ToStation->SimpleNumericSeq)
+ sprintf(Message->Seq, "%d", ++Message->ToStation->NextSeq);
+ else
+ sprintf(Message->Seq, "%02X}", ++Message->ToStation->NextSeq); // Don't know, so assume message-ack capable
+ }
+
+ if (strlen(Text) > 100)
+ Text[100] = 0;
+
+ strcpy(Message->Text, Text);
+ Message->Retries = RetryCount;
+ Message->RetryTimer = RetryTimer;
+
+ NOW = time(NULL);
+
+ if (DefaultLocalTime)
+ TM = localtime(&NOW);
+ else
+ TM = gmtime(&NOW);
+
+ sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min);
+
+
+ // Chain to Outstanding Queue
+
+ if (ptr == NULL)
+ {
+ SMEM->OutstandingMsgs = Message;
+ }
+ else
+ {
+ while(ptr->Next)
+ {
+ ptr = ptr->Next;
+ }
+ ptr->Next = Message;
+ }
+
+ sprintf(Msg, ":%-9s:%s{%s", Call, Text, Message->Seq);
+
+ if (strcmp(Call, "SERVER") == 0)
+ {
+ SendAPRSMessage(Msg, 0); // IS
+ return TRUE;
+ }
+
+ STN = FindStationInMH(Message->ToCall);
+
+ if (STN && STN->MHTIME > (time(NULL) - 900)) // Heard in last 15 mins
+ SendAPRSMessage(Msg, STN->rfPort);
+ else
+ {
+ SendAPRSMessage(Msg, -1); // All RF ports
+ SendAPRSMessage(Msg, 0); // IS
+ }
+ return TRUE;
+}
+
+
+
+
+
+extern BOOL APRSReconfigFlag;
+extern struct DATAMESSAGE * REPLYBUFFER;
+extern char COMMANDBUFFER[81];
+extern char OrigCmdBuffer[81];
+
+BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr);
+
+VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // APRS Subcommands. Default for compatibility is APRSMH
+
+ // Others are STATUS ENABLEIGATE DISABLEIGATE RECONFIG
+
+ APRSHEARDRECORD * MH = MHDATA;
+ int n = MAXHEARDENTRIES;
+ char * ptr;
+ char * Pattern, * context;
+ int Port = -1;
+ char dummypattern[] ="";
+
+ if (memcmp(CmdTail, "? ", 2) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "APRS Subcommmands:\r");
+ Bufferptr = Cmdprintf(Session, Bufferptr, "STATUS SEND MSGS SENT ENABLEIGATE DISABLEIGATE BEACON RECONFIG\r");
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Default is Station list - Params [Port] [Pattern]\r");
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "RECONFIG ", 5) == 0)
+ {
+ if (isSYSOP(Session, Bufferptr) == FALSE)
+ return;
+
+ if (!ProcessConfig())
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check failed - will continue with old config");
+ }
+ else
+ {
+ APRSReconfigFlag=TRUE;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfiguration requested\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+
+ if (memcmp(CmdTail, "ENABLEIGATE ", 6) == 0)
+ {
+ if (isSYSOP(Session, Bufferptr) == FALSE)
+ return;
+
+ IGateEnabled = TRUE;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "DISABLEIGATE ", 6) == 0)
+ {
+ if (isSYSOP(Session, Bufferptr) == FALSE)
+ return;
+
+ IGateEnabled = FALSE;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "STATUS ", 7) == 0)
+ {
+ if (IGateEnabled == FALSE)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r");
+ else
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled ");
+ if (APRSISOpen)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "and connected to %s\r", RealISHost);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "but not connected\r");
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "BEACON ", 7) == 0)
+ {
+ if (isSYSOP(Session, Bufferptr) == FALSE)
+ return;
+
+ BeaconCounter = 2;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Beacons requested\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "MSGS ", 5) == 0)
+ {
+ struct APRSMESSAGE * ptr = SMEM->Messages;
+ char Addrs[32];
+
+ Bufferptr = Cmdprintf(Session, Bufferptr,
+ "\rTime Calls Seq Text\r");
+
+ while (ptr)
+ {
+ char ToLopped[11] = "";
+
+ memcpy(ToLopped, ptr->ToCall, 10);
+ strlop(ToLopped, ' ');
+
+ sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %s\r",
+ ptr->Time, Addrs, ptr->Seq, ptr->Text);
+
+ ptr = ptr->Next;
+ }
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "SENT ", 5) == 0)
+ {
+ struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs;
+ char Addrs[32];
+
+ Bufferptr = Cmdprintf(Session, Bufferptr,
+ "\rTime Calls Seq State Text\r");
+
+ while (ptr)
+ {
+ char ToLopped[11] = "";
+ char Retries[10];
+
+ if (ptr->Acked)
+ strcpy(Retries, "A");
+ else if (ptr->Retries == 0)
+ strcpy(Retries, "F");
+ else
+ sprintf(Retries, "%d", ptr->Retries);
+
+
+ memcpy(ToLopped, ptr->ToCall, 10);
+ strlop(ToLopped, ' ');
+
+ sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %-2s %s\r",
+ ptr->Time, Addrs, ptr->Seq, Retries, ptr->Text);
+
+ ptr = ptr->Next;
+ }
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (memcmp(CmdTail, "SEND ", 5) == 0)
+ {
+ // Send Message. Params are Call and Message
+
+ char * Call = strtok_s(&CmdTail[5], " \r", &context);
+ char * Text = strtok_s(NULL, " \r", &context);
+ int len = 0;
+
+ if (isSYSOP(Session, Bufferptr) == FALSE)
+ return;
+
+ if (Call)
+ len = (int)strlen(Call);
+
+ if (len < 3 || len > 9)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Callsign\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (Text == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "No Message Text\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ // Replace command tail with original (before conversion to upper case
+
+ Text = Text + (OrigCmdBuffer - COMMANDBUFFER);
+
+ InternalSendAPRSMessage(Text, Call);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // DISPLAY APRS HEARD LIST
+
+ // APRS [Port] [Pattern]
+
+ Pattern = strtok_s(CmdTail, " \r", &context);
+
+ if (Pattern && (int)strlen(Pattern) < 3)
+ {
+ // could be port number
+
+ if (isdigit(Pattern[0]) && (Pattern[1] == 0 || isdigit(Pattern[1])))
+ {
+ Port = atoi(Pattern);
+ Pattern = strtok_s(NULL, " \r", &context);
+ }
+ }
+
+ // Param is a pattern to match
+
+ if (Pattern == NULL)
+ Pattern = dummypattern;
+
+ if (Pattern[0] == ' ')
+ {
+ // Prepare Pattern
+
+ char * ptr1 = Pattern + 1;
+ char * ptr2 = Pattern;
+ char c;
+
+ do
+ {
+ c = *ptr1++;
+ *(ptr2++) = c;
+ }
+ while (c != ' ');
+
+ *(--ptr2) = 0;
+ }
+
+ strlop(Pattern, ' ');
+ _strupr(Pattern);
+
+ *(Bufferptr++) = 13;
+
+ while (n--)
+ {
+ if (MH->MHCALL[0] == 0)
+ break;
+
+ if ((Port > -1) && Port != MH->rfPort)
+ {
+ MH++;
+ continue;
+ }
+
+ ptr = FormatAPRSMH(MH);
+
+ MH++;
+
+ if (ptr)
+ {
+ if (Pattern[0] && strstr(ptr, Pattern) == 0)
+ continue;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", ptr);
+ }
+ }
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+int GetPosnFromAPRS(char * Call, double * Lat, double * Lon)
+{
+ struct STATIONRECORD * Station;
+
+ Station = FindStation(Call, FALSE);
+
+ if (Station)
+ {
+ *Lat = Station->Lat;
+ *Lon = Station->Lon;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+// Station Name Font
+
+const unsigned char ASCII[][5] = {
+//const u08 ASCII[][5] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00} // 20
+ ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
+ ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
+ ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
+ ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
+ ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
+ ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
+ ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
+ ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
+ ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
+ ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
+ ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
+ ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
+ ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -
+ ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
+ ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /
+ ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0
+ ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1
+ ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2
+ ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3
+ ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4
+ ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5
+ ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6
+ ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7
+ ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8
+ ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9
+ ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :
+ ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;
+ ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <
+ ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =
+ ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >
+ ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?
+ ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @
+ ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A
+ ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B
+ ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C
+ ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D
+ ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E
+ ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F
+ ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G
+ ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H
+ ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I
+ ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J
+ ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K
+ ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L
+ ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M
+ ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N
+ ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O
+ ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P
+ ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q
+ ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R
+ ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S
+ ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T
+ ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U
+ ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V
+ ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W
+ ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X
+ ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y
+ ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z
+ ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [
+ ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c
+ ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ]
+ ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^
+ ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _
+ ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 `
+ ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a
+ ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b
+ ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c
+ ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d
+ ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e
+ ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f
+ ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g
+ ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h
+ ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i
+ ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j
+ ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k
+ ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l
+ ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m
+ ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n
+ ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o
+ ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p
+ ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q
+ ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r
+ ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s
+ ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t
+ ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u
+ ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v
+ ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w
+ ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x
+ ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y
+ ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z
+ ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b {
+ ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c |
+ ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d }
+ ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~
+ ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL
+};
+
+
+// APRS Web Map Code
+
+// Not sure yet what is best way to do station icons but for now build and cache any needed icons
+
+extern int IconDataLen;
+extern unsigned long long IconData[]; // Symbols as a png image.&
+
+// IconData is a png image, so needs to be uncompressed to an RGB array
+
+
+// Will key cached icons by IconRow, IconCol, Overlay Char - xxxxA
+
+int cachedIconCount = 0;
+
+// We need key, icon data, icon len for each. Maybe a simple linked list - we never remove any
+
+struct iconCacheEntry
+{
+ struct iconCacheEntry * Next;
+ char key[8];
+ int pngimagelen;
+ int pngmalloclen;
+ unsigned char * pngimage;
+};
+
+struct iconCacheEntry * iconCache = NULL;
+
+
+// Each icon has to be created as an RGB array, then converted to a png image, as
+// Leaflet icons need a png file
+
+#include "mypng.h"
+
+
+struct png_info_struct * info_ptr = NULL;
+
+unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon);
+
+void createIcon(char * Key, int iconRow, int iconCol, char Overlay)
+{
+ int i, j, index, mask;
+ int row;
+ int col; // First row
+ unsigned char * rgbData = malloc(68 * 22); // 1323
+ unsigned char * ptr = rgbData;
+ png_color colour = {0, 0, 0};
+ int Pointer;
+ char c;
+ int bit;
+ struct iconCacheEntry * Icon = zalloc(sizeof(struct iconCacheEntry));
+
+ strcpy(Icon->key, Key);
+
+ // icon data is in info_ptr->row_pointers (we have 255 of them)
+
+ row = iconRow * 21;
+ col = iconCol * 21 * 3;
+
+ for (j = 0; j < 22; j++)
+ {
+ memcpy(ptr, info_ptr->row_pointers[row + j] + col, 22 * 3); // One scan line
+ ptr += 68; // Rounded up to mod 4
+ }
+
+
+ // This code seems to assume an icon is 16 pixels, but image is 22 x 22 ???
+
+// j = ptr->iconRow * 21 * 337 * 3 + ptr->iconCol * 21 * 3 + 9 + 337 * 9;
+// for (i = 0; i < 16; i++)
+// {
+// memcpy(nptr, &iconImage[j], 16 * 3);
+// nptr += 6144;
+// j += 337 * 3;
+// }
+
+
+ // If an overlay is specified, add it
+
+ if (Overlay)
+ {
+ Pointer = 68 * 7 + 7 * 3; // 7th row, 7th col
+ mask = 1;
+
+ for (index = 0 ; index < 7 ; index++)
+ {
+ rgbData[Pointer++] = 255; // Blank line above chars
+ rgbData[Pointer++] = 255;
+ rgbData[Pointer++] = 255;
+ }
+
+ Pointer = 68 * 8 + 7 * 3; // 8th row, 7th col
+
+ for (i = 0; i < 7; i++)
+ {
+ rgbData[Pointer++] = 255; // Blank col
+ rgbData[Pointer++] = 255;
+ rgbData[Pointer++] = 255;
+
+ for (index = 0 ; index < 5 ; index++)
+ {
+ c = ASCII[Overlay - 0x20][index]; // Font data
+ bit = c & mask;
+
+ if (bit)
+ {
+ rgbData[Pointer++] = 0;
+ rgbData[Pointer++] = 0;
+ rgbData[Pointer++] = 0;
+ }
+ else
+ {
+ rgbData[Pointer++] = 255;
+ rgbData[Pointer++] = 255;
+ rgbData[Pointer++] = 255;
+ }
+ }
+
+ rgbData[Pointer++] = 255; // Blank col
+ rgbData[Pointer++] = 255;
+ rgbData[Pointer++] = 255;
+
+ mask <<= 1;
+ Pointer += 47;
+ }
+ for (index = 0 ; index < 7 ; index++)
+ {
+ rgbData[Pointer++] = 255; // Blank line above chars
+ rgbData[Pointer++] = 255;
+ rgbData[Pointer++] = 255;
+ }
+ }
+
+ // Encode
+
+ PngEncode(rgbData, 22, 22, Icon);
+
+ if (iconCache)
+ Icon->Next = iconCache;
+
+ iconCache = Icon;
+
+}
+
+int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL)
+{
+ char Key[8] = "";
+ struct iconCacheEntry * Icon = iconCache;
+
+ memcpy(Key, &NodeURL[5], 5);
+
+ while (Icon)
+ {
+ if (strcmp(Icon->key, Key) == 0) // Have it
+ {
+ memcpy(_REPLYBUFFER, Icon->pngimage, Icon->pngimagelen);
+ return Icon->pngimagelen;
+ }
+ Icon = Icon->Next;
+ }
+
+ return 0;
+}
+
+char * doHTMLTransparency(char * string)
+{
+ // Make sure string doesn't contain forbidden XML chars (<>"'&)
+
+ char * newstring = malloc(5 * strlen(string) + 1); // If len is zero still need null terminator
+
+ char * in = string;
+ char * out = newstring;
+ char c;
+
+ c = *(in++);
+
+ while (c)
+ {
+ switch (c)
+ {
+ case '<':
+
+ strcpy(out, "<");
+ out += 4;
+ break;
+
+ case '>':
+
+ strcpy(out, ">");
+ out += 4;
+ break;
+
+ case '"':
+
+ strcpy(out, """);
+ out += 6;
+ break;
+
+ case '\'':
+
+ strcpy(out, "'");
+ out += 6;
+ break;
+
+ case '&':
+
+ strcpy(out, "&");
+ out += 5;
+ break;
+
+ case ',':
+
+ strcpy(out, ",");
+ out += 5;
+ break;
+
+ case '|':
+
+ strcpy(out, "|");
+ out += 5;
+ break;
+
+ default:
+
+ *(out++) = c;
+ }
+ c = *(in++);
+ }
+
+ *(out++) = 0;
+ return newstring;
+}
+
+int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb)
+{
+ struct STATIONRECORD * ptr = *StationRecords;
+ int n = 0, Len = 0;
+ struct tm * TM;
+ time_t NOW = time(NULL);
+ char popup[65536] = "";
+ char Msg[2048];
+ int LocalTime = 0;
+ int KM = DefaultDistKM;
+ char * ptr1;
+
+ while (ptr && aprs && Len < 240000)
+ {
+ if (ptr->Lat != 0.0 && ptr->Lon != 0.0)
+ {
+ if (ptr->Lat > S && ptr->Lat < N && ptr->Lon > W && ptr->Lon < E)
+ {
+ // See if we have the Icon - if not build
+
+ char IconKey[6];
+ struct iconCacheEntry * Icon = iconCache;
+
+ sprintf(IconKey, "%02X%02X ", ptr->iconRow, ptr->iconCol);
+
+ if (ptr->IconOverlay)
+ IconKey[4] = ptr->IconOverlay;
+ else
+ IconKey[4] = '@';
+
+ while (Icon)
+ {
+ if (strcmp(Icon->key, IconKey) == 0) // Have it
+ break;
+
+ Icon = Icon->Next;
+ }
+
+ if (Icon == NULL)
+ createIcon(IconKey, ptr->iconRow, ptr->iconCol, ptr->IconOverlay);
+
+ popup[0] = 0;
+
+ if (ptr->Approx)
+ {
+ sprintf(Msg, "Approximate Position From Locator ");
+ strcat(popup, Msg);
+ }
+ ptr1 = doHTMLTransparency(ptr->Path);
+ sprintf(Msg, "%s ", ptr1);
+ strcat(popup, Msg);
+ free(ptr1);
+ ptr1 = doHTMLTransparency(ptr->LastPacket);
+ sprintf(Msg, "%s ", ptr1);
+ strcat(popup, Msg);
+ free(ptr1);
+ ptr1 = doHTMLTransparency(ptr->Status);
+ sprintf(Msg, "%s ", ptr1);
+ strcat(popup, Msg);
+ free(ptr1);
+ if (LocalTime)
+ TM = localtime(&ptr->TimeLastUpdated);
+ else
+ TM = gmtime(&ptr->TimeLastUpdated);
+
+ sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d ",
+ TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort);
+
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f° Speed %3.1f ",
+ myDistance(ptr->Lat, ptr->Lon, KM),
+ myBearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed);
+ strcat(popup, Msg);
+
+ if (ptr->LastWXPacket[0])
+ {
+ //display wx info
+
+ struct APRSConnectionInfo temp;
+
+ memset(&temp, 0, sizeof(temp));
+
+ DecodeWXReport(&temp, ptr->LastWXPacket);
+
+ sprintf(Msg, "Wind Speed %d MPH ", temp.WindSpeed);
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Wind Gust %d MPH ", temp.WindGust);
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Wind Direction %d\xC2\xB0 ", temp.WindDirn);
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Temperature %d\xC2\xB0 F ", temp.Temp);
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Pressure %05.1f ", temp.Pressure / 10.0);
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Humidity %d%% ", temp.Humidity);
+ strcat(popup, Msg);
+
+ sprintf(Msg, "Rainfall Last Hour/Last 24H/Today %5.2f, %5.2f, %5.2f (inches)",
+ temp.RainLastHour / 100.0, temp.RainLastDay / 100.0, temp.RainToday / 100.0);
+
+ ptr1 = doHTMLTransparency(Msg);
+ sprintf(Msg, "%s ", ptr1);
+ strcat(popup, Msg);
+ free(ptr1);
+ }
+
+ Len += sprintf(&Buffer[Len],"A,%.4f,%.4f,%s,%s,%s,%d\r\n|",
+ ptr->Lat, ptr->Lon, ptr->Callsign, popup, IconKey,
+ NOW - ptr->TimeLastUpdated);
+
+ if (ptr->TrackTime[0] && ptr->TrackTime[1]) // Have trackpoints
+ {
+ int n = ptr->Trackptr;
+ int i;
+ double lastLat = 0;
+
+ // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros
+
+ Len += sprintf(&Buffer[Len],"T,");
+
+ for (i = 0; i < TRACKPOINTS; i++)
+ {
+ if (ptr->TrackTime[n])
+ {
+ Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]);
+ lastLat = ptr->LatTrack[n];
+ }
+
+ n++;
+ if (n == TRACKPOINTS)
+ n = 0;
+ }
+ if (lastLat != ptr->Lat)
+ Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->Lat, ptr->Lon); //Add current position to end of track
+ else
+ Len += sprintf(&Buffer[Len],"\r\n|");
+ }
+ }
+ }
+
+ ptr = ptr->Next;
+ }
+ return Len;
+}
+
+
+
+
+ /* The png_jmpbuf() macro, used in error handling, became available in
+ * libpng version 1.0.6. If you want to be able to run your code with older
+ * versions of libpng, you must define the macro yourself (but only if it
+ * is not already defined by libpng!).
+ */
+#ifndef png_jmpbuf
+# define png_jmpbuf(png_ptr) ((png_ptr)->png_jmpbuf)
+#endif
+/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp()
+ * returns zero if the image is a PNG and nonzero if it isn't a PNG.
+ *
+ * The function check_if_png() shown here, but not used, returns nonzero (true)
+ * if the file can be opened and is a PNG, 0 (false) otherwise.
+ *
+ * If this call is successful, and you are going to keep the file open,
+ * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once
+ * you have created the png_ptr, so that libpng knows your application
+ * has read that many bytes from the start of the file. Make sure you
+ * don't call png_set_sig_bytes() with more than 8 bytes read or give it
+ * an incorrect number of bytes read, or you will either have read too
+ * many bytes (your fault), or you are telling libpng to read the wrong
+ * number of magic bytes (also your fault).
+ *
+ * Many applications already read the first 2 or 4 bytes from the start
+ * of the image to determine the file type, so it would be easiest just
+ * to pass the bytes to png_sig_cmp() or even skip that if you know
+ * you have a PNG file, and call png_set_sig_bytes().
+ */
+
+unsigned char * user_io_ptr = 0;
+
+void __cdecl user_read_fn(png_struct * png, png_bytep Buffer, png_size_t Len)
+{
+ unsigned char ** ptr = png->io_ptr;
+ unsigned char * ptr1;
+
+ ptr1 = ptr[0];
+
+ memcpy(Buffer, ptr1, Len);
+ ptr[0]+= Len;
+}
+
+
+// This is based on example https://www1.udel.edu/CIS/software/dist/libpng-1.2.8/example.c
+
+// This decodes a png encoded image from memory
+
+int read_png(unsigned char *bytes)
+{
+ png_structp png_ptr;
+ unsigned int sig_read = 0;
+
+ /* Create and initialize the png_struct with the desired error handler
+ * functions. If you want to use the default stderr and longjump method,
+ * you can supply NULL for the last three parameters. We also supply the
+ * the compiler header file version, so that we know if the application
+ * was compiled with a compatible version of the library. REQUIRED
+ */
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (png_ptr == NULL)
+ {
+ return (0);
+ }
+ /* Allocate/initialize the memory for image information. REQUIRED. */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return (0);
+ }
+ /* Set error handling if you are using the setjmp/longjmp method (this is
+ * the normal method of doing things with libpng). REQUIRED unless you
+ * set up your own error handlers in the png_create_read_struct() earlier.
+ */
+
+ user_io_ptr = (unsigned char *)&IconData;
+
+ png_set_read_fn(png_ptr, (void *)&user_io_ptr,(png_rw_ptr)user_read_fn);
+
+ png_set_sig_bytes(png_ptr, sig_read);
+
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, NULL);
+
+ // Data is in info->row_pointers. Can we use it from there ??
+
+// printf("%d %d %d\n", info_ptr->width, info_ptr->height, info_ptr->valid);
+
+ return TRUE;
+}
+
+void Myabort()
+{}
+
+// This is based on pngfile.c
+
+//-------------------------------------
+// PNGFILE.C -- Image File Functions
+//-------------------------------------
+
+// Copyright 2000, Willem van Schaik. For conditions of distribution and
+// use, see the copyright/license/disclaimer notice in png.h
+
+// Encodes pDiData to png format in memory
+
+
+
+void my_png_write_data(struct png_struct_def * png_ptr, png_bytep data, png_size_t length)
+{
+ struct iconCacheEntry * Icon = png_ptr->io_ptr;
+
+ if (Icon->pngimagelen + (int)length > Icon->pngmalloclen)
+ {
+ Icon->pngmalloclen += length;
+ Icon->pngimage = realloc(Icon->pngimage, Icon->pngmalloclen);
+ }
+
+ memcpy(&Icon->pngimage[Icon->pngimagelen], data, length);
+ Icon->pngimagelen += length;
+}
+
+ // io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr));
+ // Area png_uint_32 check;
+
+
+
+static void png_flush(png_structp png_ptr)
+{
+}
+
+unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon)
+{
+ const int ciBitDepth = 8;
+ const int ciChannels = 3;
+ png_structp png_ptr;
+ png_infop info_ptr = NULL;
+ png_uint_32 ulRowBytes;
+ static png_byte **ppbRowPointers = NULL;
+ int i;
+
+ // Set up image array and pointer. First allocate a buffer as big as the original
+ // in the unlikely event of it being too small write_data will realloc it
+
+ Icon->pngmalloclen = iWidth * iHeight * 3;
+ Icon->pngimage = malloc(Icon->pngmalloclen);
+ Icon->pngimagelen = 0;
+
+ // prepare the standard PNG structures
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!png_ptr)
+ {
+ return FALSE;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+ return FALSE;
+ }
+
+ {
+ // initialize the png structure
+
+ png_set_write_fn(png_ptr, Icon, my_png_write_data, png_flush);
+
+ png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth,
+ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+ // write the file header information
+
+ png_write_info(png_ptr, info_ptr);
+
+ // row_bytes is the width x number of channels
+
+ ulRowBytes = iWidth * ciChannels;
+
+ // we can allocate memory for an array of row-pointers
+
+ if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL)
+ Debugprintf( "Visualpng: Out of memory");
+
+ // set the individual row-pointers to point at the correct offsets
+
+ for (i = 0; i < iHeight; i++)
+ ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2);
+
+ // write out the entire image data in one call
+
+ png_write_image (png_ptr, ppbRowPointers);
+
+ // write the additional chunks to the PNG file (not really needed)
+
+ png_write_end(png_ptr, info_ptr);
+
+ // and we're done
+
+ free (ppbRowPointers);
+ ppbRowPointers = NULL;
+
+ // clean up after the write, and free any memory allocated
+
+ png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+
+ // yepp, done
+ }
+
+ return Icon->pngimage;
+}
+
+void SaveAPRSMessage(struct APRSMESSAGE * ptr)
+{
+ // Save messages in case of a restart
+
+ char FN[250];
+ FILE *file;
+
+ // Set up filename
+
+ if (BPQDirectory[0] == 0)
+ {
+ strcpy(FN,"APRSMsgs.dat");
+ }
+ else
+ {
+ strcpy(FN,BPQDirectory);
+ strcat(FN,"/");
+ strcat(FN,"APRSMsgs.dat");
+ }
+
+ if ((file = fopen(FN, "a")) == NULL)
+ return ;
+
+ fprintf(file, "%d %s,%s,%s,%s,%s\n", time(NULL), ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, ptr->Text);
+
+ fclose(file);
+}
+
+void ClearSavedMessages()
+{
+ char FN[250];
+ FILE *file;
+
+ // Set up filename
+
+ if (BPQDirectory[0] == 0)
+ {
+ strcpy(FN,"APRSMsgs.dat");
+ }
+ else
+ {
+ strcpy(FN,BPQDirectory);
+ strcat(FN,"/");
+ strcat(FN,"APRSMsgs.dat");
+ }
+
+ if ((file = fopen(FN, "w")) == NULL)
+ return ;
+
+ fclose(file);
+}
+
+void GetSavedAPRSMessages()
+{
+ // Get Saved messages
+
+ // 1668768157 SERVER ,GM8BPQ-2 ,D7Yx,10:42,filter m/200 active
+
+ char FN[250];
+ FILE *file;
+ struct APRSMESSAGE * Message;
+ struct APRSMESSAGE * ptr;
+ char Line[512];
+ char * Stamp = 0;
+ char * From = 0;
+ char * To = 0;
+ char * Seq = 0;
+ char * Time = 0;
+ char * Text = 0;
+
+ // Set up filename
+
+ if (BPQDirectory[0] == 0)
+ {
+ strcpy(FN,"APRSMsgs.dat");
+ }
+ else
+ {
+ strcpy(FN,BPQDirectory);
+ strcat(FN,"/");
+ strcat(FN,"APRSMsgs.dat");
+ }
+
+ if ((file = fopen(FN, "r")) == NULL)
+ return ;
+
+ while (fgets(Line, 512, file))
+ {
+ Stamp = Line;
+ From = strlop(Stamp, ' ');
+ To = strlop(From, ',');
+ Seq = strlop(To, ',');
+ Time = strlop(Seq, ',');
+ Text = strlop(Time, ',');
+
+ if (Stamp && From && To && Seq && Time && Text)
+ {
+ Message = APRSGetMessageBuffer();
+
+ if (Message == NULL)
+ break;
+
+ memset(Message, 0, sizeof(struct APRSMESSAGE));
+
+ strcpy(Message->FromCall, From);
+ strcpy(Message->ToCall, To);
+ strcpy(Message->Seq, Seq);
+ strcpy(Message->Time, Time);
+ strcpy(Message->Text, Text);
+
+ ptr = SMEM->Messages;
+
+ if (ptr == NULL)
+ {
+ SMEM->Messages = Message;
+ }
+ else
+ {
+ while(ptr->Next)
+ {
+ ptr = ptr->Next;
+ }
+ ptr->Next = Message;
+ }
+
+ }
+ }
+ fclose(file);
+}
diff --git a/APRSCode.c.rej b/APRSCode.c.rej
new file mode 100644
index 0000000..1137117
--- /dev/null
+++ b/APRSCode.c.rej
@@ -0,0 +1,139 @@
+--- APRSCode.c
++++ APRSCode.c
+@@ -3674,7 +3674,7 @@
+ if (ptr1)
+ *ptr1 = 0;
+
+-// Debugprintf("Duplicate Message supressed %s", Msg);
++// Debugprintf("Duplicate Message suppressed %s", Msg);
+ return TRUE; // Duplicate
+ }
+ }
+--- BPQChat.rc
++++ BPQChat.rc
+@@ -162,7 +162,7 @@
+ WS_VSCROLL
+ DEFPUSHBUTTON "Save Welcome Message",SAVEWELCOME,140,296,91,14,
+ BS_CENTER | BS_VCENTER
+- LTEXT " If the node is not directly connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT",
++ LTEXT " If the node is not directly connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands separated by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT",
+ IDC_STATIC,9,52,355,24
+ END
+
+--- BPQMail.rc
++++ BPQMail.rc
+@@ -1045,7 +1045,7 @@
+ CONTROL "Delete Log and Message Files to Recycle Bin",
+ IDC_DELETETORECYCLE,"Button",BS_AUTOCHECKBOX |
+ BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,5,142,115,20
+- CONTROL "Supress Mailing of Housekeeping Results",
++ CONTROL "Suppress Mailing of Housekeeping Results",
+ IDC_MAINTNOMAIL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT |
+ BS_MULTILINE | WS_TABSTOP,5,182,115,20
+ CONTROL "Generate Traffic Report",IDC_MAINTTRAFFIC,"Button",
+--- HanksRT.c
++++ HanksRT.c
+@@ -1186,7 +1186,7 @@
+ // Duplicate, so discard, but save time
+
+ DupInfo[i].DupTime = Now;
+- Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s supressed", Call, Msg);
++ Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s suppressed", Call, Msg);
+
+ return TRUE; // Duplicate
+ }
+--- RigControl.c
++++ RigControl.c
+@@ -8385,7 +8385,7 @@
+
+ switch (Msg[0])
+ {
+- case 'f': // Get Freqency
++ case 'f': // Get Frequency
+
+ HLGetFreq(Sock, RIG, sep);
+ return 0;
+--- UZ7HODrv.c
++++ UZ7HODrv.c
+@@ -374,7 +374,7 @@
+ {
+ // Read Freq
+
+- buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Freqency %d\r", AGW->CenterFreq);
++ buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Frequency %d\r", AGW->CenterFreq);
+ return 1;
+ }
+
+@@ -382,7 +382,7 @@
+
+ if (AGW->CenterFreq == 0)
+ {
+- buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Invalid Modem Freqency\r");
++ buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Invalid Modem Frequency\r");
+ return 1;
+ }
+
+--- WinRPRHelper.c
++++ WinRPRHelper.c
+@@ -111,7 +111,7 @@
+
+ if (argc < 3)
+ {
+- printf ("Missing paramters - you need COM port and IP Address and rigctl port of BPQ, eg \r\n"
++ printf ("Missing parameters - you need COM port and IP Address and rigctl port of BPQ, eg \r\n"
+ " WinRPRHelper com10 192.168.1.64:4532\r\n\r\n"
+ "Press any key to exit\r\n");
+
+--- config.c
++++ config.c
+@@ -649,7 +649,7 @@
+ if (LOCATOR[0] == 0 && LocSpecified == 0 && RFOnly == 0)
+ {
+ Consoleprintf("");
+- Consoleprintf("Please enter a LOCATOR statment in your BPQ32.cfg");
++ Consoleprintf("Please enter a LOCATOR statement in your BPQ32.cfg");
+ Consoleprintf("If you really don't want to be on the Node Map you can enter LOCATOR=NONE");
+ Consoleprintf("");
+
+--- kiss.c
++++ kiss.c
+@@ -1485,7 +1485,7 @@
+ }
+ }
+ else
+- Debugprintf("Polled KISS - response from wrong address - Polled %d Reponse %d",
++ Debugprintf("Polled KISS - response from wrong address - Polled %d Response %d",
+ KISS->POLLPOINTER->OURCTRL, (Port->RXMSG[0] & 0xf0));
+
+ goto SeeifMore; // SEE IF ANYTHING ELSE
+--- templatedefs.c
++++ templatedefs.c
+@@ -1165,7 +1165,7 @@
+ "Send Non-delivery Notifications \r\n"
+ "for P and T messages \r\n"
+ " \r\n"
+- "Supress Mailing of \r\n"
++ "Suppress Mailing of \r\n"
+ "Housekeeping Result
\r\n"
+ "Generate Traffic Report
\r\n"
+ "\r\n"
+@@ -1454,7 +1454,7 @@
+ "
The Nodes to link to box defines which other Chat Nodes should be connected to, or from which "
+ "connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. If the node is not directly "
+ "connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands "
+- "seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT"
++ "separated by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT"
+
+ "
The Callsign of the Chat Node is not defined here - it is obtained from the bpq32.cfg APPLICATION line corresponding to the Chat Appl Number. \r\n"
+ "
The Nodes to link to box defines which other Chat Nodes should be connected to, or from which "
"connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. If the node is not directly "
"connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands "
- "seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT"
+ "separared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT"
"
The Callsign of the Chat Node is not defined here - it is obtained from the bpq32.cfg APPLICATION line corresponding to the Chat Appl Number. \r\n"
"