9224 lines
200 KiB
C
9224 lines
200 KiB
C
|
/*
|
|||
|
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 <stdio.h>
|
|||
|
#include "CHeaders.h"
|
|||
|
#include "bpq32.h"
|
|||
|
#include <time.h>
|
|||
|
#include "kernelresource.h"
|
|||
|
|
|||
|
#include "tncinfo.h"
|
|||
|
|
|||
|
#include "bpqaprs.h"
|
|||
|
|
|||
|
#ifndef WIN32
|
|||
|
|
|||
|
#include <unistd.h>
|
|||
|
#include <sys/mman.h>
|
|||
|
#include <sys/un.h>
|
|||
|
|
|||
|
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 <EFBFBD>0000.00N\00000.00W.<EFBFBD>
|
|||
|
*/
|
|||
|
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 <math.h>
|
|||
|
|
|||
|
#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 <dirent.h>
|
|||
|
|
|||
|
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, "<IGATE,MSG_CNT=%d,LOC_CNT=%d", MessageCount , CountLocalStations());
|
|||
|
|
|||
|
for (Port = 1; Port <= MaxBPQPortNo; Port++)
|
|||
|
{
|
|||
|
if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined
|
|||
|
{
|
|||
|
memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7);
|
|||
|
Msg.DEST[6] |= 0x80; // set Command Bit
|
|||
|
|
|||
|
Send_AX_Datagram(&Msg, Len + 2, Port);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Len = sprintf(Msg.L2DATA, "%s>%s,TCPIP*:<IGATE,MSG_CNT=%d,LOC_CNT=%d\r\n", APRSCall, APRSDest, MessageCount, CountLocalStations());
|
|||
|
ISSend(sock, Msg.L2DATA, Len, 0);
|
|||
|
// Debugprintf(">%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&R<NDEp/ B>io94sg
|
|||
|
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 <EFBFBD> d <EFBFBD> 189
|
|||
|
(i.e. the longitude is in the range 100<EFBFBD>109 degrees).
|
|||
|
4. or, subtract 190 if 190 <EFBFBD> d <EFBFBD> 199.
|
|||
|
(i.e. the longitude is in the range 0<EFBFBD>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 <20> 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 <EFBFBD> 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)
|
|||
|
|
|||
|
//<td><a href="find.cgi?call=EI7IG-1">EI7IG-1</a></td>
|
|||
|
//<td><a href="find.cgi?call=G7TKK-1">G7TKK-1</a></td>
|
|||
|
//<td><a href="find.cgi?call=GB7GL-B">GB7GL-B</a></td>
|
|||
|
//<td><a href="find.cgi?call=GM1TCN">GM1TCN</a></td>
|
|||
|
//<td><a href="find.cgi?call=GM8BPQ">GM8BPQ</a></td>
|
|||
|
//<td><a href="find.cgi?call=GM8BPQ-14">GM8BPQ-14</a></td>
|
|||
|
//<td><a href="find.cgi?call=LA2VPA-9">LA2VPA-9</a></td>
|
|||
|
//<td><a href="find.cgi?call=LA3FIA-10">LA3FIA-10</a></td></tr><tr>
|
|||
|
//<td><a href="find.cgi?call=LA6JF-2">LA6JF-2</a></td><td><a href="find.cgi?call=LD4ST">LD4ST</a></td><td><a href="find.cgi?call=M0CHK-7">M0CHK-7</a></td><td><a href="find.cgi?call=M0OZH-7">M0OZH-7</a></td><td><a href="find.cgi?call=MB7UFO-1">MB7UFO-1</a></td><td><a href="find.cgi?call=MB7UN">MB7UN</a></td><td><a href="find.cgi?call=MM0DXE-15">MM0DXE-15</a></td><td><a href="find.cgi?call=PA2AYX-9">PA2AYX-9</a></td></tr><tr>
|
|||
|
//<td><a href="find.cgi?call=PA3AQW-5">PA3AQW-5</a></td><td><a href="find.cgi?call=PD1C">PD1C</a></td><td><a href="find.cgi?call=PD5LWD-2">PD5LWD-2</a></td><td><a href="find.cgi?call=PI1ECO">PI1ECO</a></td></tr>
|
|||
|
|
|||
|
|
|||
|
char * DoSummaryLine(struct STATIONRECORD * ptr, int n, int Width)
|
|||
|
{
|
|||
|
static char Line2[80];
|
|||
|
int x;
|
|||
|
char XCall[256];
|
|||
|
char * ptr1 = ptr->Callsign;
|
|||
|
char * ptr2 = XCall;
|
|||
|
|
|||
|
// Object Names can contain spaces
|
|||
|
|
|||
|
while(*ptr1)
|
|||
|
{
|
|||
|
if (*ptr1 == ' ')
|
|||
|
{
|
|||
|
memcpy(ptr2, "%20", 3);
|
|||
|
ptr2 += 3;
|
|||
|
}
|
|||
|
else
|
|||
|
*(ptr2++) = *ptr1;
|
|||
|
|
|||
|
ptr1++;
|
|||
|
}
|
|||
|
|
|||
|
*ptr2 = 0;
|
|||
|
|
|||
|
|
|||
|
// Object Names can contain spaces
|
|||
|
|
|||
|
|
|||
|
sprintf(Line2, "<td><a href=""find.cgi?call=%s"">%s</a></td>",
|
|||
|
XCall, ptr->Callsign);
|
|||
|
|
|||
|
x = ++n/Width;
|
|||
|
x = x * Width;
|
|||
|
|
|||
|
if (x == n)
|
|||
|
strcat(Line2, "</tr><tr>");
|
|||
|
|
|||
|
return Line2;
|
|||
|
}
|
|||
|
|
|||
|
char * DoDetailLine(struct STATIONRECORD * ptr, BOOL LocalTime, BOOL KM)
|
|||
|
{
|
|||
|
static char Line[512];
|
|||
|
double Lat = ptr->Lat;
|
|||
|
double Lon = ptr->Lon;
|
|||
|
char NS='N', EW='E';
|
|||
|
|
|||
|
char LatString[20], LongString[20], DistString[20], BearingString[20];
|
|||
|
int Degrees;
|
|||
|
double Minutes;
|
|||
|
char Time[80];
|
|||
|
struct tm * TM;
|
|||
|
char XCall[256];
|
|||
|
|
|||
|
char * ptr1 = ptr->Callsign;
|
|||
|
char * ptr2 = XCall;
|
|||
|
|
|||
|
// Object Names can contain spaces
|
|||
|
|
|||
|
while(*ptr1)
|
|||
|
{
|
|||
|
if (*ptr1 == ' ')
|
|||
|
{
|
|||
|
memcpy(ptr2, "%20", 3);
|
|||
|
ptr2 += 3;
|
|||
|
}
|
|||
|
else
|
|||
|
*(ptr2++) = *ptr1;
|
|||
|
|
|||
|
ptr1++;
|
|||
|
}
|
|||
|
|
|||
|
*ptr2 = 0;
|
|||
|
|
|||
|
|
|||
|
// if (ptr->ObjState == '_') // Killed Object
|
|||
|
// return;
|
|||
|
|
|||
|
if (LocalTime)
|
|||
|
TM = localtime(&ptr->TimeLastUpdated);
|
|||
|
else
|
|||
|
TM = gmtime(&ptr->TimeLastUpdated);
|
|||
|
|
|||
|
sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec);
|
|||
|
|
|||
|
if (ptr->Lat < 0)
|
|||
|
{
|
|||
|
NS = 'S';
|
|||
|
Lat=-Lat;
|
|||
|
}
|
|||
|
if (Lon < 0)
|
|||
|
{
|
|||
|
EW = 'W';
|
|||
|
Lon=-Lon;
|
|||
|
}
|
|||
|
|
|||
|
#pragma warning(push)
|
|||
|
#pragma warning(disable:4244)
|
|||
|
|
|||
|
Degrees = Lat;
|
|||
|
Minutes = Lat * 60.0 - (60 * Degrees);
|
|||
|
|
|||
|
sprintf(LatString,"%2d<32>%05.2f'%c", Degrees, Minutes, NS);
|
|||
|
|
|||
|
Degrees = Lon;
|
|||
|
|
|||
|
#pragma warning(pop)
|
|||
|
|
|||
|
Minutes = Lon * 60 - 60 * Degrees;
|
|||
|
|
|||
|
sprintf(LongString, "%3d<33>%05.2f'%c",Degrees, Minutes, EW);
|
|||
|
|
|||
|
sprintf(DistString, "%6.1f", myDistance(ptr->Lat, ptr->Lon, KM));
|
|||
|
sprintf(BearingString, "%3.0f", myBearing(ptr->Lat, ptr->Lon));
|
|||
|
|
|||
|
sprintf(Line, "<tr><td align=""left""><a href=""find.cgi?call=%s""> %s%s</a></td><td align=""left"">%s</td><td align=""center"">%s %s</td><td align=""right"">%s</td><td align=""right"">%s</td><td align=""left"">%s</td></tr>\r\n",
|
|||
|
XCall, ptr->Callsign,
|
|||
|
(strchr(ptr->Path, '*'))? "*": "", &SymbolText[ptr->iconRow << 4 | ptr->iconCol][0], LatString, LongString, DistString, BearingString, Time);
|
|||
|
|
|||
|
return Line;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int CompareFN(const void *a, const void *b)
|
|||
|
{
|
|||
|
const struct STATIONRECORD * x = a;
|
|||
|
const struct STATIONRECORD * y = b;
|
|||
|
|
|||
|
x = x->Next;
|
|||
|
y = y->Next;
|
|||
|
|
|||
|
return strcmp(x->Callsign, y->Callsign);
|
|||
|
|
|||
|
/* strcmp functions works exactly as expected from
|
|||
|
comparison function */
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
char * CreateStationList(BOOL RFOnly, BOOL WX, BOOL Mobile, char Objects, int * Count, char * Param, BOOL KM)
|
|||
|
{
|
|||
|
char * Line = malloc(100000);
|
|||
|
struct STATIONRECORD * ptr = *StationRecords;
|
|||
|
int n = 0, i;
|
|||
|
struct STATIONRECORD * List[1000];
|
|||
|
int TableWidth = 8;
|
|||
|
BOOL LocalTime = DefaultLocalTime;
|
|||
|
|
|||
|
if (strstr(Param, "time=local"))
|
|||
|
LocalTime = TRUE;
|
|||
|
else if (strstr(Param, "time=utc"))
|
|||
|
LocalTime = FALSE;
|
|||
|
|
|||
|
Line[0] = 0;
|
|||
|
|
|||
|
if (Param && Param[0])
|
|||
|
{
|
|||
|
char * Key, *Context;
|
|||
|
|
|||
|
Key = strtok_s(Param, "=", &Context);
|
|||
|
|
|||
|
if (_stricmp(Key, "width") == 0)
|
|||
|
TableWidth = atoi(Context);
|
|||
|
|
|||
|
if (TableWidth == 0)
|
|||
|
TableWidth = 8;
|
|||
|
}
|
|||
|
|
|||
|
// Build list of calls
|
|||
|
|
|||
|
while (ptr)
|
|||
|
{
|
|||
|
if (ptr->ObjState == Objects && ptr->Lat != 0.0 && ptr->Lon != 0.0)
|
|||
|
{
|
|||
|
if ((WX && (ptr->LastWXPacket[0] == 0)) || (RFOnly && (ptr->LastPort == 0)) ||
|
|||
|
(Mobile && ((ptr->Speed < 0.1) || ptr->LastWXPacket[0] != 0)))
|
|||
|
{
|
|||
|
ptr = ptr->Next;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
List[n++] = ptr;
|
|||
|
|
|||
|
if (n > 999)
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
ptr = ptr->Next;
|
|||
|
}
|
|||
|
|
|||
|
if (n > 1)
|
|||
|
qsort(List, n, sizeof(void *), CompareFN);
|
|||
|
|
|||
|
for (i = 0; i < n; i++)
|
|||
|
{
|
|||
|
if (RFOnly)
|
|||
|
strcat(Line, DoDetailLine(List[i], LocalTime, KM));
|
|||
|
else
|
|||
|
strcat(Line, DoSummaryLine(List[i], i, TableWidth));
|
|||
|
}
|
|||
|
|
|||
|
*Count = n;
|
|||
|
|
|||
|
return Line;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
char * APRSLookupKey(struct APRSConnectionInfo * sockptr, char * Key, BOOL KM)
|
|||
|
{
|
|||
|
struct STATIONRECORD * stn = sockptr->SelCall;
|
|||
|
|
|||
|
if (strcmp(Key, "##MY_CALLSIGN##") == 0)
|
|||
|
return _strdup(LoppedAPRSCall);
|
|||
|
|
|||
|
if (strcmp(Key, "##CALLSIGN##") == 0)
|
|||
|
return _strdup(sockptr->Callsign);
|
|||
|
|
|||
|
if (strcmp(Key, "##CALLSIGN_NOSSID##") == 0)
|
|||
|
{
|
|||
|
char * Call = _strdup(sockptr->Callsign);
|
|||
|
char * ptr = strchr(Call, '-');
|
|||
|
if (ptr)
|
|||
|
*ptr = 0;
|
|||
|
return Call;
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##MY_WX_CALLSIGN##") == 0)
|
|||
|
return _strdup(LoppedAPRSCall);
|
|||
|
|
|||
|
if (strcmp(Key, "##MY_BEACON_COMMENT##") == 0)
|
|||
|
return _strdup(StatusMsg);
|
|||
|
|
|||
|
if (strcmp(Key, "##MY_WX_BEACON_COMMENT##") == 0)
|
|||
|
return _strdup(WXComment);
|
|||
|
|
|||
|
if (strcmp(Key, "##MILES_KM##") == 0)
|
|||
|
if (KM)
|
|||
|
return _strdup("KM");
|
|||
|
else
|
|||
|
return _strdup("Miles");
|
|||
|
|
|||
|
if (strcmp(Key, "##EXPIRE_TIME##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
sprintf(val, "%d", ExpireTime);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##LOCATION##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
double Lat = sockptr->SelCall->Lat;
|
|||
|
double Lon = sockptr->SelCall->Lon;
|
|||
|
char NS='N', EW='E';
|
|||
|
char LatString[20];
|
|||
|
int Degrees;
|
|||
|
double Minutes;
|
|||
|
|
|||
|
if (Lat < 0)
|
|||
|
{
|
|||
|
NS = 'S';
|
|||
|
Lat=-Lat;
|
|||
|
}
|
|||
|
if (Lon < 0)
|
|||
|
{
|
|||
|
EW = 'W';
|
|||
|
Lon=-Lon;
|
|||
|
}
|
|||
|
|
|||
|
#pragma warning(push)
|
|||
|
#pragma warning(disable:4244)
|
|||
|
|
|||
|
Degrees = Lat;
|
|||
|
Minutes = Lat * 60.0 - (60 * Degrees);
|
|||
|
|
|||
|
sprintf(LatString,"%2d<32>%05.2f'%c",Degrees, Minutes, NS);
|
|||
|
|
|||
|
Degrees = Lon;
|
|||
|
|
|||
|
#pragma warning(pop)
|
|||
|
|
|||
|
Minutes = Lon * 60 - 60 * Degrees;
|
|||
|
|
|||
|
sprintf(val,"%s %3d<33>%05.2f'%c", LatString, Degrees, Minutes, EW);
|
|||
|
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##LOCDDMMSS##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
double Lat = sockptr->SelCall->Lat;
|
|||
|
double Lon = sockptr->SelCall->Lon;
|
|||
|
char NS='N', EW='E';
|
|||
|
char LatString[20];
|
|||
|
int Degrees;
|
|||
|
double Minutes;
|
|||
|
|
|||
|
// 48.45.18N, 002.18.37E
|
|||
|
|
|||
|
if (Lat < 0)
|
|||
|
{
|
|||
|
NS = 'S';
|
|||
|
Lat=-Lat;
|
|||
|
}
|
|||
|
if (Lon < 0)
|
|||
|
{
|
|||
|
EW = 'W';
|
|||
|
Lon=-Lon;
|
|||
|
}
|
|||
|
|
|||
|
#pragma warning(push)
|
|||
|
#pragma warning(disable:4244)
|
|||
|
|
|||
|
Degrees = Lat;
|
|||
|
Minutes = Lat * 60.0 - (60 * Degrees);
|
|||
|
// IntMins = Minutes;
|
|||
|
// Seconds = Minutes * 60.0 - (60 * IntMins);
|
|||
|
|
|||
|
sprintf(LatString,"%2d.%05.2f%c",Degrees, Minutes, NS);
|
|||
|
|
|||
|
Degrees = Lon;
|
|||
|
Minutes = Lon * 60.0 - 60 * Degrees;
|
|||
|
// IntMins = Minutes;
|
|||
|
// Seconds = Minutes * 60.0 - (60 * IntMins);
|
|||
|
|
|||
|
#pragma warning(pop)
|
|||
|
|
|||
|
sprintf(val,"%s, %03d.%05.2f%c", LatString, Degrees, Minutes, EW);
|
|||
|
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##LATLON##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
double Lat = sockptr->SelCall->Lat;
|
|||
|
double Lon = sockptr->SelCall->Lon;
|
|||
|
char NS='N', EW='E';
|
|||
|
|
|||
|
// 58.5, -6.2
|
|||
|
|
|||
|
sprintf(val,"%f, %f", Lat, Lon);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##STATUS_TEXT##") == 0)
|
|||
|
return _strdup(stn->Status);
|
|||
|
|
|||
|
if (strcmp(Key, "##LASTPACKET##") == 0)
|
|||
|
return _strdup(stn->LastPacket);
|
|||
|
|
|||
|
|
|||
|
if (strcmp(Key, "##LAST_HEARD##") == 0)
|
|||
|
{
|
|||
|
char Time[80];
|
|||
|
struct tm * TM;
|
|||
|
time_t Age = time(NULL) - stn->TimeLastUpdated;
|
|||
|
|
|||
|
TM = gmtime(&Age);
|
|||
|
|
|||
|
sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec);
|
|||
|
|
|||
|
return _strdup(Time);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##FRAME_HEADER##") == 0)
|
|||
|
return _strdup(stn->Path);
|
|||
|
|
|||
|
if (strcmp(Key, "##FRAME_INFO##") == 0)
|
|||
|
return _strdup(stn->LastWXPacket);
|
|||
|
|
|||
|
if (strcmp(Key, "##BEARING##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%03.0f", myBearing(sockptr->SelCall->Lat, sockptr->SelCall->Lon));
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##COURSE##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%03.0f", stn->Course);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##SPEED_MPH##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%5.1f", stn->Speed);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##DISTANCE##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%5.1f", myDistance(sockptr->SelCall->Lat, sockptr->SelCall->Lon, KM));
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (strcmp(Key, "##WIND_DIRECTION##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%03d", sockptr->WindDirn);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##WIND_SPEED_MPH##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%d", sockptr->WindSpeed);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##WIND_GUST_MPH##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%d", sockptr->WindGust);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##TEMPERATURE_F##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%d", sockptr->Temp);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##HUMIDITY##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%d", sockptr->Humidity);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##PRESSURE_HPA##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%05.1f", sockptr->Pressure /10.0);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##RAIN_TODAY_IN##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%5.2f", sockptr->RainToday /100.0);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (strcmp(Key, "##RAIN_24_IN##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%5.2f", sockptr->RainLastDay /100.0);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (strcmp(Key, "##RAIN_HOUR_IN##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
|
|||
|
sprintf(val, "%5.2f", sockptr->RainLastHour /100.0);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##MAP_LAT_LON##") == 0)
|
|||
|
{
|
|||
|
char val[256];
|
|||
|
|
|||
|
sprintf(val, "%f,%f", stn->Lat, stn->Lon);
|
|||
|
return _strdup(val);
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(Key, "##SYMBOL_DESCRIPTION##") == 0)
|
|||
|
return _strdup(&SymbolText[stn->iconRow << 4 | stn->iconCol][0]);
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
##WIND_SPEED_MS## - wind speed metres/sec
|
|||
|
##WIND_SPEED_KMH## - wind speed km/hour
|
|||
|
##WIND_GUST_MPH## - wind gust miles/hour
|
|||
|
##WIND_GUST_MS## - wind gust metres/sec
|
|||
|
##WIND_GUST_KMH## - wind gust km/hour
|
|||
|
##WIND_CHILL_F## - wind chill F
|
|||
|
##WIND_CHILL_C## - wind chill C
|
|||
|
##TEMPERATURE_C## - temperature C
|
|||
|
##DEWPOINT_F## - dew point temperature F
|
|||
|
##DEWPOINT_C## - dew point temperature C
|
|||
|
##PRESSURE_IN## - pressure inches of mercury
|
|||
|
##PRESSURE_HPA## - pressure hPa (mb)
|
|||
|
##RAIN_HOUR_MM## - rain in last hour mm
|
|||
|
##RAIN_TODAY_MM## - rain today mm
|
|||
|
##RAIN_24_MM## - rain in last 24 hours mm
|
|||
|
##FRAME_HEADER## - frame header of the last posit heard from the station
|
|||
|
##FRAME_INFO## - information field of the last posit heard from the station
|
|||
|
##MAP_LARGE_SCALE##" - URL of a suitable large scale map on www.vicinity.com
|
|||
|
##MEDIUM_LARGE_SCALE##" - URL of a suitable medium scale map on www.vicinity.com
|
|||
|
##MAP_SMALL_SCALE##" - URL of a suitable small scale map on www.vicinity.com
|
|||
|
##MY_LOCATION## - 'Latitude', 'Longitude' in 'Station Setup'
|
|||
|
##MY_STATUS_TEXT## - status text configured in 'Status Text'
|
|||
|
##MY_SYMBOL_DESCRIPTION## - 'Symbol' that would be shown for our station in 'Station List'
|
|||
|
##HIT_COUNTER## - The number of times the page has been accessed
|
|||
|
##DOCUMENT_LAST_CHANGED## - The date/time the page was last edited
|
|||
|
|
|||
|
##FRAME_HEADER## - frame header of the last posit heard from the station
|
|||
|
##FRAME_INFO## - information field of the last posit heard from the station
|
|||
|
|
|||
|
*/
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
VOID APRSProcessSpecialPage(struct APRSConnectionInfo * sockptr, char * Buffer, int FileSize, char * StationTable, int Count, BOOL WX, BOOL KM)
|
|||
|
{
|
|||
|
// replaces ##xxx### constructs with the requested data
|
|||
|
|
|||
|
char * NewMessage = malloc(250000);
|
|||
|
char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage;
|
|||
|
size_t PrevLen;
|
|||
|
size_t BytesLeft = FileSize;
|
|||
|
size_t NewFileSize = FileSize;
|
|||
|
char * StripPtr = ptr1;
|
|||
|
int HeaderLen;
|
|||
|
char Header[256];
|
|||
|
|
|||
|
if (WX && sockptr->SelCall && sockptr->SelCall->LastWXPacket)
|
|||
|
{
|
|||
|
DecodeWXReport(sockptr, sockptr->SelCall->LastWXPacket);
|
|||
|
}
|
|||
|
|
|||
|
// strip comments blocks
|
|||
|
|
|||
|
while (ptr4 = strstr(ptr1, "<!--"))
|
|||
|
{
|
|||
|
ptr2 = strstr(ptr4, "-->");
|
|||
|
if (ptr2)
|
|||
|
{
|
|||
|
PrevLen = (ptr4 - ptr1);
|
|||
|
memcpy(StripPtr, ptr1, PrevLen);
|
|||
|
StripPtr += PrevLen;
|
|||
|
ptr1 = ptr2 + 3;
|
|||
|
BytesLeft = FileSize - (ptr1 - Buffer);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
memcpy(StripPtr, ptr1, BytesLeft);
|
|||
|
StripPtr += BytesLeft;
|
|||
|
|
|||
|
BytesLeft = StripPtr - Buffer;
|
|||
|
|
|||
|
FileSize = (int)BytesLeft;
|
|||
|
NewFileSize = FileSize;
|
|||
|
ptr1 = Buffer;
|
|||
|
ptr1[FileSize] = 0;
|
|||
|
|
|||
|
loop:
|
|||
|
ptr2 = strstr(ptr1, "##");
|
|||
|
|
|||
|
if (ptr2)
|
|||
|
{
|
|||
|
PrevLen = (ptr2 - ptr1); // Bytes before special text
|
|||
|
|
|||
|
ptr3 = strstr(ptr2+2, "##");
|
|||
|
|
|||
|
if (ptr3)
|
|||
|
{
|
|||
|
char Key[80] = "";
|
|||
|
size_t KeyLen;
|
|||
|
char * NewText;
|
|||
|
size_t NewTextLen;
|
|||
|
|
|||
|
ptr3 += 2;
|
|||
|
KeyLen = ptr3 - ptr2;
|
|||
|
|
|||
|
if (KeyLen < 80)
|
|||
|
memcpy(Key, ptr2, KeyLen);
|
|||
|
|
|||
|
if (strcmp(Key, "##STATION_TABLE##") == 0)
|
|||
|
{
|
|||
|
NewText = _strdup(StationTable);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (strcmp(Key, "##TABLE_COUNT##") == 0)
|
|||
|
{
|
|||
|
char val[80];
|
|||
|
sprintf(val, "%d", Count);
|
|||
|
NewText = _strdup(val);
|
|||
|
}
|
|||
|
else
|
|||
|
NewText = APRSLookupKey(sockptr, Key, KM);
|
|||
|
}
|
|||
|
|
|||
|
if (NewText)
|
|||
|
{
|
|||
|
NewTextLen = strlen(NewText);
|
|||
|
NewFileSize = NewFileSize + NewTextLen - KeyLen;
|
|||
|
// NewMessage = realloc(NewMessage, NewFileSize);
|
|||
|
|
|||
|
memcpy(NewPtr, ptr1, PrevLen);
|
|||
|
NewPtr += PrevLen;
|
|||
|
memcpy(NewPtr, NewText, NewTextLen);
|
|||
|
NewPtr += NewTextLen;
|
|||
|
|
|||
|
free(NewText);
|
|||
|
NewText = NULL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Key not found, so just leave
|
|||
|
|
|||
|
memcpy(NewPtr, ptr1, PrevLen + KeyLen);
|
|||
|
NewPtr += (PrevLen + KeyLen);
|
|||
|
}
|
|||
|
|
|||
|
ptr1 = ptr3; // Continue scan from here
|
|||
|
BytesLeft = Buffer + FileSize - ptr3;
|
|||
|
}
|
|||
|
else // Unmatched ##
|
|||
|
{
|
|||
|
memcpy(NewPtr, ptr1, PrevLen + 2);
|
|||
|
NewPtr += (PrevLen + 2);
|
|||
|
ptr1 = ptr2 + 2;
|
|||
|
}
|
|||
|
goto loop;
|
|||
|
}
|
|||
|
|
|||
|
// Copy Rest
|
|||
|
|
|||
|
memcpy(NewPtr, ptr1, BytesLeft);
|
|||
|
|
|||
|
HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)NewFileSize);
|
|||
|
send(sockptr->sock, Header, HeaderLen, 0);
|
|||
|
send(sockptr->sock, NewMessage, (int)NewFileSize, 0);
|
|||
|
|
|||
|
free (NewMessage);
|
|||
|
free(StationTable);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID APRSSendMessageFile(struct APRSConnectionInfo * sockptr, char * FN)
|
|||
|
{
|
|||
|
int FileSize = 0;
|
|||
|
char * MsgBytes = 0;
|
|||
|
char * SaveMsgBytes = 0;
|
|||
|
|
|||
|
char MsgFile[MAX_PATH];
|
|||
|
FILE * hFile;
|
|||
|
BOOL Special = FALSE;
|
|||
|
int HeaderLen;
|
|||
|
char Header[256];
|
|||
|
char * Param = 0;
|
|||
|
struct stat STAT;
|
|||
|
int Sent;
|
|||
|
char * ptr;
|
|||
|
|
|||
|
if (strchr(FN, '?'))
|
|||
|
strtok_s(FN, "?", &Param);
|
|||
|
|
|||
|
UndoTransparency(FN);
|
|||
|
|
|||
|
if (strstr(FN, ".."))
|
|||
|
{
|
|||
|
FN[0] = '/';
|
|||
|
FN[1] = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (strcmp(FN, "/") == 0)
|
|||
|
sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, SpecialDocs);
|
|||
|
else
|
|||
|
sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory, APRSDir, SpecialDocs, &FN[5]);
|
|||
|
|
|||
|
hFile = fopen(MsgFile, "rb");
|
|||
|
|
|||
|
if (hFile == NULL)
|
|||
|
{
|
|||
|
// Try normal pages
|
|||
|
|
|||
|
|
|||
|
if (strcmp(FN, "/") == 0)
|
|||
|
sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, HTDocs);
|
|||
|
else
|
|||
|
sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory,APRSDir, HTDocs, &FN[5]);
|
|||
|
|
|||
|
// My standard page set is now hard coded
|
|||
|
|
|||
|
|
|||
|
|
|||
|
MsgBytes = SaveMsgBytes = GetStandardPage(&FN[6], &FileSize);
|
|||
|
|
|||
|
if (MsgBytes)
|
|||
|
{
|
|||
|
if (FileSize == 0)
|
|||
|
FileSize = strlen(MsgBytes);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
hFile = fopen(MsgFile, "rb");
|
|||
|
|
|||
|
if (hFile == NULL)
|
|||
|
{
|
|||
|
HeaderLen = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
|
|||
|
send(sockptr->sock, Header, HeaderLen, 0);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
Special = TRUE;
|
|||
|
|
|||
|
if (FileSize == 0) // Not from internal template
|
|||
|
{
|
|||
|
if (stat(MsgFile, &STAT) == 0)
|
|||
|
FileSize = STAT.st_size;
|
|||
|
|
|||
|
MsgBytes = SaveMsgBytes = malloc(FileSize+1);
|
|||
|
|
|||
|
fread(MsgBytes, 1, FileSize, hFile);
|
|||
|
|
|||
|
fclose(hFile);
|
|||
|
}
|
|||
|
|
|||
|
// if HTML file, look for ##...## substitutions
|
|||
|
|
|||
|
if ((strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" ))
|
|||
|
{
|
|||
|
// Build Station list, depending on URL
|
|||
|
|
|||
|
int Count = 0;
|
|||
|
BOOL RFOnly = !(strstr(_strlwr(FN), "rf") == NULL); // Leaves FN in lower case
|
|||
|
BOOL WX =!(strstr(FN, "wx") == NULL);
|
|||
|
BOOL Mobile = !(strstr(FN, "mobile") == NULL);
|
|||
|
char Objects = (strstr(FN, "obj"))? '*' :0;
|
|||
|
char * StationList;
|
|||
|
BOOL KM = DefaultDistKM;
|
|||
|
|
|||
|
if (Param == 0)
|
|||
|
Param ="";
|
|||
|
else
|
|||
|
_strlwr(Param);
|
|||
|
|
|||
|
if (strstr(Param, "dist=km"))
|
|||
|
KM = TRUE;
|
|||
|
else if (strstr(Param, "dist=miles"))
|
|||
|
KM = FALSE;
|
|||
|
|
|||
|
|
|||
|
StationList = CreateStationList(RFOnly, WX, Mobile, Objects, &Count, Param, KM);
|
|||
|
|
|||
|
APRSProcessSpecialPage(sockptr, MsgBytes, FileSize, StationList, Count, WX, KM);
|
|||
|
free (MsgBytes);
|
|||
|
return; // ProcessSpecial has sent the reply
|
|||
|
}
|
|||
|
|
|||
|
ptr = FN;
|
|||
|
|
|||
|
while (strchr(ptr, '.'))
|
|||
|
{
|
|||
|
ptr = strchr(ptr, '.');
|
|||
|
++ptr;
|
|||
|
}
|
|||
|
|
|||
|
if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0)
|
|||
|
HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: image\r\n\r\n", FileSize);
|
|||
|
else
|
|||
|
HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", FileSize);
|
|||
|
|
|||
|
send(sockptr->sock, Header, HeaderLen, 0);
|
|||
|
|
|||
|
Sent = send(sockptr->sock, MsgBytes, FileSize, 0);
|
|||
|
// printf("Send %d %d\n", FileSize, Sent);
|
|||
|
|
|||
|
while (Sent < FileSize)
|
|||
|
{
|
|||
|
FileSize -= Sent;
|
|||
|
MsgBytes += Sent;
|
|||
|
Sent = send(sockptr->sock, MsgBytes, FileSize, 0);
|
|||
|
// printf("Send %d %d\n", FileSize, Sent);
|
|||
|
if (Sent == -1)
|
|||
|
{
|
|||
|
Sleep(10);
|
|||
|
Sent = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
free (SaveMsgBytes);
|
|||
|
}
|
|||
|
|
|||
|
char WebHeader[] = "<HTML><HEAD><meta http-equiv=\"expires\" content=\"-1\">"
|
|||
|
"<meta http-equiv=\"pragma\" content=\"no-cache\">"
|
|||
|
"<TITLE>APRS Messaging</TITLE></HEAD>"
|
|||
|
"<BODY alink=\"#008000\" bgcolor=\"#F5F5DC\" link=\"#0000FF\" vlink=\"#000080\" background=/background.jpg>"
|
|||
|
"<table align=center border=2 cellpadding=2 cellspacing=2 bgcolor=white><tr>"
|
|||
|
"<td align=center><a href=/aprs.html>APRS Map</a></td>"
|
|||
|
"<td align=center><a href=/aprs/msgs>Received Messages</a></td>"
|
|||
|
"<td align=center><a href=/aprs/txmsgs>Sent Messages</a></td>"
|
|||
|
"<td align=center><a href=/aprs/msgs/entermsg>Send Message</a></td>"
|
|||
|
"<td align=center><a href=/aprs/all.html>Station Pages</a></td>"
|
|||
|
"<td align=center><a href=/Node/NodeMenu.html>Return to Node Pages</a></td>"
|
|||
|
"</tr></table>"
|
|||
|
"<center><h2>%s's Messages</h2><TABLE BORDER=\"3\" CELLSPACING=\"2\" CELLPADDING=\"1\">"
|
|||
|
"<tr><td>From</td><td>To</td><td>Seq</td><td>Time</td><td> </td><td>Message</td></tr>";
|
|||
|
|
|||
|
char WebTXHeader[] = "<HTML><HEAD><meta http-equiv=\"expires\" content=\"-1\">"
|
|||
|
"<meta http-equiv=\"pragma\" content=\"no-cache\">"
|
|||
|
"<TITLE>APRS Messaging</TITLE></HEAD>"
|
|||
|
"<BODY alink=\"#008000\" bgcolor=\"#F5F5DC\" link=\"#0000FF\" vlink=\"#000080\" background=/background.jpg>"
|
|||
|
"<table align=center border=2 cellpadding=2 cellspacing=2 bgcolor=white><tr>"
|
|||
|
"<td align=center><a href=/aprs.html>APRS Map</a></td>"
|
|||
|
"<td align=center><a href=/aprs/msgs>Received Messages</a></td>"
|
|||
|
"<td align=center><a href=/aprs/txmsgs>Sent Messages</a></td>"
|
|||
|
"<td align=center><a href=/aprs/msgs/entermsg>Send Message</a></td>"
|
|||
|
"<td align=center><a href=/aprs/all.html>Station Pages</a></td>"
|
|||
|
"<td align=center><a href=/Node/NodeMenu.html>Return to Node Pages</a></td>"
|
|||
|
"</tr></table>"
|
|||
|
"<center><h2>Message Sent by %s</h2><TABLE BORDER=\"3\" CELLSPACING=\"2\" CELLPADDING=\"1\">"
|
|||
|
"<tr><td>To</td><td>Seq</td><td>Time</td><td>State</td><td>message</td></tr>";
|
|||
|
|
|||
|
char WebLine[] = "<tr bgcolor=\"#ffcccc\"><td>%s </td><td> %s </td><td> %s </td><td> %s</td><td>"
|
|||
|
"<a href=\"entermsg?tocall=%s&fromcall=%s\">Reply</a></td><td> %s</td></tr>";
|
|||
|
|
|||
|
char WebTXLine[] = "<tr bgcolor=\"#ffcccc\">"
|
|||
|
"<td>%s </td><td> %s </td><td> %s </td><td> %s </td><td> %s</td></tr>";
|
|||
|
|
|||
|
|
|||
|
char WebTrailer[] = "</table></BODY></HTML>";
|
|||
|
|
|||
|
char SendMsgPage[] = "<html><head><title>BPQ32 APRS Messaging</title></head><body background=\"/background.jpg\">"
|
|||
|
"<center><h2>APRS Message Input</h1>"
|
|||
|
"<form method=post action=/APRS/Msgs/SendMsg>"
|
|||
|
"<table align=center bgcolor=white>"
|
|||
|
"<tr><td>To</td><td><input type=text name=call tabindex=1 size=10 maxlength=12 value=\"%s\"/></td></tr>"
|
|||
|
"<tr><td>Message</td><td><input type=text name=message tabindex=2 size=80 maxlength=100 /></td></tr></table>"
|
|||
|
"<p align=center><input type=submit value=Submit /><input type=submit value=Cancel name=Cancel /></form>";
|
|||
|
|
|||
|
char APRSIndexPage[] = "<html><head><title>BPQ32 Web Server APRS Pages</title></head>"
|
|||
|
"<body background=/background.jpg><P align=center>"
|
|||
|
"<h2 align=center>BPQ32 APRS Server</h2><P align=center>"
|
|||
|
"<table border=2 cellpadding=2 cellspacing=2 bgcolor=white><tr>"
|
|||
|
"<td align=center><a href=/aprs.html>APRS Map</a></td>"
|
|||
|
"<td align=center><a href=/aprs/msgs>Received Messages</a></td>"
|
|||
|
"<td align=center><a href=/aprs/txmsgs>Sent Messages</a></td>"
|
|||
|
"<td align=center><a href=/aprs/msgs/entermsg>Send Message</a></td>"
|
|||
|
"<td align=center><a href=/aprs/all.html>Station Pages</a></td>"
|
|||
|
"<td align=center><a href=/Node/NodeMenu.html>Return to Node Pages</a></td>"
|
|||
|
"</tr></table>%s</body></html>";
|
|||
|
|
|||
|
extern char Tail[];
|
|||
|
|
|||
|
VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE)
|
|||
|
{
|
|||
|
int InputLen = 0;
|
|||
|
int OutputLen = 0;
|
|||
|
char * URL;
|
|||
|
char * ptr;
|
|||
|
struct APRSConnectionInfo CI;
|
|||
|
struct APRSConnectionInfo * sockptr = &CI;
|
|||
|
char Key[12] = "";
|
|||
|
char OutBuffer[100000];
|
|||
|
char Header[1024];
|
|||
|
int HeaderLen = 0;
|
|||
|
|
|||
|
memset(&CI, 0, sizeof(CI));
|
|||
|
|
|||
|
sockptr->sock = sock;
|
|||
|
|
|||
|
if (memcmp(MsgPtr, "POST" , 3) == 0)
|
|||
|
{
|
|||
|
char * To;
|
|||
|
char * Msg = "";
|
|||
|
|
|||
|
URL = &MsgPtr[5];
|
|||
|
|
|||
|
ptr = strstr(URL, "\r\n\r\n");
|
|||
|
|
|||
|
if (ptr)
|
|||
|
{
|
|||
|
char * param, * context;
|
|||
|
|
|||
|
UndoTransparency(ptr);
|
|||
|
|
|||
|
param = strtok_s(ptr + 4, "&", &context);
|
|||
|
|
|||
|
while (param)
|
|||
|
{
|
|||
|
char * val = strlop(param, '=');
|
|||
|
|
|||
|
if (val)
|
|||
|
{
|
|||
|
if (_stricmp(param, "call") == 0)
|
|||
|
To = _strupr(val);
|
|||
|
else if (_stricmp(param, "message") == 0)
|
|||
|
Msg = val;
|
|||
|
else if (_stricmp(param, "Cancel") == 0)
|
|||
|
{
|
|||
|
|
|||
|
// Return APRS Index Page
|
|||
|
|
|||
|
OutputLen = sprintf(OutBuffer, APRSIndexPage, "<br><br><br><br><h2 align=center>Message Cancelled</h2>");
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
param = strtok_s(NULL,"&", &context);
|
|||
|
}
|
|||
|
|
|||
|
strlop(To, ' ');
|
|||
|
|
|||
|
if (strlen(To) < 2)
|
|||
|
{
|
|||
|
OutputLen = sprintf(OutBuffer, SendMsgPage, To);
|
|||
|
OutputLen += sprintf(&OutBuffer[OutputLen], "<br><br><h2 align=center>Invalid Callsign</h2>");
|
|||
|
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 (Msg[0] == 0)
|
|||
|
{
|
|||
|
OutputLen = sprintf(OutBuffer, SendMsgPage, To);
|
|||
|
OutputLen += sprintf(&OutBuffer[OutputLen], "<br><br><h2 align=center>No Message</h2>");
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
// Send APRS Messsage
|
|||
|
|
|||
|
if (strlen(Msg) > 100)
|
|||
|
Msg[100] = 0;
|
|||
|
|
|||
|
InternalSendAPRSMessage(Msg, To);
|
|||
|
|
|||
|
OutputLen = sprintf(OutBuffer, APRSIndexPage, "<br><br><br><br><h2 align=center>Message Sent</h2>");
|
|||
|
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, "<br><B>Not authorized - please return to Node Menu and sign in</B>");
|
|||
|
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<br>");
|
|||
|
strcat(popup, Msg);
|
|||
|
}
|
|||
|
ptr1 = doHTMLTransparency(ptr->Path);
|
|||
|
sprintf(Msg, "%s<br>", ptr1);
|
|||
|
strcat(popup, Msg);
|
|||
|
free(ptr1);
|
|||
|
ptr1 = doHTMLTransparency(ptr->LastPacket);
|
|||
|
sprintf(Msg, "%s<br>", ptr1);
|
|||
|
strcat(popup, Msg);
|
|||
|
free(ptr1);
|
|||
|
ptr1 = doHTMLTransparency(ptr->Status);
|
|||
|
sprintf(Msg, "%s<br>", 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<br>",
|
|||
|
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<br>",
|
|||
|
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<br>", temp.WindSpeed);
|
|||
|
strcat(popup, Msg);
|
|||
|
|
|||
|
sprintf(Msg, "Wind Gust %d MPH<br>", temp.WindGust);
|
|||
|
strcat(popup, Msg);
|
|||
|
|
|||
|
sprintf(Msg, "Wind Direction %d\xC2\xB0<br>", temp.WindDirn);
|
|||
|
strcat(popup, Msg);
|
|||
|
|
|||
|
sprintf(Msg, "Temperature %d\xC2\xB0 F<br>", temp.Temp);
|
|||
|
strcat(popup, Msg);
|
|||
|
|
|||
|
sprintf(Msg, "Pressure %05.1f<br>", temp.Pressure / 10.0);
|
|||
|
strcat(popup, Msg);
|
|||
|
|
|||
|
sprintf(Msg, "Humidity %d%%<br>", 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<br>", 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);
|
|||
|
}
|