/* Copyright 2001-2022 John Wiseman G8BPQ This file is part of LinBPQ/BPQ32. LinBPQ/BPQ32 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. LinBPQ/BPQ32 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses */ // Module to implement APRS "New Paradigm" Digipeater and APRS-IS Gateway // First Version, November 2011 #pragma data_seg("_BPQDATA") #define _CRT_SECURE_NO_DEPRECATE #include #include "CHeaders.h" #include "bpq32.h" #include #include "kernelresource.h" #include "tncinfo.h" #include "bpqaprs.h" #ifndef WIN32 #include #include #include int sfd; struct sockaddr_un my_addr, peer_addr; socklen_t peer_addr_size; #endif #define MAXAGE 3600 * 12 // 12 Hours #define MAXCALLS 20 // Max Flood, Trace and Digi #define GATETIMELIMIT 40 * 60 // Don't gate to RF if station not heard for this time (40 mins) static BOOL APIENTRY GETSENDNETFRAMEADDR(); static VOID DoSecTimer(); static VOID DoMinTimer(); static int APRSProcessLine(char * buf); static BOOL APRSReadConfigFile(); VOID APRSISThread(void * Report); VOID __cdecl Debugprintf(const char * format, ...); VOID __cdecl Consoleprintf(const char * format, ...); BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); char * strlop(char * buf, char delim); int APRSDecodeFrame(char * msg, char * buffer, time_t Stamp, uint64_t Mask); // Unsemaphored DecodeFrame APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port); BOOL CheckforDups(char * Call, char * Msg, int Len); 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); int FromLOC(char * Locator, double * pLat, double * pLon); 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); extern int SemHeldByAPI; extern int APRSMONDECODE(); extern struct ConsoleInfo MonWindow; extern char VersionString[]; BOOL SaveAPRSMsgs = 0; BOOL LogAPRSIS = FALSE; // All data should be initialised to force into shared segment static char ConfigClassName[]="CONFIG"; extern BPQVECSTRUC * APRSMONVECPTR; extern int MONDECODE(); extern VOID * zalloc(int len); extern BOOL StartMinimized; extern char TextVerstring[]; extern HWND hConsWnd; extern HKEY REGTREE; extern char LOCATOR[80]; extern char LOC[7]; static int SecTimer = 10; static int MinTimer = 60; BOOL APRSApplConnected = FALSE; BOOL APRSWeb = FALSE; void * APPL_Q = 0; // Queue of frames for APRS Appl void * APPLTX_Q = 0; // Queue of frames from APRS Appl uint64_t APRSPortMask = 0; char APRSCall[10] = ""; char APRSDest[10] = "APBPQ1"; char WXCall[10]; UCHAR AXCall[7] = ""; char CallPadded[10] = " "; char GPSPort[80] = ""; int GPSSpeed = 0; char GPSRelay[80] = ""; BOOL GateLocal = FALSE; double GateLocalDistance = 0.0; int MaxDigisforIS = 7; // Dont send to IS if more digis uued to reach us char WXFileName[MAX_PATH]; char WXComment[80]; BOOL SendWX = FALSE; int WXInterval = 30; int WXCounter = 29 * 60; char APRSCall[10]; char LoppedAPRSCall[10]; BOOL WXPort[MaxBPQPortNo + 1]; // Ports to send WX to BOOL GPSOK = 0; char LAT[] = "0000.00N"; // in standard APRS Format char LON[] = "00000.00W"; //in standard APRS Format char HostName[80]; // for BlueNMEA int HostPort = 4352; char GPSDHost[80]; int GPSDPort = 2947; extern int ADSBPort; extern char ADSBHost[]; BOOL BlueNMEAOK = FALSE; int BlueNMEATimer = 0; BOOL GPSDOK = FALSE; int GPSDTimer = 0; BOOL GPSSetsLocator = 0; // Update Map Location from GPS double SOG, COG; // From GPS double Lat = 0.0; double Lon = 0.0; BOOL PosnSet = FALSE; /* The null position should be include the \. symbol (unknown/indeterminate position). For example, a Position Report for a station with unknown position will contain the coordinates …0000.00N\00000.00W.… */ char * FloodCalls = 0; // Calls to relay using N-n without tracing char * TraceCalls = 0; // Calls to relay using N-n with tracing char * DigiCalls = 0; // Calls for normal relaying UCHAR FloodAX[MAXCALLS][7] = {0}; UCHAR TraceAX[MAXCALLS][7] = {0}; UCHAR DigiAX[MAXCALLS][7] = {0}; int FloodLen[MAXCALLS]; int TraceLen[MAXCALLS]; int DigiLen[MAXCALLS]; int ISPort = 0; char ISHost[256] = ""; int ISPasscode = 0; char NodeFilter[1000] = "m/50"; // Filter when the isn't an application char ISFilter[1000] = "m/50"; // Current Filter char APPLFilter[1000] = ""; // Filter when an Applcation is running extern BOOL IGateEnabled; char StatusMsg[256] = ""; // Must be in shared segment int StatusMsgLen = 0; char * BeaconPath[65] = {0}; char CrossPortMap[65][65] = {0}; char APRSBridgeMap[65][65] = {0}; UCHAR BeaconHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis int BeaconHddrLen[65] = {0}; // Actual Length used UCHAR GatedHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis for messages gated from IS int GatedHddrLen[65] = {0}; // Actual Length used char CFGSYMBOL = 'a'; char CFGSYMSET = 'B'; char SYMBOL = '='; // Unknown Locaton char SYMSET = '/'; char * PHG = 0; // Optional PHG (Power-Height-Gain) string for beacon BOOL TraceDigi = FALSE; // Add Trace to packets relayed on Digi Calls BOOL SATGate = FALSE; // Delay Gating to IS directly heard packets BOOL RXOnly = FALSE; // Run as RX only IGATE, ie don't gate anything to RF BOOL DefaultLocalTime = FALSE; BOOL DefaultDistKM = FALSE; int multiple = 0; // Allows multiple copies of LinBPQ/APRS on one machine extern BOOL needAIS; extern unsigned long long IconData[]; // Symbols as a png image. typedef struct _ISDELAY { struct _ISDELAY * Next; char * ISMSG; time_t SendTIme; } ISDELAY; ISDELAY * SatISQueue = NULL; int MaxTraceHops = 2; int MaxFloodHops = 2; int BeaconInterval = 0; int MobileBeaconInterval = 0; time_t LastMobileBeacon = 0; int BeaconCounter = 0; int IStatusCounter = 3600; // Used to send ?ISTATUS? Responses //int StatusCounter = 0; // Used to send Status Messages char RunProgram[128] = ""; // Program to start BOOL APRSISOpen = FALSE; BOOL BeacontoIS = TRUE; int ISDelayTimer = 0; // Time before trying to reopen APRS-IS link char APRSDESTS[][7] = {"AIR*", "ALL*", "AP*", "BEACON", "CQ*", "GPS*", "DF*", "DGPS*", "DRILL*", "DX*", "ID*", "JAVA*", "MAIL*", "MICE*", "QST*", "QTH*", "RTCM*", "SKY*", "SPACE*", "SPC*", "SYM*", "TEL*", "TEST*", "TLM*", "WX*", "ZIP"}; UCHAR AXDESTS[30][7] = {""}; int AXDESTLEN[30] = {0}; UCHAR axTCPIP[7]; UCHAR axRFONLY[7]; UCHAR axNOGATE[7]; int MessageCount = 0; struct PortInfo { int Index; int ComPort; char PortType[2]; BOOL NewVCOM; // Using User Mode Virtual COM Driver int ReopenTimer; // Retry if open failed delay int RTS; int CTS; int DCD; int DTR; int DSR; char Params[20]; // Init Params (eg 9600,n,8) char PortLabel[20]; HANDLE hDevice; BOOL Created; BOOL PortEnabled; int FLOWCTRL; int gpsinptr; #ifdef WIN32 OVERLAPPED Overlapped; OVERLAPPED OverlappedRead; #endif char GPSinMsg[160]; int GPSTypeFlag; // GPS Source flags BOOL RMCOnly; // Only send RMC msgs to this port }; struct PortInfo InPorts[1] = {0}; // Heard Station info #define MAXHEARD 1000 int HEARDENTRIES = 0; int MAXHEARDENTRIES = 0; int MHLEN = sizeof(APRSHEARDRECORD); // Area is allocated as needed APRSHEARDRECORD MHTABLE[MAXHEARD] = {0}; APRSHEARDRECORD * MHDATA = &MHTABLE[0]; static SOCKET sock = (SOCKET) NULL; //Duplicate suppression Code #define MAXDUPS 100 // Number to keep #define DUPSECONDS 28 // Time to Keep struct DUPINFO { time_t DupTime; int DupLen; char DupUser[8]; // Call in ax.35 format char DupText[100]; }; struct DUPINFO DupInfo[MAXDUPS]; struct OBJECT { struct OBJECT * Next; UCHAR Path[10][7]; // Dest, Source and up to 8 digis int PathLen; // Actual Length used char Message[81]; char PortMap[MaxBPQPortNo + 1]; int Interval; int Timer; }; struct OBJECT * ObjectList; // List of objects to send; int ObjectCount = 0; #include #define M_PI 3.14159265358979323846 int RetryCount = 4; int RetryTimer = 45; int ExpireTime = 120; int TrackExpireTime = 1440; BOOL SuppressNullPosn = FALSE; BOOL DefaultNoTracks = FALSE; int MaxStations = 1000; int SharedMemorySize = 0; RECT Rect, MsgRect, StnRect; char Key[80]; // function prototypes VOID RefreshMessages(); // a few global variables char APRSDir[MAX_PATH] = "BPQAPRS"; char DF[MAX_PATH]; #define FEND 0xC0 // KISS CONTROL CODES #define FESC 0xDB #define TFEND 0xDC #define TFESC 0xDD int StationCount = 0; UCHAR NextSeq = 1; // Stationrecords are stored in a shared memory segment. based at APRSStationMemory (normally 0x43000000) // A pointer to the first is placed at the start of this struct STATIONRECORD ** StationRecords = NULL; struct STATIONRECORD * StationRecordPool = NULL; struct APRSMESSAGE * MessageRecordPool = NULL; struct SharedMem * SMEM; UCHAR * Shared; UCHAR * StnRecordBase; VOID SendObject(struct OBJECT * Object); VOID MonitorAPRSIS(char * Msg, int MsgLen, BOOL TX); #ifndef WIN32 #define WSAEWOULDBLOCK 11 #endif HANDLE hMapFile; // Logging static int LogAge = 14; #ifdef WIN32 int DeleteAPRSLogFiles() { WIN32_FIND_DATA ffd; char szDir[MAX_PATH]; char File[MAX_PATH]; HANDLE hFind = INVALID_HANDLE_VALUE; DWORD dwError=0; LARGE_INTEGER ft; time_t now = time(NULL); int Age; // Prepare string for use with FindFile functions. First, copy the // string to a buffer, then append '\*' to the directory name. strcpy(szDir, GetLogDirectory()); strcat(szDir, "/logs/APRS*.log"); // Find the first file in the directory. hFind = FindFirstFile(szDir, &ffd); if (INVALID_HANDLE_VALUE == hFind) return dwError; // Walk directory do { if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { OutputDebugString(ffd.cFileName); } else { ft.HighPart = ffd.ftCreationTime.dwHighDateTime; ft.LowPart = ffd.ftCreationTime.dwLowDateTime; ft.QuadPart -= 116444736000000000; ft.QuadPart /= 10000000; Age = (int)((now - ft.LowPart) / 86400); if (Age > LogAge) { sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); Debugprintf("Deleting %s", File); DeleteFile(File); } } } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); return dwError; } #else #include int APRSFilter(const struct dirent * dir) { return (memcmp(dir->d_name, "APRS", 4) == 0 && strstr(dir->d_name, ".log")); } int DeleteAPRSLogFiles() { struct dirent **namelist; int n; struct stat STAT; time_t now = time(NULL); int Age = 0, res; char FN[256]; n = scandir("logs", &namelist, APRSFilter, alphasort); if (n < 0) perror("scandir"); else { while(n--) { sprintf(FN, "logs/%s", namelist[n]->d_name); if (stat(FN, &STAT) == 0) { Age = (now - STAT.st_mtime) / 86400; if (Age > LogAge) { Debugprintf("Deleting %s\n", FN); unlink(FN); } } free(namelist[n]); } free(namelist); } return 0; } #endif int APRSWriteLog(char * msg) { FILE *file; UCHAR Value[MAX_PATH]; time_t T; struct tm * tm; if (LogAPRSIS == 0) return 0; if (strchr(msg, '\n') == 0) strcat(msg, "\r\n"); T = time(NULL); tm = gmtime(&T); if (GetLogDirectory()[0] == 0) { strcpy(Value, "logs/APRS_"); } else { strcpy(Value, GetLogDirectory()); strcat(Value, "/"); strcat(Value, "logs/APRS_"); } sprintf(Value, "%s%02d%02d%02d.log", Value, tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); if ((file = fopen(Value, "ab")) == NULL) return FALSE; fputs(msg, file); fclose(file); return 0; } int ISSend(SOCKET sock, char * Msg, int Len, int flags) { int Loops = 0; int Sent; MonitorAPRSIS(Msg, Len, TRUE); Sent = send(sock, Msg, Len, flags); while (Sent != Len && Loops++ < 300) // 10 secs max { if ((Sent == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) return SOCKET_ERROR; if (Sent > 0) // something sent { Len -= Sent; memmove(Msg, &Msg[Sent], Len); } Sleep(30); Sent = send(sock, Msg, Len, flags); } return Sent; } void * endofStations; Dll BOOL APIENTRY Init_APRS() { int i; char * DCall; #ifdef WIN32 HKEY hKey=0; int retCode, Vallen, Type; #else int fd; char RX_SOCK_PATH[] = "BPQAPRSrxsock"; char TX_SOCK_PATH[] = "BPQAPRStxsock"; char SharedName[256]; char * ptr1; #endif struct STATIONRECORD * Stn1, * Stn2; struct APRSMESSAGE * Msg1, * Msg2; // Clear tables in case a restart StationRecords = NULL; StationCount = 0; HEARDENTRIES = 0; MAXHEARDENTRIES = 0; MobileBeaconInterval = 0; BeaconInterval = 0; DeleteAPRSLogFiles(); memset(MHTABLE, 0, sizeof(MHTABLE)); ConvToAX25(MYNODECALL, MYCALL); ConvToAX25("TCPIP", axTCPIP); ConvToAX25("RFONLY", axRFONLY); ConvToAX25("NOGATE", axNOGATE); memset(&FloodAX[0][0], 0, sizeof(FloodAX)); memset(&TraceAX[0][0], 0, sizeof(TraceAX)); memset(&DigiAX[0][0], 0, sizeof(DigiAX)); APRSPortMask = 0; memset(BeaconPath, sizeof(BeaconPath), 0); memset(&CrossPortMap[0][0], 0, sizeof(CrossPortMap)); memset(&APRSBridgeMap[0][0], 0, sizeof(APRSBridgeMap)); for (i = 1; i <= MaxBPQPortNo; i++) { if (CanPortDigi(i)) CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port CrossPortMap[i][0] = TRUE; // and APRS-IS } PosnSet = 0; ObjectList = NULL; ObjectCount = 0; ISPort = ISHost[0] = ISPasscode = 0; if (APRSReadConfigFile() == 0) return FALSE; if (APRSCall[0] == 0) { strcpy(APRSCall, MYNODECALL); strlop(APRSCall, ' '); strcpy(LoppedAPRSCall, APRSCall); memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging ConvToAX25(APRSCall, AXCall); } if (WXCall[0] == 0) strcpy(WXCall, APRSCall); // Caluclate size of Shared Segment SharedMemorySize = sizeof(struct STATIONRECORD) * (MaxStations + 4) + sizeof(struct APRSMESSAGE) * (MAXMESSAGES + 4) + 32; // 32 for header #ifndef WIN32 // Create a Shared Memory Object Shared = NULL; // Append last bit of current directory to shared name ptr1 = BPQDirectory; while (strchr(ptr1, '/')) { ptr1 = strchr(ptr1, '/'); ptr1++; } if (multiple) sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); else strcpy(SharedName, "/BPQAPRSSharedMem"); printf("Using Shared Memory %s\n", SharedName); #ifndef WIN32 fd = shm_open(SharedName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); if (fd == -1) { perror("Create Shared Memory"); printf("Create APRS Shared Memory Failed\n"); } else { if (ftruncate(fd, SharedMemorySize)) { perror("Extend Shared Memory"); printf("Extend APRS Shared Memory Failed\n"); } else { // Map shared memory object Shared = mmap((void *)APRSSHAREDMEMORYBASE, SharedMemorySize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (Shared == MAP_FAILED) { perror("Map Shared Memory"); printf("Map APRS Shared Memory Failed\n"); Shared = NULL; } if (Shared != (void *)APRSSHAREDMEMORYBASE) { printf("Map APRS Shared Memory Allocated at %x\n", Shared); Shared = NULL; } } } #endif printf("Map APRS Shared Memory Allocated at %p\n", Shared); if (Shared == NULL) { printf("APRS not using shared memory\n"); Shared = malloc(SharedMemorySize); printf("APRS Non-Shared Memory Allocated at %x\n", Shared); } #else #ifndef LINBPQ retCode = RegOpenKeyEx (REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, KEY_QUERY_VALUE, &hKey); if (retCode == ERROR_SUCCESS) { Vallen = 4; retCode = RegQueryValueEx(hKey, "IGateEnabled", 0, &Type, (UCHAR *)&IGateEnabled, &Vallen); } #endif // Create Memory Mapping for Station List hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE, // read/write access 0, // maximum object size (high-order DWORD) SharedMemorySize, // maximum object size (low-order DWORD) "BPQAPRSStationsMappingObject");// name of mapping object if (hMapFile == NULL) { Consoleprintf("Could not create file mapping object (%d).\n", GetLastError()); return 0; } UnmapViewOfFile((void *)APRSSHAREDMEMORYBASE); Shared = (LPTSTR) MapViewOfFileEx(hMapFile, // handle to map object FILE_MAP_ALL_ACCESS, // read/write permission 0, 0, SharedMemorySize, (void *)APRSSHAREDMEMORYBASE); if (Shared == NULL) { Consoleprintf("Could not map view of file (%d).\n", GetLastError()); CloseHandle(hMapFile); return 0; } #endif // First record has pointer to table memset(Shared, 0, SharedMemorySize); StnRecordBase = Shared + 32; SMEM = (struct SharedMem *)Shared; SMEM->Version = 1; SMEM->SharedMemLen = SharedMemorySize; SMEM->NeedRefresh = TRUE; SMEM->Arch = sizeof(void *); SMEM->SubVersion = 1; Stn1 = (struct STATIONRECORD *)StnRecordBase; StationRecords = (struct STATIONRECORD **)Stn1; Stn1++; StationRecordPool = Stn1; for (i = 1; i < MaxStations; i++) // Already have first { Stn2 = Stn1; Stn2++; Stn1->Next = Stn2; Stn1 = Stn2; } Debugprintf("End of Stations %p", Stn1); endofStations = Stn1; Stn1 += 2; // Try to fix corruption of messages. // Build Message Record Pool Msg1 = (struct APRSMESSAGE *)Stn1; MessageRecordPool = Msg1; for (i = 1; i < MAXMESSAGES; i++) // Already have first { Msg2 = Msg1; Msg2++; Msg1->Next = Msg2; Msg1 = Msg2; } if (PosnSet == 0) { SYMBOL = '.'; SYMSET = '\\'; // Undefined Posn Symbol } else { // Convert posn to floating degrees char LatDeg[3], LonDeg[4]; memcpy(LatDeg, LAT, 2); LatDeg[2]=0; Lat=atof(LatDeg) + (atof(LAT+2)/60); if (LAT[7] == 'S') Lat=-Lat; memcpy(LonDeg, LON, 3); LonDeg[3]=0; Lon=atof(LonDeg) + (atof(LON+3)/60); if (LON[8]== 'W') Lon=-Lon; SYMBOL = CFGSYMBOL; SYMSET = CFGSYMSET; } // First record has control info for APRS Mapping App Stn1 = (struct STATIONRECORD *)StnRecordBase; memcpy(Stn1->Callsign, APRSCall, 10); Stn1->Lat = Lat; Stn1->Lon = Lon; Stn1->LastPort = MaxStations; #ifndef WIN32 // Open unix socket for messaging app sfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sfd == -1) { perror("Socket"); } else { u_long param=1; ioctl(sfd, FIONBIO, ¶m); // Set non-blocking memset(&my_addr, 0, sizeof(struct sockaddr_un)); my_addr.sun_family = AF_UNIX; strncpy(my_addr.sun_path, TX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); memset(&peer_addr, 0, sizeof(struct sockaddr_un)); peer_addr.sun_family = AF_UNIX; strncpy(peer_addr.sun_path, RX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); unlink(TX_SOCK_PATH); if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) perror("bind"); } #endif // Convert Dest ADDRS to AX.25 for (i = 0; i < 26; i++) { DCall = &APRSDESTS[i][0]; if (strchr(DCall, '*')) AXDESTLEN[i] = (int)strlen(DCall) - 1; else AXDESTLEN[i] = 6; ConvToAX25(DCall, &AXDESTS[i][0]); } // Process any Object Definitions // Setup Heard Data Area HEARDENTRIES = 0; MAXHEARDENTRIES = MAXHEARD; APRSMONVECPTR->HOSTAPPLFLAGS = 0x80; // Request Monitoring if (ISPort && IGateEnabled) { _beginthread(APRSISThread, 0, (VOID *) TRUE); } if (GPSPort[0]) OpenGPSPort(); WritetoConsole("APRS Digi/Gateway Enabled\n"); APRSWeb = TRUE; read_png((unsigned char *)IconData); // Reload saved messages if (SaveAPRSMsgs) GetSavedAPRSMessages(); // If a Run parameter was supplied, run the program if (RunProgram[0] == 0) return TRUE; #ifndef WIN32 { char * arg_list[] = {NULL, NULL}; pid_t child_pid; signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. // Fork and Exec program printf("Trying to start %s\n", RunProgram); arg_list[0] = RunProgram; // Duplicate this process. child_pid = fork (); if (child_pid == -1) { printf ("APRS fork() Failed\n"); return 0; } if (child_pid == 0) { execvp (arg_list[0], arg_list); // The execvp function returns only if an error occurs. printf ("Failed to run %s\n", RunProgram); exit(0); // Kill the new process } } #else { int n = 0; STARTUPINFO SInfo; // pointer to STARTUPINFO PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION SInfo.cb=sizeof(SInfo); SInfo.lpReserved=NULL; SInfo.lpDesktop=NULL; SInfo.lpTitle=NULL; SInfo.dwFlags=0; SInfo.cbReserved2=0; SInfo.lpReserved2=NULL; while (KillOldTNC(RunProgram) && n++ < 100) { Sleep(100); } if (!CreateProcess(RunProgram, NULL, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) Debugprintf("Failed to Start %s Error %d ", RunProgram, GetLastError()); } #endif return TRUE; } #define SD_RECEIVE 0x00 #define SD_SEND 0x01 #define SD_BOTH 0x02 BOOL APRSActive; VOID APRSClose() { APRSActive = FALSE; if (sock) { shutdown(sock, SD_BOTH); Sleep(50); closesocket(sock); } #ifdef WIN32 if (InPorts[0].hDevice) CloseHandle(InPorts[0].hDevice); #endif } time_t lastSecTimer = 0; Dll VOID APIENTRY Poll_APRS() { time_t Now = time(NULL); if (lastSecTimer != Now) { lastSecTimer = Now; DoSecTimer(); MinTimer--; if (MinTimer == 0) { MinTimer = 60; DoMinTimer(); } } if (SMEM->ClearRX) { // Clear Received Messages request from GUI struct APRSMESSAGE * ptr = SMEM->Messages; // Move Message Queue to Free Queue if (ptr) { while (ptr->Next) // Find end of chain { ptr = ptr->Next; } // ptr is end of chain - chain free pool to it ptr->Next = MessageRecordPool; MessageRecordPool = SMEM->Messages; MessageCount = 0; } SMEM->Messages = NULL; SMEM->ClearRX = 0; SMEM->NeedRefresh = TRUE; ClearSavedMessages(); } if (SMEM->ClearTX) { // Clear Sent Messages )request from GUI struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; // Move Message Queue to Free Queue if (ptr) { while (ptr->Next) // Find end of chain { ptr = ptr->Next; } // ptr is end of chain - chain free pool to it ptr->Next = MessageRecordPool; MessageRecordPool = SMEM->OutstandingMsgs; MessageCount = 0; } SMEM->OutstandingMsgs = NULL; SMEM->ClearTX = 0; SMEM->NeedRefresh = TRUE; } #ifdef LINBPQ #ifndef WIN32 { char Msg[256]; int numBytes; // Look for messages from App numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); if (numBytes > 0) { InternalSendAPRSMessage(&Msg[10], &Msg[0]); } } #endif #endif if (GPSPort[0]) PollGPSIn(); if (APPLTX_Q) { PMSGWITHLEN buffptr = Q_REM(&APPLTX_Q); InternalSendAPRSMessage(&buffptr->Data[10], &buffptr->Data[0]); ReleaseBuffer(buffptr); } while (APRSMONVECPTR->HOSTTRACEQ) { time_t stamp; int len; BOOL MonitorNODES = FALSE; PMESSAGE monbuff; UCHAR * monchars; MESSAGE * Orig; int Digis = 0; MESSAGE * AdjBuff; // Adjusted for digis BOOL FirstUnused = FALSE; int DigisUsed = 0; // Digis used to reach us DIGIMESSAGE Msg = {0}; int Port; unsigned char buffer[1024]; char ISMsg[500]; char * ptr1; char * Payload; char * ptr3; char * ptr4; BOOL ThirdParty = FALSE; BOOL NoGate = FALSE; APRSHEARDRECORD * MH; char MsgCopy[500]; int toPort; struct STATIONRECORD * Station; int ourMessage = 0; #ifdef WIN32 struct _EXCEPTION_POINTERS exinfo; char EXCEPTMSG[80] = ""; #endif monbuff = Q_REM((VOID **)&APRSMONVECPTR->HOSTTRACEQ); monchars = (UCHAR *)monbuff; AdjBuff = Orig = (MESSAGE *)monchars; // Adjusted for digis Port = Orig->PORT; if (Port & 0x80) // TX { ReleaseBuffer(monbuff); continue; } if ((APRSPortMask & ((uint64_t)1 << (Port - 1))) == 0)// Port in use for APRS? { ReleaseBuffer(monbuff); continue; } stamp = monbuff->Timestamp; if ((UCHAR)monchars[4] & 0x80) // TX { ReleaseBuffer(monbuff); continue; } // See if digipeaters present. while ((AdjBuff->ORIGIN[6] & 1) == 0 && Digis < 9) { UCHAR * temp = (UCHAR *)AdjBuff; temp += 7; AdjBuff = (MESSAGE *)temp; // If we have already digi'ed it or if we sent it, // ignore (Dup Check my fail on slow links) if (AdjBuff->ORIGIN[6] & 0x80) { // Used Digi if (memcmp(AdjBuff->ORIGIN, AXCall, 7) == 0) { ReleaseBuffer(monbuff); return; } DigisUsed++; } if (memcmp(AdjBuff->ORIGIN, axTCPIP, 6) == 0) ThirdParty = TRUE; Digis ++; if (FirstUnused == FALSE && (AdjBuff->ORIGIN[6] & 0x80) == 0) { // Unused Digi - see if we should digi it FirstUnused = Digis; // CheckDigi(buff, AdjBuff->ORIGIN); } } if (Digis > 8) { ReleaseBuffer(monbuff); continue; // Corrupt } if (Digis) { if (memcmp(AdjBuff->ORIGIN, axNOGATE, 6) == 0 || memcmp(AdjBuff->ORIGIN, axRFONLY, 6) == 0 || DigisUsed > MaxDigisforIS) // Too many digis or Last digis is NOGATE or RFONLY - dont send to IS NoGate = TRUE; } if (AdjBuff->CTL != 3 || AdjBuff->PID != 0xf0) // Only UI { ReleaseBuffer(monbuff); continue; } // Bridge if requested for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) { if (APRSBridgeMap[Port][toPort]) { MESSAGE * Buffer = GetBuff(); struct PORTCONTROL * PORT; if (Buffer) { memcpy(Buffer, Orig, Orig->LENGTH); Buffer->PORT = toPort; PORT = GetPortTableEntryFromPortNum(toPort); if (PORT) { if (PORT->SmartIDInterval && PORT->SmartIDNeeded == 0) { // Using Smart ID, but none scheduled PORT->SmartIDNeeded = time(NULL) + PORT->SmartIDInterval; } PUT_ON_PORT_Q(PORT, Buffer); } else ReleaseBuffer(Buffer); } } } // Used to check for dups here but according to "Notes to iGate developers" IS should be sent dups, and dup // check only applied to digi'ing // if (SATGate == 0) // { // if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *))) // { // ReleaseBuffer(monbuff); // continue; // } // } // Decode Frame to TNC2 Monitor Format len = APRSDecodeFrame((char *)monchars, buffer, stamp, APRSPortMask); if (len == 0) { // Couldn't Decode ReleaseBuffer(monbuff); Debugprintf("APRS discarded frame - decode failed\n"); continue; } buffer[len] = 0; memcpy(MsgCopy, buffer, len); MsgCopy[len] = 0; // Do internal Decode #ifdef WIN32 strcpy(EXCEPTMSG, "ProcessRFFrame"); __try { Station = ProcessRFFrame(MsgCopy, len, &ourMessage); } #include "StdExcept.c" } #else Station = ProcessRFFrame(MsgCopy, len, &ourMessage); #endif if (Station == NULL) { ReleaseBuffer(monbuff); continue; } memcpy(MsgCopy, buffer, len); // Process RF Frame may have changed it MsgCopy[len] = 0; buffer[len++] = 10; buffer[len] = 0; ptr1 = &buffer[10]; // Skip Timestamp Payload = strchr(ptr1, ':') + 2; // Start of Payload ptr3 = strchr(ptr1, ' '); // End of addresses *ptr3 = 0; // We should send path to IS unchanged, so create IS // message before chopping path. We won't decide if // we will actually send it to IS till later len = sprintf(ISMsg, "%s,qAR,%s:%s", ptr1, APRSCall, Payload); // if digis, remove any unactioned ones if (Digis) { ptr4 = strchr(ptr1, '*'); // Last Used Digi if (ptr4) { // We need header up to ptr4 *(ptr4) = 0; } else { // No digis actioned - remove them all ptr4 = strchr(ptr1, ','); // End of Dest if (ptr4) *ptr4 = 0; } } ptr4 = strchr(ptr1, '>'); // End of Source *ptr4++ = 0; MH = UpdateHeard(ptr1, Port); MH->Station = Station; if (ThirdParty) { // Debugprintf("Setting Igate Flag - %s", MsgCopy); MH->IGate = TRUE; // if we've seen msgs to TCPIP, it must be an Igate } if (NoGate || RXOnly) goto NoIS; // I think all PID F0 UI frames go to APRS-IS, // Except General Queries, Frames Gated from IS to RF, and Messages Addressed to us // or should we process Query frames locally ?? if (Payload[0] == '}') goto NoIS; if (Payload[0] == '?') { // General Query ProcessQuery(&Payload[1]); // ?? Should we pass addressed Queries to IS ?? goto NoIS; } if (Payload[0] == ':' && memcmp(&Payload[1], CallPadded, 9) == 0) { // Message for us if (Payload[11] == '?') // Only queries - the node doesnt do messaging ProcessSpecificQuery(&Payload[12], Port, ptr1, ptr4); goto NoIS; } if (APRSISOpen && CrossPortMap[Port][0]) // No point if not open { // was done above len = sprintf(ISMsg, "%s>%s,qAR,%s:%s", ptr1, ptr4, APRSCall, Payload); if (BeacontoIS == 0) { // Don't send anything we've received as an echo char SaveCall[7]; memcpy(SaveCall, &monbuff->ORIGIN, 7); SaveCall[6] &= 0x7e; // Mask End of address bit if (memcmp(SaveCall, AXCall, 7) == 0) // We sent it { // Should we check for being received via digi? - not for now goto NoIS; } } if (SATGate && (DigisUsed == 0)) { // If in Satgate mode delay directly heard to IGate ISDELAY * SatISEntry = malloc(sizeof(ISDELAY)); SatISEntry->Next = NULL; SatISEntry->ISMSG = _strdup(ISMsg); SatISEntry->SendTIme = time(NULL) + 10; // Delay 10 seconds if (SatISQueue) SatISEntry->Next = SatISQueue; // Chain SatISQueue = SatISEntry; goto NoIS; } ISSend(sock, ISMsg, len, 0); ptr1 = strchr(ISMsg, 13); if (ptr1) *ptr1 = 0; // Debugprintf(">%s", ISMsg); } NoIS: // We skipped DUP check for SATGate Mode, so apply it here // Now we don't dup check earlier so always check here // if (SATGate) // { if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *)))) { ReleaseBuffer(monbuff); continue; } // } // See if it is an APRS frame // If MIC-E, we need to process, whatever the destination // Now process any dest /* DEST = &Orig->DEST[0]; for (i = 0; i < 26; i++) { if (memcmp(DEST, &AXDESTS[i][0], AXDESTLEN[i]) == 0) goto OK; } switch(AdjBuff->L2DATA[0]) { case '`': case 0x27: // ' case 0x1c: case 0x1d: // MIC-E break; // default: // Not to an APRS Destination // ReleaseBuffer(monbuff); // continue; } OK: */ // If there are unused digis, we may need to digi it. if (ourMessage) { // A message addressed to us, so no point in digi'ing it ReleaseBuffer(monbuff); continue; } if (Digis == 0 || FirstUnused == 0) { // No Digis, so finished ReleaseBuffer(monbuff); continue; } if (memcmp(monbuff->ORIGIN, AXCall, 7) == 0) // We sent it { ReleaseBuffer(monbuff); continue; } // Copy frame to a DIGIMessage Struct memcpy(&Msg, monbuff, MSGHDDRLEN + 14 + (7 * Digis)); // Header, Dest, Source, Addresses and Digis len = Msg.LENGTH - (MSGHDDRLEN + 14) - (7 * Digis); // Payload Length (including CTL and PID memcpy(&Msg.CTL, &AdjBuff->CTL, len); // Pass to Digi Code CheckandDigi(&Msg, Port, FirstUnused, Digis, len); // Digi if necessary ReleaseBuffer(monbuff); } return; } VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len) { UCHAR * Digi = &Msg->DIGIS[--FirstUnused][0]; UCHAR * Call; int Index = 0; int SSID; // Check ordinary digi first Call = &DigiAX[0][0]; SSID = Digi[6] & 0x1e; while (*Call) { if ((memcmp(Digi, Call, 6) == 0) && ((Call[6] & 0x1e) == SSID)) { // Trace Call if enabled if (TraceDigi) memcpy(Digi, AXCall, 7); // mark as used; Digi[6] |= 0x80; // Used bit SendtoDigiPorts(Msg, Len, Port); return; } Call += 7; Index++; } Call = &TraceAX[0][0]; Index = 0; while (*Call) { if (memcmp(Digi, Call, TraceLen[Index]) == 0) { // if possible move calls along // insert our call, set used // decrement ssid, and if zero, mark as used; SSID = (Digi[6] & 0x1E) >> 1; if (SSID == 0) return; // Shouldn't have SSID 0 for Rrace/Flood if (SSID > MaxTraceHops) SSID = MaxTraceHops; // Enforce our limit SSID--; if (SSID ==0) // Finihed with it ? Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits else Digi[6] = (SSID << 1) | 0x60; // Fixed bits if (Digis < 8) { memmove(Digi + 7, Digi, (Digis - FirstUnused) * 7); } memcpy(Digi, AXCall, 7); Digi[6] |= 0x80; SendtoDigiPorts(Msg, Len, Port); return; } Call += 7; Index++; } Index = 0; Call = &FloodAX[0][0]; while (*Call) { if (memcmp(Digi, Call, FloodLen[Index]) == 0) { // decrement ssid, and if zero, mark as used; SSID = (Digi[6] & 0x1E) >> 1; if (SSID == 0) return; // Shouldn't have SSID 0 for Trace/Flood if (SSID > MaxFloodHops) SSID = MaxFloodHops; // Enforce our limit SSID--; if (SSID ==0) // Finihed with it ? Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits else Digi[6] = (SSID << 1) | 0x60; // Fixed bits SendtoDigiPorts(Msg, Len, Port); return; } Call += 7; Index++; } } static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) { // Can't use API SENDRAW, as that tries to get the semaphore, which we already have // Len is the Payload Length (from CTL onwards) // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 // We send to all ports enabled in CrossPortMap UCHAR * EndofDigis = &Block->CTL; int i = 0; int toPort; while (Block->DIGIS[i][0] && i < 8) { i++; } EndofDigis = &Block->DIGIS[i][0]; *(EndofDigis -1) |= 1; // Set End of Address Bit if (i != 8) memmove(EndofDigis, &Block->CTL, Len); Len = Len + (i * 7) + 14; // Include Source, Dest and Digis // Block->DEST[6] &= 0x7e; // Clear End of Call // Block->ORIGIN[6] |= 1; // Set End of Call // Block->CTL = 3; //UI for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) { if (CrossPortMap[Port][toPort]) Send_AX((PMESSAGE)Block, Len, toPort); } return; } VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) { // Can't use API SENDRAW, as that tries to get the semaphore, which we already have // Len is the Payload Length (CTL, PID, Data) // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 UCHAR * EndofDigis = &Block->CTL; int i = 0; while (Block->DIGIS[i][0] && i < 8) { i++; } EndofDigis = &Block->DIGIS[i][0]; *(EndofDigis -1) |= 1; // Set End of Address Bit if (i != 8) memmove(EndofDigis, &Block->CTL, Len); // Include PID Len = Len + (i * 7) + 14; // Include Source, Dest and Digis Send_AX((PMESSAGE)Block, Len, Port); return; } static BOOL APRSReadConfigFile() { char * Config; char * ptr1, * ptr2; char buf[256],errbuf[256]; Config = PortConfig[APRSConfigSlot]; // Config from bpq32.cfg sprintf(StatusMsg, "BPQ32 Igate V %s", VersionString); // Set Default Status Message if (Config) { // Using config from bpq32.cfg ptr1 = Config; ptr2 = strchr(ptr1, 13); while(ptr2) { memcpy(buf, ptr1, ptr2 - ptr1); buf[ptr2 - ptr1] = 0; ptr1 = ptr2 + 2; ptr2 = strchr(ptr1, 13); strcpy(errbuf,buf); // save in case of error if (!APRSProcessLine(buf)) { WritetoConsole("APRS Bad config record "); strcat(errbuf, "\r\n"); WritetoConsole(errbuf); } } return TRUE; } return FALSE; } BOOL ConvertCalls(char * DigiCalls, UCHAR * AX, int * Lens) { int Index = 0; char * ptr; char * Context; UCHAR Work[MAXCALLS][7] = {0}; int Len[MAXCALLS] = {0}; ptr = strtok_s(DigiCalls, ", ", &Context); while(ptr) { if (Index == MAXCALLS) return FALSE; ConvToAX25(ptr, &Work[Index][0]); Len[Index++] = (int)strlen(ptr); ptr = strtok_s(NULL, ", ", &Context); } memcpy(AX, Work, sizeof(Work)); memcpy(Lens, Len, sizeof(Len)); return TRUE; } static int APRSProcessLine(char * buf) { char * ptr, * p_value; ptr = strtok(buf, "= \t\n\r"); if(ptr == NULL) return (TRUE); if(*ptr =='#') return (TRUE); // comment if(*ptr ==';') return (TRUE); // comment // OBJECT PATH=APRS,WIDE1-1 PORT=1,IS INTERVAL=30 TEXT=;444.80TRF*111111z4807.60N/09610.63Wr%156 R15m if (_stricmp(ptr, "OBJECT") == 0) { char * p_Path, * p_Port, * p_Text; int Interval; struct OBJECT * Object; int Digi = 2; char * Context; int SendTo; p_value = strtok(NULL, "="); if (p_value == NULL) return FALSE; if (_stricmp(p_value, "PATH")) return FALSE; p_Path = strtok(NULL, "\t\n\r "); if (p_Path == NULL) return FALSE; p_value = strtok(NULL, "="); if (p_value == NULL) return FALSE; if (_stricmp(p_value, "PORT")) return FALSE; p_Port = strtok(NULL, "\t\n\r "); if (p_Port == NULL) return FALSE; p_value = strtok(NULL, "="); if (p_value == NULL) return FALSE; if (_stricmp(p_value, "INTERVAL")) return FALSE; p_value = strtok(NULL, " \t"); if (p_value == NULL) return FALSE; Interval = atoi(p_value); if (Interval == 0) return FALSE; p_value = strtok(NULL, "="); if (p_value == NULL) return FALSE; if (_stricmp(p_value, "TEXT")) return FALSE; p_Text = strtok(NULL, "\n\r"); if (p_Text == NULL) return FALSE; Object = zalloc(sizeof(struct OBJECT)); if (Object == NULL) return FALSE; Object->Next = ObjectList; ObjectList = Object; if (Interval < 10) Interval = 10; Object->Interval = Interval; Object->Timer = (ObjectCount++) * 10 + 30; // Spread them out; // Convert Path to AX.25 ConvToAX25(APRSCall, &Object->Path[1][0]); ptr = strtok_s(p_Path, ",\t\n\r", &Context); if (_stricmp(ptr, "APRS") == 0) // First is Dest ConvToAX25(APRSDest, &Object->Path[0][0]); else if (_stricmp(ptr, "APRS-0") == 0) ConvToAX25("APRS", &Object->Path[0][0]); else ConvToAX25(ptr, &Object->Path[0][0]); ptr = strtok_s(NULL, ",\t\n\r", &Context); while (ptr) { ConvToAX25(ptr, &Object->Path[Digi++][0]); ptr = strtok_s(NULL, " ,\t\n\r", &Context); } Object->PathLen = Digi * 7; // Process Port List ptr = strtok_s(p_Port, ",", &Context); while (ptr) { SendTo = atoi(ptr); // this gives zero for IS if (SendTo > MaxBPQPortNo) return FALSE; Object->PortMap[SendTo] = TRUE; ptr = strtok_s(NULL, " ,\t\n\r", &Context); } if (strlen(p_Text) > 80) p_Text[80] = 0; strcpy(Object->Message, p_Text); return TRUE; } if (_stricmp(ptr, "STATUSMSG") == 0) { p_value = strtok(NULL, ";\t\n\r"); memcpy(StatusMsg, p_value, 128); // Just in case too long StatusMsgLen = (int)strlen(p_value); return TRUE; } if (_stricmp(ptr, "WXFileName") == 0) { p_value = strtok(NULL, ";\t\n\r"); strcpy(WXFileName, p_value); SendWX = TRUE; return TRUE; } if (_stricmp(ptr, "WXComment") == 0) { p_value = strtok(NULL, ";\t\n\r"); if (p_value == NULL) return TRUE; if (strlen(p_value) > 79) p_value[80] = 0; strcpy(WXComment, p_value); return TRUE; } if (_stricmp(ptr, "ISFILTER") == 0) { p_value = strtok(NULL, ";\t\n\r"); strcpy(ISFilter, p_value); strcpy(NodeFilter, ISFilter); return TRUE; } if (_stricmp(ptr, "ReplaceDigiCalls") == 0) { TraceDigi = TRUE; return TRUE; } if (_stricmp(ptr, "Multiple") == 0) { multiple = TRUE; return TRUE; } if (_stricmp(ptr, "SATGate") == 0) { SATGate = TRUE; return TRUE; } if (_stricmp(ptr, "RXOnly") == 0) { RXOnly = TRUE; return TRUE; } if (_stricmp(ptr, "DISTKM") == 0) { DefaultDistKM = TRUE; return TRUE; } if (_stricmp(ptr, "LOCALTIME") == 0) { DefaultLocalTime = TRUE; return TRUE; } if (_stricmp(ptr, "LOGAPRSIS") == 0) { LogAPRSIS = TRUE; return TRUE; } if (_stricmp(ptr, "SaveAPRSMsgs") == 0) { SaveAPRSMsgs = TRUE; return TRUE; } p_value = strtok(NULL, " \t\n\r"); if (p_value == NULL) return FALSE; if (_stricmp(ptr, "APRSCALL") == 0) { strcpy(APRSCall, p_value); strcpy(LoppedAPRSCall, p_value); memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging // Convert to ax.25 return ConvToAX25(APRSCall, AXCall); } if (_stricmp(ptr, "WXCALL") == 0) { strcpy(WXCall, p_value); return TRUE; } if (_stricmp(ptr, "APRSPATH") == 0) { int Digi = 2; int Port; char * Context; p_value = strtok_s(p_value, "=\t\n\r", &Context); Port = atoi(p_value); if (GetPortTableEntryFromPortNum(Port) == NULL) return FALSE; APRSPortMask |= (uint64_t)1 << (Port - 1); if (Context == NULL || Context[0] == 0) return TRUE; // No dest - a receive-only port BeaconPath[Port] = _strdup(_strupr(Context)); ptr = strtok_s(NULL, ",\t\n\r", &Context); if (ptr == NULL) return FALSE; ConvToAX25(APRSCall, &BeaconHeader[Port][1][0]); if (_stricmp(ptr, "APRS") == 0) // First is Dest ConvToAX25(APRSDest, &BeaconHeader[Port][0][0]); else if (_stricmp(ptr, "APRS-0") == 0) ConvToAX25("APRS", &BeaconHeader[Port][0][0]); else ConvToAX25(ptr, &BeaconHeader[Port][0][0]); ptr = strtok_s(NULL, ",\t\n\r", &Context); while (ptr) { ConvToAX25(ptr, &BeaconHeader[Port][Digi++][0]); ptr = strtok_s(NULL, " ,\t\n\r", &Context); } BeaconHddrLen[Port] = Digi * 7; return TRUE; } if (_stricmp(ptr, "GATEDPATH") == 0) { int Digi = 2; int Port; char * Context; p_value = strtok_s(p_value, "=\t\n\r", &Context); Port = atoi(p_value); if (GetPortTableEntryFromPortNum(Port) == NULL) return FALSE; // APRSPortMask |= 1 << (Port - 1); if (Context == NULL || Context[0] == 0) return TRUE; // No dest - a receive-only port BeaconPath[Port] = _strdup(_strupr(Context)); ptr = strtok_s(NULL, ",\t\n\r", &Context); if (ptr == NULL) return FALSE; ConvToAX25(APRSCall, &GatedHeader[Port][1][0]); if (_stricmp(ptr, "APRS") == 0) // First is Dest ConvToAX25(APRSDest, &GatedHeader[Port][0][0]); else if (_stricmp(ptr, "APRS-0") == 0) ConvToAX25("APRS", &GatedHeader[Port][0][0]); else ConvToAX25(ptr, &GatedHeader[Port][0][0]); ptr = strtok_s(NULL, ",\t\n\r", &Context); while (ptr) { ConvToAX25(ptr, &GatedHeader[Port][Digi++][0]); ptr = strtok_s(NULL, " ,\t\n\r", &Context); } GatedHddrLen[Port] = Digi * 7; return TRUE; } if (_stricmp(ptr, "DIGIMAP") == 0) { int DigiTo; int Port; char * Context; p_value = strtok_s(p_value, "=\t\n\r", &Context); Port = atoi(p_value); if (GetPortTableEntryFromPortNum(Port) == NULL) return FALSE; // Check that port can digi (SCS Pactor can't set digi'd bit in calls) if (CanPortDigi(Port) == 0) return FALSE; CrossPortMap[Port][Port] = FALSE; // Cancel Default mapping CrossPortMap[Port][0] = FALSE; // Cancel Default APRSIS if (Context == NULL || Context[0] == 0) return FALSE; ptr = strtok_s(NULL, ",\t\n\r", &Context); while (ptr) { DigiTo = atoi(ptr); // this gives zero for IS if (DigiTo && GetPortTableEntryFromPortNum(DigiTo) == NULL) return FALSE; CrossPortMap[Port][DigiTo] = TRUE; ptr = strtok_s(NULL, " ,\t\n\r", &Context); } return TRUE; } if (_stricmp(ptr, "BRIDGE") == 0) { int DigiTo; int Port; char * Context; p_value = strtok_s(p_value, "=\t\n\r", &Context); Port = atoi(p_value); if (GetPortTableEntryFromPortNum(Port) == NULL) return FALSE; if (Context == NULL) return FALSE; ptr = strtok_s(NULL, ",\t\n\r", &Context); while (ptr) { DigiTo = atoi(ptr); // this gives zero for IS if (DigiTo > MaxBPQPortNo) return FALSE; APRSBridgeMap[Port][DigiTo] = TRUE; ptr = strtok_s(NULL, " ,\t\n\r", &Context); } return TRUE; } if (_stricmp(ptr, "BeaconInterval") == 0) { BeaconInterval = atoi(p_value); if (BeaconInterval < 5) BeaconInterval = 5; if (BeaconInterval) BeaconCounter = 30; // Send first after 30 secs return TRUE; } if (_stricmp(ptr, "MobileBeaconInterval") == 0) { MobileBeaconInterval = atoi(p_value) * 60; return TRUE; } if (_stricmp(ptr, "MobileBeaconIntervalSecs") == 0) { MobileBeaconInterval = atoi(p_value); if (MobileBeaconInterval < 10) MobileBeaconInterval = 10; return TRUE; } if (_stricmp(ptr, "BeacontoIS") == 0) { BeacontoIS = atoi(p_value); return TRUE; } if (_stricmp(ptr, "TRACECALLS") == 0) { TraceCalls = _strdup(_strupr(p_value)); ConvertCalls(TraceCalls, &TraceAX[0][0], &TraceLen[0]); return TRUE; } if (_stricmp(ptr, "FLOODCALLS") == 0) { FloodCalls = _strdup(_strupr(p_value)); ConvertCalls(FloodCalls, &FloodAX[0][0], &FloodLen[0]); return TRUE; } if (_stricmp(ptr, "DIGICALLS") == 0) { char AllCalls[1024]; DigiCalls = _strdup(_strupr(p_value)); strcpy(AllCalls, APRSCall); strcat(AllCalls, ","); strcat(AllCalls, DigiCalls); ConvertCalls(AllCalls, &DigiAX[0][0], &DigiLen[0]); return TRUE; } if (_stricmp(ptr, "MaxStations") == 0) { MaxStations = atoi(p_value); if (MaxStations > 10000) MaxStations = 10000; return TRUE; } if (_stricmp(ptr, "MaxAge") == 0) { ExpireTime = atoi(p_value); return TRUE; } if (_stricmp(ptr, "GPSPort") == 0) { if (strcmp(p_value, "0") != 0) strcpy(GPSPort, p_value); return TRUE; } if (_stricmp(ptr, "GPSSpeed") == 0) { GPSSpeed = atoi(p_value); return TRUE; } if (_stricmp(ptr, "GPSRelay") == 0) { if (strlen(p_value) > 79) return FALSE; strcpy(GPSRelay, p_value); return TRUE; } if (_stricmp(ptr, "BlueNMEA") == 0 || _stricmp(ptr, "TCPHost") == 0 || _stricmp(ptr, "AISHost") == 0) { if (strlen(p_value) > 70) return FALSE; strcpy(HostName, p_value); return TRUE; } if (_stricmp(ptr, "TCPPort") == 0 || _stricmp(ptr, "AISPort") == 0) { HostPort = atoi(p_value); return TRUE; } if (_stricmp(ptr, "GPSDHost") == 0) { if (strlen(p_value) > 70) return FALSE; strcpy(GPSDHost, p_value); return TRUE; } if (_stricmp(ptr, "GPSDPort") == 0) { GPSDPort = atoi(p_value); return TRUE; } if (_stricmp(ptr, "ADSBHost") == 0) { if (strlen(p_value) > 70) return FALSE; strcpy(ADSBHost, p_value); return TRUE; } if (_stricmp(ptr, "ADSBPort") == 0) { ADSBPort = atoi(p_value); return TRUE; } if (_stricmp(ptr, "GPSSetsLocator") == 0) { GPSSetsLocator = atoi(p_value); return TRUE; } if (_stricmp(ptr, "LAT") == 0) { if (strlen(p_value) != 8) return FALSE; memcpy(LAT, _strupr(p_value), 8); PosnSet = TRUE; return TRUE; } if (_stricmp(ptr, "LON") == 0) { if (strlen(p_value) != 9) return FALSE; memcpy(LON, _strupr(p_value), 9); PosnSet = TRUE; return TRUE; } if (_stricmp(ptr, "SYMBOL") == 0) { if (p_value[0] > ' ' && p_value[0] < 0x7f) CFGSYMBOL = p_value[0]; return TRUE; } if (_stricmp(ptr, "SYMSET") == 0) { CFGSYMSET = p_value[0]; return TRUE; } if (_stricmp(ptr, "PHG") == 0) { PHG = _strdup(p_value); return TRUE; } if (_stricmp(ptr, "MaxTraceHops") == 0) { MaxTraceHops = atoi(p_value); return TRUE; } if (_stricmp(ptr, "MaxFloodHops") == 0) { MaxFloodHops = atoi(p_value); return TRUE; } if (_stricmp(ptr, "ISHOST") == 0) { strncpy(ISHost, p_value, 250); return TRUE; } if (_stricmp(ptr, "ISPORT") == 0) { ISPort = atoi(p_value); return TRUE; } if (_stricmp(ptr, "ISPASSCODE") == 0) { ISPasscode = atoi(p_value); return TRUE; } if (_stricmp(ptr, "MaxDigisforIS") == 0) { MaxDigisforIS = atoi(p_value); return TRUE; } if (_stricmp(ptr, "GateLocalDistance") == 0) { GateLocalDistance = atoi(p_value); if (GateLocalDistance > 0.0) GateLocal = TRUE; return TRUE; } if (_stricmp(ptr, "WXInterval") == 0) { WXInterval = atoi(p_value); WXCounter = (WXInterval - 1) * 60; return TRUE; } if (_stricmp(ptr, "WXPortList") == 0) { char ParamCopy[80]; char * Context; int Port; char * ptr; int index = 0; for (index = 0; index < MaxBPQPortNo; index++) WXPort[index] = FALSE; if (strlen(p_value) > 79) p_value[80] = 0; strcpy(ParamCopy, p_value); ptr = strtok_s(ParamCopy, " ,\t\n\r", &Context); while (ptr) { Port = atoi(ptr); // this gives zero for IS WXPort[Port] = TRUE; ptr = strtok_s(NULL, " ,\t\n\r", &Context); } return TRUE; } if (_stricmp(ptr, "Run") == 0) { strcpy(RunProgram, p_value); return TRUE; } // // Bad line // return (FALSE); } VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated); VOID SendAPRSMessage(char * Message, int toPort) { SendAPRSMessageEx(Message, toPort, APRSCall, 0); } // Ex allows setting source call (For WX Messages) VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated) { int Port; DIGIMESSAGE Msg; int Len; // toPort = -1 means all tadio ports. 0 = IS if (toPort == -1) { for (Port = 1; Port <= MaxBPQPortNo; Port++) { if (Gated && GatedHddrLen[Port]) memcpy(Msg.DEST, &GatedHeader[Port][0][0], 10 * 7); else if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); else continue; Msg.DEST[6] |= 0x80; // set Command Bit ConvToAX25(FromCall, Msg.ORIGIN); Msg.PID = 0xf0; Msg.CTL = 3; Len = sprintf(Msg.L2DATA, "%s", Message); Send_AX_Datagram(&Msg, Len + 2, Port); } return; } if (toPort == 0 && APRSISOpen) { char ISMsg[300]; Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", FromCall, APRSDest, Message); ISSend(sock, ISMsg, Len, 0); } if (toPort == 0) return; if (Gated && GatedHddrLen[toPort]) memcpy(Msg.DEST, &GatedHeader[toPort][0][0], 10 * 7); else if (BeaconHddrLen[toPort]) // Only send to ports with a DEST defined memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); else return; Msg.DEST[6] |= 0x80; // set Command Bit ConvToAX25(FromCall, Msg.ORIGIN); Msg.PID = 0xf0; Msg.CTL = 3; Len = sprintf(Msg.L2DATA, "%s", Message); Send_AX_Datagram(&Msg, Len + 2, toPort); return; } VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis) { if (_memicmp(Query, "APRSS", 5) == 0) { char Message[255]; sprintf(Message, ":%-9s:%s", Origin, StatusMsg); SendAPRSMessage(Message, Port); return; } if (_memicmp(Query, "APRST", 5) == 0 || _memicmp(Query, "PING?", 5) == 0) { // Trace Route //:KH2ZV :?APRST :N8UR :KH2Z>APRS,DIGI1,WIDE*: //:G8BPQ-14 :Path - G8BPQ-14>APU25N char Message[255]; sprintf(Message, ":%-9s:Path - %s>%s", Origin, Origin, DestPlusDigis); SendAPRSMessage(Message, Port); return; } } VOID ProcessQuery(char * Query) { if (memcmp(Query, "IGATE?", 6) == 0) { IStatusCounter = (rand() & 31) + 5; // 5 - 36 secs delay return; } if (memcmp(Query, "APRS?", 5) == 0) { BeaconCounter = (rand() & 31) + 5; // 5 - 36 secs delay return; } } Dll VOID APIENTRY APISendBeacon() { BeaconCounter = 2; } typedef struct _BeaconParams { int toPort; char * BeaconText; BOOL SendStatus; BOOL SendSOGCOG; } Params; Params BeaconParams; void SendBeaconThread(void * Params); VOID SendBeacon(int toPort, char * BeaconText, BOOL SendStatus, BOOL SendSOGCOG) { // Send to IS if needed then start a thread to send to radio ports if (PosnSet == FALSE) return; if (APRSISOpen && toPort == 0 && BeacontoIS) { char SOGCOG[10] = ""; char ISMsg[300]; int Len; Debugprintf("Sending APRS Beacon to APRS-IS"); if (SendSOGCOG | (COG != 0.0)) sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); if (PHG) // Send PHG instead of SOG COG Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, PHG, BeaconText); else Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); ISSend(sock, ISMsg, Len, 0); Debugprintf(">%s", ISMsg); } BeaconParams.toPort = toPort; BeaconParams.BeaconText = BeaconText; BeaconParams.SendStatus = SendStatus; BeaconParams.SendSOGCOG = SendSOGCOG; _beginthread(SendBeaconThread, 0, (VOID *) &BeaconParams); } void SendBeaconThread(void * Param) { // runs as a thread so we can sleep() between calls // Params are passed via a param block Params * BeaconParams = (Params *)Param; int toPort = BeaconParams->toPort; char * BeaconText = BeaconParams->BeaconText; BOOL SendStatus = BeaconParams->SendStatus; BOOL SendSOGCOG = BeaconParams->SendSOGCOG; int Port; DIGIMESSAGE Msg; int Len; char SOGCOG[256] = ""; struct STATIONRECORD * Station; struct PORTCONTROL * PORT; if (PosnSet == FALSE) return; if (PHG) // Send PHG instead of SOG COG strcpy(SOGCOG, PHG); else if (SendSOGCOG | (COG != 0.0)) sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); BeaconCounter = BeaconInterval * 60; if (ISPort && IGateEnabled) Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); else Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); Msg.PID = 0xf0; Msg.CTL = 3; // Add to dup check list, so we won't digi it if we hear it back // Should we drop it if we've sent it recently ?? if (CheckforDups(APRSCall, Msg.L2DATA, Len - (19 + sizeof(void *)))) return; // Add to our station list Station = FindStation(APRSCall, TRUE); if (Station == NULL) return; strcpy(Station->Path, "APBPQ1"); strcpy(Station->LastPacket, Msg.L2DATA); // Station->LastPort = Port; DecodeAPRSPayload(Msg.L2DATA, Station); Station->TimeLastUpdated = time(NULL); if (toPort) { if (BeaconHddrLen[toPort] == 0) return; Debugprintf("Sending APRS Beacon to port %d", toPort); memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); // Clear unused digis Msg.DEST[6] |= 0x80; // set Command Bit GetSemaphore(&Semaphore, 12); Send_AX_Datagram(&Msg, Len + 2, toPort); FreeSemaphore(&Semaphore); return; } for (Port = 1; Port <= MaxBPQPortNo; Port++) // Check all ports { if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined { Debugprintf("Sending APRS Beacon to port %d", Port); if (ISPort && IGateEnabled) Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); else Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); Msg.PID = 0xf0; Msg.CTL = 3; memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); Msg.DEST[6] |= 0x80; // set Command Bit GetSemaphore(&Semaphore, 12); Send_AX_Datagram(&Msg, Len + 2, Port); FreeSemaphore(&Semaphore); // if Port has interlock set pause before next PORT = GetPortTableEntryFromPortNum(Port); // Just pause for all ports // if (PORT && PORT->PORTINTERLOCK) Sleep(20000); } } return ; } VOID SendObject(struct OBJECT * Object) { int Port; DIGIMESSAGE Msg; int Len; // Add to dup list in case we get it back CheckforDups(APRSCall, Object->Message, (int)strlen(Object->Message)); for (Port = 1; Port <= MaxBPQPortNo; Port++) { if (Object->PortMap[Port]) { Msg.PID = 0xf0; Msg.CTL = 3; Len = sprintf(Msg.L2DATA, "%s", Object->Message); memcpy(Msg.DEST, &Object->Path[0][0], Object->PathLen + 1); Msg.DEST[6] |= 0x80; // set Command Bit Send_AX_Datagram(&Msg, Len + 2, Port); } } // Also send to APRS-IS if connected if (APRSISOpen && Object->PortMap[0]) { char ISMsg[300]; Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", APRSCall, APRSDest, Object->Message); ISSend(sock, ISMsg, Len, 0); } } /* VOID SendStatus(char * StatusText) { int Port; DIGIMESSAGE Msg; int Len; if (APRSISOpen) { Msg.PID = 0xf0; Msg.CTL = 3; Len = sprintf(Msg.L2DATA, ">%s", StatusText); for (Port = 1; Port <= NUMBEROFPORTS; Port++) { if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined { memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); Send_AX_Datagram(&Msg, Len + 2, Port); } } Len = sprintf(Msg.L2DATA, "%s>%s,TCPIP*:>%s\r\n", APRSCall, APRSDest, StatusText); ISSend(sock, Msg.L2DATA, Len, 0); // Debugprintf(">%s", Msg.L2DATA); } } */ VOID SendIStatus() { int Port; DIGIMESSAGE Msg; int Len; IStatusCounter = 3600; // One per hour if (APRSISOpen && BeacontoIS && RXOnly == 0) { Msg.PID = 0xf0; Msg.CTL = 3; Len = sprintf(Msg.L2DATA, "%s,TCPIP*:%s", Msg.L2DATA); } } VOID DoSecTimer() { struct OBJECT * Object = ObjectList; while (Object) { Object->Timer--; if (Object->Timer == 0) { Object->Timer = 60 * Object->Interval; SendObject(Object); } Object = Object->Next; } // Check SatGate Mode delay Q if (SatISQueue) { time_t NOW = time(NULL); ISDELAY * SatISEntry = SatISQueue; ISDELAY * Prev = NULL; while (SatISEntry) { if (SatISEntry->SendTIme < NOW) { // Send it ISSend(sock, SatISEntry->ISMSG, (int)strlen(SatISEntry->ISMSG), 0); free(SatISEntry->ISMSG); if (Prev) Prev->Next = SatISEntry->Next; else SatISQueue = SatISEntry->Next; free(SatISEntry); return; // unlinkely to get 2 in sam esecond and doesn;t matter if we delay a bit more } Prev = SatISEntry; SatISEntry = SatISEntry->Next; } } if (ISPort && APRSISOpen == 0 && IGateEnabled) { ISDelayTimer++; if (ISDelayTimer > 60) { ISDelayTimer = 0; _beginthread(APRSISThread, 0, (VOID *) TRUE); } } if (HostName[0]) { if (BlueNMEAOK == 0) { BlueNMEATimer++; if (BlueNMEATimer > 15) { BlueNMEATimer = 0; _beginthread(TCPConnect, 0, 0); } } } if (GPSDHost[0]) { if (GPSDOK == 0) { GPSDTimer++; if (GPSDTimer > 15) { GPSDTimer = 0; _beginthread(GPSDConnect, 0, 0); } } } if (BeaconCounter) { BeaconCounter--; if (BeaconCounter == 0) { BeaconCounter = BeaconInterval * 60; SendBeacon(0, StatusMsg, TRUE, FALSE); } } if (IStatusCounter) { IStatusCounter--; if (IStatusCounter == 0) { SendIStatus(); } } if (GPSOK) { GPSOK--; if (GPSOK == 0) #ifdef LINBPQ Debugprintf("GPS Lost"); #else SetDlgItemText(hConsWnd, IDC_GPS, "No GPS"); #endif } APRSSecTimer(); // Code from APRS APPL } int CountPool() { struct STATIONRECORD * ptr = StationRecordPool; int n = 0; while (ptr) { n++; ptr = ptr->Next; } return n; } static VOID DoMinTimer() { struct STATIONRECORD * ptr = *StationRecords; struct STATIONRECORD * last = NULL; time_t AgeLimit = time(NULL ) - (ExpireTime * 60); int i = 0; // Remove old records while (ptr) { if (ptr->TimeLastUpdated < AgeLimit) { StationCount--; if (last) { last->Next = ptr->Next; // Put on front of free chain ptr->Next = StationRecordPool; StationRecordPool = ptr; ptr = last->Next; } else { // First in list *StationRecords = ptr->Next; // Put on front of free chain ptr->Next = StationRecordPool; StationRecordPool = ptr; if (*StationRecords) { ptr = *StationRecords; } else { ptr = NULL; } } } else { last = ptr; ptr = ptr->Next; } } } char APRSMsg[300]; int ISHostIndex = 0; char RealISHost[256]; VOID APRSISThread(void * Report) { // Receive from core server char Signon[500]; unsigned char work[4]; struct sockaddr_in sinx; int addrlen=sizeof(sinx); struct addrinfo hints, *res = 0, *saveres; size_t len; int err; u_long param=1; BOOL bcopt=TRUE; char Buffer[1000]; int InputLen = 1; // Non-zero char errmsg[100]; char * ptr; size_t inptr = 0; char APRSinMsg[1000]; char PortString[20]; char serv[256]; Debugprintf("BPQ32 APRS IS Thread"); #ifndef LINBPQ SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Connecting"); #endif if (ISFilter[0]) sprintf(Signon, "user %s pass %d vers BPQ32 %s filter %s\r\n", APRSCall, ISPasscode, TextVerstring, ISFilter); else sprintf(Signon, "user %s pass %d vers BPQ32 %s\r\n", APRSCall, ISPasscode, TextVerstring); sprintf(PortString, "%d", ISPort); // get host info, make socket, and connect it memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever hints.ai_socktype = SOCK_STREAM; getaddrinfo(ISHost, PortString, &hints, &res); InputLen = sprintf(errmsg, "Connecting to APRS Host %s\r\n", ISHost); MonitorAPRSIS(errmsg, InputLen, FALSE); if (!res) { err = WSAGetLastError(); InputLen = sprintf(errmsg, "APRS IS Resolve %s Failed Error %d\r\n", ISHost, err); MonitorAPRSIS(errmsg, InputLen, FALSE); return; // Resolve failed } // Step thorough the list of hosts saveres = res; // Save for free if (res->ai_next) // More than one { int n = ISHostIndex; while (n && res->ai_next) { res = res->ai_next; n--; } if (n) { // We have run off the end of the list ISHostIndex = 0; // Back to start res = saveres; } else ISHostIndex++; } getnameinfo(res->ai_addr, (int)res->ai_addrlen, RealISHost, 256, serv, 256, 0); sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock == INVALID_SOCKET) return; setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); memcpy(work, res->ai_addr->sa_data, 4); Debugprintf("Trying APRSIS Host %d.%d.%d.%d (%d) %s", work[0], work[1], work[2], work[3], ISHostIndex, RealISHost); if (connect(sock, res->ai_addr, (int)res->ai_addrlen)) { err=WSAGetLastError(); // // Connect failed // #ifndef LINBPQ MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connect Failed"); #else printf("APRS Igate connect failed\n"); #endif err=WSAGetLastError(); InputLen = sprintf(errmsg, "Connect Failed %s af %d Error %d \r\n", RealISHost, res->ai_family, err); MonitorAPRSIS(errmsg, InputLen, FALSE); freeaddrinfo(res); return; } freeaddrinfo(saveres); APRSISOpen = TRUE; #ifndef LINBPQ MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connected"); #endif InputLen=recv(sock, Buffer, 500, 0); if (InputLen > 0) { Buffer[InputLen] = 0; Debugprintf(Buffer); MonitorAPRSIS(Buffer, InputLen, FALSE); } ISSend(sock, Signon, (int)strlen(Signon), 0); /* InputLen=recv(sock, Buffer, 500, 0); if (InputLen > 0) { Buffer[InputLen] = 0; Debugprintf(Buffer); MonitorAPRSIS(Buffer, InputLen, FALSE); } InputLen=recv(sock, Buffer, 500, 0); if (InputLen > 0) { Buffer[InputLen] = 0; Debugprintf(Buffer); MonitorAPRSIS(Buffer, InputLen, FALSE); } */ while (InputLen > 0 && IGateEnabled) { InputLen = recv(sock, &APRSinMsg[inptr], (int)(500 - inptr), 0); if (InputLen > 0) { inptr += InputLen; ptr = memchr(APRSinMsg, 0x0a, inptr); while (ptr != NULL) { ptr++; // include lf len = ptr-(char *)APRSinMsg; inptr -= len; // bytes left // UIView server has a null before crlf if (*(ptr - 3) == 0) { *(ptr - 3) = 13; *(ptr - 2) = 10; *(ptr - 1) = 0; len --; } if (len > 10 && len < 300) // Ignore if way too long or too short { memcpy(&APRSMsg, APRSinMsg, len); MonitorAPRSIS(APRSMsg, (int)len, FALSE); if (APRSMsg[len - 2] == 13) APRSMsg[len - 2] = 0; else APRSMsg[len - 1] = 0; // Debugprintf("%s", APRSMsg); ProcessAPRSISMsg(APRSMsg); } if (inptr > 0) { memmove(APRSinMsg, ptr, inptr); ptr = memchr(APRSinMsg, 0x0a, inptr); } else ptr = 0; if (inptr < 0) break; } } } closesocket(sock); APRSISOpen = FALSE; Debugprintf("BPQ32 APRS IS Thread Exited"); #ifndef LINBPQ if (IGateEnabled) SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disconnected"); else SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disabled"); #endif ISDelayTimer = 30; // Retry pretty quickly return; } VOID ProcessAPRSISMsg(char * APRSMsg) { char * Payload; char * Source; char * Dest; char IGateCall[10] = " "; char * ptr; char Message[255]; PAPRSHEARDRECORD MH; time_t NOW = time(NULL); char ISCopy[1024]; struct STATIONRECORD * Station = NULL; #ifdef WIN32 struct _EXCEPTION_POINTERS exinfo; char EXCEPTMSG[80] = ""; #endif if (APRSMsg[0] == '#') // Comment return; // if APRS Appl is atttached, queue message to it strcpy(ISCopy, APRSMsg); GetSemaphore(&Semaphore, 12); #ifdef WIN32 strcpy(EXCEPTMSG, "ProcessAPRSISMsg"); __try { Station = DecodeAPRSISMsg(ISCopy); } #include "StdExcept.c" Debugprintf(APRSMsg); } #else Station = DecodeAPRSISMsg(ISCopy); #endif FreeSemaphore(&Semaphore); //}WB4APR-14>APRS,RELAY,TCPIP,G9RXG*::G3NRWVVVV:Hi Ian{001 //KE7XO-2>hg,TCPIP*,qAC,T2USASW::G8BPQ-14 :Path - G8BPQ-14>APU25N //IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data Payload = strchr(APRSMsg, ':'); // Get call of originating Igate ptr = Payload; if (Payload == NULL) return; *(Payload++) = 0; while (ptr[0] != ',') ptr--; ptr++; if (strlen(ptr) > 9) return; memcpy(IGateCall, ptr, (int)strlen(ptr)); if (strstr(APRSMsg, ",qAS,") == 0) // Findu generates invalid q construct { MH = FindStationInMH(IGateCall); if (MH) { // Debugprintf("Setting Igate Flag - %s:%s", APRSMsg, Payload); MH->IGate = TRUE; // If we have seen this station on RF, set it as an Igate } } Source = APRSMsg; Dest = strchr(APRSMsg, '>'); if (Dest == NULL) return; *(Dest++) = 0; // Termainate Source ptr = strchr(Dest, ','); if (ptr) *ptr = 0; MH = UpdateHeard(Source, 0); MH->Station = Station; // See if we should gate to RF. // Have we heard dest recently? (use the message dest (not ax.25 dest) - does this mean we only gate Messages? // Not if it is an Igate (it will get a copy direct) // Have we recently sent a message from this call - if so, we gate the next Position /* From http://www.aprs-is.net/IGateDetails.aspx Gate message packets and associated posits to RF if all of the following are true: the receiving station has been heard within range within a predefined time period (range defined as digi hops, distance, or both). the sending station has not been heard via RF within a predefined time period (packets gated from the Internet by other stations are excluded from this test). the sending station does not have TCPXX, NOGATE, or RFONLY in the header. the receiving station has not been heard via the Internet within a predefined time period. A station is said to be heard via the Internet if packets from the station contain TCPIP* or TCPXX* in the header or if gated (3rd-party) packets are seen on RF gated by the station and containing TCPIP or TCPXX in the 3rd-party header (in other words, the station is seen on RF as being an IGate). */ if (Payload[0] == ':') // Message { char MsgDest[10]; APRSHEARDRECORD * STN; if (strlen(Payload) > 100) // I don't think any valid APRS msgs are more than this return; memcpy(MsgDest, &Payload[1], 9); MsgDest[9] = 0; if (strcmp(MsgDest, CallPadded) == 0) // to us return; // Check that the sending station has not been heard via RF recently if (MH->rfPort && (NOW - MH->MHTIME) < GATETIMELIMIT) return; STN = FindStationInMH(MsgDest); // Shouldn't we check DUP list, in case we have digi'ed this message directly? if (CheckforDups(Source, Payload, (int)strlen(Payload))) return; // has the receiving station has been heard on RF and is not an IGate if (STN && STN->rfPort && !STN->IGate && (NOW - STN->MHTIME) < GATETIMELIMIT) { sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); GetSemaphore(&Semaphore, 12); SendAPRSMessageEx(Message, STN->rfPort, APRSCall, 1); // Set gated to IS flag FreeSemaphore(&Semaphore); MessageCount++; MH->LASTMSG = NOW; return; } } // Not a message. If it is a position report gate if have sent a message recently if (Payload[0] == '!' || Payload[0] == '/' || Payload[0] == '=' || Payload[0] == '@') // Posn Reports { if ((NOW - MH->LASTMSG) < 900 && MH->rfPort) { sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); GetSemaphore(&Semaphore, 12); SendAPRSMessageEx(Message, MH->rfPort, APRSCall, 1); // Set gated to IS flag FreeSemaphore(&Semaphore); return; } } // If Gate Local to RF is defined, and station is in range, Gate it if (GateLocal && Station) { if (Station->Object) Station = Station->Object; // If Object Report, base distance on Object, not station if (Station->Lat != 0.0 && Station->Lon != 0.0 && myDistance(Station->Lat, Station->Lon, 0) < GateLocalDistance) { sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); GetSemaphore(&Semaphore, 12); SendAPRSMessage(Message, -1); // Send to all APRS Ports FreeSemaphore(&Semaphore); return; } } } APRSHEARDRECORD * FindStationInMH(char * Call) { APRSHEARDRECORD * MH = MHDATA; int i; // We keep call in ascii format, as that is what we get from APRS-IS, and we have it in that form for (i = 0; i < HEARDENTRIES; i++) { if (memcmp(Call, MH->MHCALL, 9) == 0) return MH; MH++; } return NULL; } APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port) { APRSHEARDRECORD * MH = MHDATA; APRSHEARDRECORD * MHBASE = MH; int i; time_t NOW = time(NULL); time_t OLDEST = NOW - MAXAGE; char CallPadded[10] = " "; BOOL SaveIGate = FALSE; time_t SaveLastMsg = 0; int SaveheardViaIS = 0; // We keep call in ascii format, space padded, as that is what we get from APRS-IS, and we have it in that form // Make Sure Space Padded memcpy(CallPadded, Call, (int)strlen(Call)); for (i = 0; i < MAXHEARDENTRIES; i++) { if (memcmp(CallPadded, MH->MHCALL, 10) == 0) { // if from APRS-IS, only update if record hasn't been heard via RF if (Port == 0) MH->heardViaIS = 1; // Flag heard via IS if (Port == 0 && MH->rfPort) return MH; // Don't update RF with IS if (Port == MH->rfPort) { SaveIGate = MH->IGate; SaveLastMsg = MH->LASTMSG; SaveheardViaIS = MH->heardViaIS; goto DoMove; } } if (MH->MHCALL[0] == 0 || MH->MHTIME < OLDEST) // Spare entry goto DoMove; MH++; } // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP i = MAXHEARDENTRIES - 1; // Move others down and add at front DoMove: if (i != 0) // First memmove(MHBASE + 1, MHBASE, i * sizeof(APRSHEARDRECORD)); if (i >= HEARDENTRIES) { char Status[80]; HEARDENTRIES = i + 1; sprintf(Status, "IGATE Stats: Msgs %d Local Stns %d", MessageCount , CountLocalStations()); #ifndef LINBPQ SetDlgItemText(hConsWnd, IGATESTATS, Status); #endif } memcpy (MHBASE->MHCALL, CallPadded, 10); MHBASE->rfPort = Port; MHBASE->MHTIME = NOW; MHBASE->IGate = SaveIGate; MHBASE->LASTMSG = SaveLastMsg; MHBASE->heardViaIS = SaveheardViaIS; return MHBASE; } int CountLocalStations() { APRSHEARDRECORD * MH = MHDATA; int i, n = 0; for (i = 0; i < HEARDENTRIES; i++) { if (MH->rfPort) // DOn't count IS Stations n++; MH++; } return n; } BOOL CheckforDups(char * Call, char * Msg, int Len) { // Primitive duplicate suppression - see if same call and text reeived in last few seconds time_t Now = time(NULL); time_t DupCheck = Now - DUPSECONDS; int i, saveindex = -1; char * ptr1; if (Len < 1) return TRUE; for (i = 0; i < MAXDUPS; i++) { if (DupInfo[i].DupTime < DupCheck) { // too old - use first if we need to save it if (saveindex == -1) { saveindex = i; } if (DupInfo[i].DupTime == 0) // Off end of used area break; continue; } if ((Len == DupInfo[i].DupLen || (DupInfo[i].DupLen == 99 && Len > 99)) && memcmp(Call, DupInfo[i].DupUser, 7) == 0 && (memcmp(Msg, DupInfo[i].DupText, DupInfo[i].DupLen) == 0)) { // Duplicate, so discard Msg[Len] = 0; ptr1 = strchr(Msg, 13); if (ptr1) *ptr1 = 0; // Debugprintf("Duplicate Message supressed %s", Msg); return TRUE; // Duplicate } } // Not in list if (saveindex == -1) // List is full saveindex = MAXDUPS - 1; // Stick on end DupInfo[saveindex].DupTime = Now; memcpy(DupInfo[saveindex].DupUser, Call, 7); if (Len > 99) Len = 99; DupInfo[saveindex].DupLen = Len; memcpy(DupInfo[saveindex].DupText, Msg, Len); return FALSE; } char * FormatAPRSMH(APRSHEARDRECORD * MH) { // Called from CMD.ASM struct tm * TM; static char MHLine[50]; time_t szClock = MH->MHTIME; szClock = (time(NULL) - szClock); TM = gmtime(&szClock); sprintf(MHLine, "%-10s %d %.2d:%.2d:%.2d:%.2d %s\r", MH->MHCALL, MH->rfPort, TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, (MH->IGate) ? "IGATE" : ""); return MHLine; } // GPS Handling Code void SelectSource(BOOL Recovering); void DecodeRMC(char * msg, size_t len); void PollGPSIn(); UINT GPSType = 0xffff; // Source of Postion info - 1 = Phillips 2 = AIT1000. ffff = not posn message int RecoveryTimer; // Serial Port recovery double PI = 3.1415926535; double P2 = 3.1415926535 / 180; double Latitude, Longtitude, SOG, COG, LatIncrement, LongIncrement; double LastSOG = -1.0; BOOL Check0183CheckSum(char * msg, size_t len) { BOOL retcode=TRUE; char * ptr; UCHAR sum,xsum1,xsum2; sum=0; ptr=++msg; // Skip $ loop: if (*(ptr)=='*') goto eom; sum ^=*(ptr++); len--; if (len > 0) goto loop; return TRUE; // No Checksum eom: _strupr(ptr); xsum1=*(++ptr); xsum1-=0x30; if (xsum1 > 9) xsum1-=7; xsum2=*(++ptr); xsum2-=0x30; if (xsum2 > 9) xsum2-=7; xsum1=xsum1<<4; xsum1+=xsum2; return (xsum1==sum); } BOOL OpenGPSPort() { struct PortInfo * portptr = &InPorts[0]; // open COMM device if (strlen(GPSPort) < 4) { int port = atoi(GPSPort); #ifdef WIN32 sprintf(GPSPort, "COM%d", port); #else sprintf(GPSPort, "com%d", port); #endif } portptr->hDevice = OpenCOMPort(GPSPort, GPSSpeed, TRUE, TRUE, FALSE, 0); if (portptr->hDevice == 0) { return FALSE; } return TRUE; } void PollGPSIn() { size_t len; char GPSMsg[2000] = "$GPRMC,061213.000,A,5151.5021,N,00056.8388,E,0.15,324.11,190414,,,A*6F"; char * ptr; struct PortInfo * portptr; portptr = &InPorts[0]; if (!portptr->hDevice) return; getgpsin: // Comm Error - probably lost USB Port. Try closing and reopening after a delay // if (RecoveryTimer == 0) // { // RecoveryTimer = 100; // 10 Secs // return; // } // } if (portptr->gpsinptr == 160) portptr->gpsinptr = 0; len = ReadCOMBlock(portptr->hDevice, &portptr->GPSinMsg[portptr->gpsinptr], 160 - portptr->gpsinptr); if (len > 0) { portptr->gpsinptr += (int)len; ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); while (ptr != NULL) { ptr++; // include lf len=ptr-(char *)&portptr->GPSinMsg; memcpy(&GPSMsg,portptr->GPSinMsg,len); GPSMsg[len] = 0; if (Check0183CheckSum(GPSMsg, len)) if (memcmp(&GPSMsg[3], "RMC", 3) == 0) DecodeRMC(GPSMsg, len); portptr->gpsinptr -= (int)len; // bytes left if (portptr->gpsinptr > 0 && *ptr == 0) { *ptr++; portptr->gpsinptr--; } if (portptr->gpsinptr > 0) { memmove(portptr->GPSinMsg,ptr, portptr->gpsinptr); ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); } else ptr=0; } goto getgpsin; } return; } void ClosePorts() { if (InPorts[0].hDevice) { CloseCOMPort(InPorts[0].hDevice); InPorts[0].hDevice=0; } return; } void DecodeRMC(char * msg, size_t len) { char * ptr1; char * ptr2; char TimHH[3], TimMM[3], TimSS[3]; char OurSog[5], OurCog[4]; char LatDeg[3], LonDeg[4]; char NewLat[10] = "", NewLon[10] = ""; struct STATIONRECORD * Stn1; char Day[3]; ptr1 = &msg[7]; len-=7; ptr2=(char *)memchr(ptr1,',',15); if (ptr2 == 0) return; // Duff *(ptr2++)=0; memcpy(TimHH,ptr1,2); memcpy(TimMM,ptr1+2,2); memcpy(TimSS,ptr1+4,2); TimHH[2]=0; TimMM[2]=0; TimSS[2]=0; ptr1=ptr2; if (*(ptr1) != 'A') // ' Data Not Valid { #ifndef LINBPQ SetDlgItemText(hConsWnd, IDC_GPS, "No GPS Fix"); #endif return; } ptr1+=2; ptr2=(char *)memchr(ptr1,',',15); if (ptr2 == 0) return; // Duff *(ptr2++)=0; memcpy(NewLat, ptr1, 7); memcpy(LatDeg, ptr1, 2); LatDeg[2]=0; Lat=atof(LatDeg) + (atof(ptr1+2)/60); if (*(ptr1+7) > '4') if (NewLat[6] < '9') NewLat[6]++; ptr1=ptr2; NewLat[7] = (*ptr1); if ((*ptr1) == 'S') Lat=-Lat; ptr1+=2; ptr2=(char *)memchr(ptr1,',',15); if (ptr2 == 0) return; // Duff *(ptr2++)=0; memcpy(NewLon, ptr1, 8); memcpy(LonDeg,ptr1,3); LonDeg[3]=0; Lon=atof(LonDeg) + (atof(ptr1+3)/60); if (*(ptr1+8) > '4') if (NewLon[7] < '9') NewLon[7]++; ptr1=ptr2; NewLon[8] = (*ptr1); if ((*ptr1) == 'W') Lon=-Lon; // Now have a valid posn, so stop sending Undefined LOC Sysbol SYMBOL = CFGSYMBOL; SYMSET = CFGSYMSET; PosnSet = TRUE; Stn1 = (struct STATIONRECORD *)StnRecordBase; // Pass to App Stn1->Lat = Lat; Stn1->Lon = Lon; if (GPSOK == 0) { #ifdef LINBPQ Debugprintf("GPS OK"); printf("GPS OK\n"); #else SetDlgItemText(hConsWnd, IDC_GPS, "GPS OK"); #endif } GPSOK = 30; ptr1+=2; ptr2 = (char *)memchr(ptr1,',',30); if (ptr2 == 0) return; // Duff *(ptr2++)=0; memcpy(OurSog, ptr1, 4); OurSog[4] = 0; ptr1=ptr2; ptr2 = (char *)memchr(ptr1,',',15); if (ptr2 == 0) return; // Duff *(ptr2++)=0; memcpy(OurCog, ptr1, 3); OurCog[3] = 0; memcpy(Day,ptr2,2); Day[2]=0; SOG = atof(OurSog); COG = atof(OurCog); if (strcmp(NewLat, LAT) || strcmp(NewLon, LON)) { if (MobileBeaconInterval) { time_t NOW = time(NULL); if ((NOW - LastMobileBeacon) > MobileBeaconInterval) { LastMobileBeacon = NOW; SendBeacon(0, StatusMsg, FALSE, TRUE); } } if (GPSSetsLocator) { ToLOC(Lat, Lon, LOC); sprintf(LOCATOR, "%f:%f", Lat, Lon); } } strcpy(LAT, NewLat); strcpy(LON, NewLon); } Dll VOID APIENTRY APRSConnect(char * Call, char * Filter) { // Request APRS Data from Switch (called by APRS Applications) APRSApplConnected = TRUE; APRSWeb = TRUE; strcpy(APPLFilter, Filter); if (APPLFilter[0]) { // This is called in APPL context so must queue the message char Msg[2000]; PMSGWITHLEN buffptr; sprintf(Msg, "filter %s", Filter); if (strlen(Msg) > 240) Msg[240] = 0; GetSemaphore(&Semaphore, 11); buffptr = GetBuff(); if (buffptr) { buffptr->Len = 0; strcpy(&buffptr->Data[0], "SERVER"); strcpy(&buffptr->Data[10], Msg); C_Q_ADD(&APPLTX_Q, buffptr); } buffptr = GetBuff(); if (buffptr) { buffptr->Len = 0; strcpy(&buffptr->Data[0], "SERVER"); strcpy(&buffptr->Data[10], "filter?"); C_Q_ADD(&APPLTX_Q, buffptr); } FreeSemaphore(&Semaphore); } strcpy(Call, CallPadded); } Dll VOID APIENTRY APRSDisconnect() { // Stop requesting APRS Data from Switch (called by APRS Applications) char Msg[2000]; PMSGWITHLEN buffptr; strcpy(ISFilter, NodeFilter); sprintf(Msg, "filter %s", NodeFilter); APRSApplConnected = FALSE; APRSWeb = FALSE; GetSemaphore(&Semaphore, 11); buffptr = GetBuff(); if (buffptr) { buffptr->Len = 0; strcpy(&buffptr->Data[0], "SERVER"); strcpy(&buffptr->Data[10], Msg); C_Q_ADD(&APPLTX_Q, buffptr); } buffptr = GetBuff(); if (buffptr) { buffptr->Len = 0; strcpy(&buffptr->Data[0], "SERVER"); strcpy(&buffptr->Data[10], "filter?"); C_Q_ADD(&APPLTX_Q, buffptr); } while (APPL_Q) { buffptr = Q_REM(&APPL_Q); ReleaseBuffer(buffptr); } FreeSemaphore(&Semaphore); } Dll char * APIENTRY APRSGetStatusMsgPtr() { return StatusMsg; } Dll BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call) { // Request APRS Data from Switch (called by APRS Applications) void ** buffptr; #ifdef bpq32 struct _EXCEPTION_POINTERS exinfo; #endif GetSemaphore(&Semaphore, 10); { if (APPL_Q) { buffptr = Q_REM(&APPL_Q); memcpy(Call, (char *)&buffptr[2], 12); strcpy(Frame, (char *)&buffptr[5]); ReleaseBuffer(buffptr); FreeSemaphore(&Semaphore); return TRUE; } } FreeSemaphore(&Semaphore); return FALSE; } Dll BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port) { // Called from BPQAPRS App // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) PMSGWITHLEN buffptr; GetSemaphore(&Semaphore, 11); buffptr = GetBuff(); if (buffptr) { buffptr->Len = ++Len; // Len doesn't include Null memcpy(&buffptr->Data[0], Frame, Len); C_Q_ADD(&APPLTX_Q, buffptr); } // buffptr-> = Port; // Pass to SendAPRSMessage(); FreeSemaphore(&Semaphore); return TRUE; } Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall) { // Called from BPQAPRS App or BPQMail // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) PMSGWITHLEN buffptr; if (APRSActive == 0) return FALSE; GetSemaphore(&Semaphore, 11); buffptr = GetBuff(); if (buffptr) { buffptr->Len = 0; memcpy(&buffptr->Data[0], ToCall, 9); buffptr->Data[9] = 0; strcpy(&buffptr->Data[10], Text); C_Q_ADD(&APPLTX_Q, buffptr); } FreeSemaphore(&Semaphore); return TRUE; } Dll BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon) { *PLat = Lat; *PLon = Lon; return GPSOK; } Dll BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon) { strcpy(PLat, LAT); strcpy(PLon, LON); return GPSOK; } // Code to support getting GPS from Andriod Device running BlueNMEA #define SD_BOTH 0x02 static VOID ProcessReceivedData(SOCKET TCPSock) { char UDPMsg[8192]; char Buffer[65536]; int len = recv(TCPSock, Buffer, 65500, 0); char * ptr; char * Lastptr; if (len <= 0) { closesocket(TCPSock); BlueNMEAOK = FALSE; return; } ptr = Lastptr = Buffer; Buffer[len] = 0; while (len > 0) { ptr = strchr(Lastptr, 10); if (ptr) { size_t Len = ptr - Lastptr -1; if (Len > 8100) return; memcpy(UDPMsg, Lastptr, Len); UDPMsg[Len++] = 13; UDPMsg[Len++] = 10; UDPMsg[Len] = 0; if (!Check0183CheckSum(UDPMsg, Len)) { Debugprintf("Checksum Error %s", UDPMsg); } else { if (memcmp(&UDPMsg[3], "RMC", 3) == 0) DecodeRMC(UDPMsg, Len); else if (memcmp(UDPMsg, "!AIVDM", 6) == 0) ProcessAISMessage(UDPMsg, Len); } Lastptr = ptr + 1; len -= (int)Len; } else return; } } static VOID TCPConnect(void * unused) { int err, ret; u_long param=1; BOOL bcopt=TRUE; fd_set readfs; fd_set errorfs; struct timeval timeout; struct sockaddr_in destaddr; SOCKET TCPSock; if (HostName[0] == 0) return; destaddr.sin_addr.s_addr = inet_addr(HostName); destaddr.sin_family = AF_INET; destaddr.sin_port = htons(HostPort); if (destaddr.sin_addr.s_addr == INADDR_NONE) { // Resolve name to address struct hostent * HostEnt = gethostbyname(HostName); if (!HostEnt) return; // Resolve failed memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); } TCPSock = socket(AF_INET,SOCK_STREAM,0); if (TCPSock == INVALID_SOCKET) { return; } setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); BlueNMEAOK = TRUE; // So we don't try to reconnect while waiting if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) { // // Connected successful // ioctl(TCPSock, FIONBIO, ¶m); } else { err=WSAGetLastError(); #ifdef LINBPQ printf("Connect Failed for AIS socket - error code = %d\n", err); #else Debugprintf("Connect Failed for AIS socket - error code = %d", err); #endif closesocket(TCPSock); BlueNMEAOK = FALSE; return; } BlueNMEAOK = TRUE; while (TRUE) { FD_ZERO(&readfs); FD_ZERO(&errorfs); FD_SET(TCPSock,&readfs); FD_SET(TCPSock,&errorfs); timeout.tv_sec = 900; timeout.tv_usec = 0; // We should get messages more frequently that this ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); if (ret == SOCKET_ERROR) { goto Lost; } if (ret > 0) { // See what happened if (FD_ISSET(TCPSock, &readfs)) { ProcessReceivedData(TCPSock); } if (FD_ISSET(TCPSock, &errorfs)) { Lost: #ifdef LINBPQ printf("AIS Connection lost\n"); #endif closesocket(TCPSock); BlueNMEAOK = FALSE;; return; } } else { // 15 mins without data. Shouldn't happen shutdown(TCPSock, SD_BOTH); Sleep(100); closesocket(TCPSock); BlueNMEAOK = FALSE; return; } } } int GPSDAlerted = 0; static VOID GPSDConnect(void * unused) { int err, ret; u_long param=1; BOOL bcopt=TRUE; fd_set readfs; fd_set errorfs; struct timeval timeout; struct sockaddr_in destaddr; SOCKET TCPSock; if (GPSDHost[0] == 0) return; destaddr.sin_addr.s_addr = inet_addr(GPSDHost); destaddr.sin_family = AF_INET; destaddr.sin_port = htons(GPSDPort); TCPSock = socket(AF_INET,SOCK_STREAM,0); if (TCPSock == INVALID_SOCKET) { return; } setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); GPSDOK = TRUE; // So we don't try to reconnect while waiting if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) { // // Connected successful // #ifdef LINBPQ printf("GPSD Connected\n"); #else Debugprintf("GPSD Connected"); #endif GPSDAlerted = 0; ioctl(TCPSock, FIONBIO, ¶m); // Request data send(TCPSock, "?WATCH={\"enable\":true,\"nmea\":true}\r\n", 36, 0); } else { err=WSAGetLastError(); if (GPSDAlerted == 0) #ifdef LINBPQ printf("GPSD Connect Failed - error code = %d\n", err); #else Debugprintf("GPSD Connect Failed - error code = %d", err); #endif GPSDAlerted = 1; closesocket(TCPSock); GPSDOK = FALSE; return; } while (TRUE) { FD_ZERO(&readfs); FD_ZERO(&errorfs); FD_SET(TCPSock,&readfs); FD_SET(TCPSock,&errorfs); timeout.tv_sec = 900; timeout.tv_usec = 0; // We should get messages more frequently that this ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); if (ret == SOCKET_ERROR) { goto Lost; } if (ret > 0) { // See what happened if (FD_ISSET(TCPSock, &readfs)) { char Buffer[65536]; int len = recv(TCPSock, Buffer, 65500, 0); char TCPMsg[8192]; char * ptr; char * Lastptr; if (len == 0) { closesocket(TCPSock); GPSDOK = FALSE;; return; } if (len < 9000) { Buffer[len] = 0; ptr = Lastptr = Buffer; Buffer[len] = 0; while (len > 0) { ptr = strchr(Lastptr, 10); if (ptr) { size_t Len = ptr - Lastptr -1; if (Len > 8100) return; memcpy(TCPMsg, Lastptr, Len); TCPMsg[Len++] = 13; TCPMsg[Len++] = 10; TCPMsg[Len] = 0; if (!Check0183CheckSum(TCPMsg, Len)) { Debugprintf("Checksum Error %s", TCPMsg); } else { if (memcmp(&TCPMsg[3], "RMC", 3) == 0) DecodeRMC(TCPMsg, Len); } Lastptr = ptr + 1; len -= (int)Len; } else return; } } } if (FD_ISSET(TCPSock, &errorfs)) { Lost: #ifdef LINBPQ printf("GPSD Connection lost\n"); #endif closesocket(TCPSock); GPSDOK = FALSE;; return; } } else { // 15 mins without data. Shouldn't happen shutdown(TCPSock, SD_BOTH); Sleep(100); closesocket(TCPSock); GPSDOK = FALSE; return; } } } // Code Moved from APRS Application // // APRS Mapping and Messaging App for BPQ32 Switch. // VOID APIENTRY APRSConnect(char * Call, char * Filter); VOID APIENTRY APRSDisconnect(); BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call); BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port); BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); VOID APIENTRY APISendBeacon(); int NewLine(HWND hWnd); VOID ProcessBuff(HWND hWnd, MESSAGE * buff,int len,int stamp); int TogglePort(HWND hWnd, int Item, int mask); VOID SendFrame(UCHAR * buff, int txlen); int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); int KissDecode(UCHAR * inbuff, int len); //void UpdateStation(char * Call, char * Path, char * Comment, double V_Lat, double V_Lon, double V_SOG, double V_COG, int iconRow, int iconCol); VOID FindStationsByPixel(int MouseX, int MouseY); void RefreshStation(struct STATIONRECORD * ptr); void RefreshStationList(); void RefreshStationMap(); BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station); VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station); BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y); VOID APRSPoll(); VOID OSMThread(); VOID ResolveThread(); VOID RefreshTile(char * FN, int Zoom, int x, int y); int ProcessMessage(char * Payload, struct STATIONRECORD * Station); VOID APRSSecTimer(); double myBearing(double laa, double loa); BOOL CreatePipeThread(); VOID SendWeatherBeacon(); VOID DecodeWXPortList(); VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) { UCHAR * ptr = strchr(WX, '_'); char Type; int Val; if (ptr == 0) return; sockptr->WindDirn = atoi(++ptr); ptr += 4; sockptr->WindSpeed = atoi(ptr); ptr += 3; WXLoop: Type = *(ptr++); if (*ptr =='.') // Missing Value { while (*ptr == '.') ptr++; goto WXLoop; } Val = atoi(ptr); switch (Type) { case 'c': // = wind direction (in degrees). sockptr->WindDirn = Val; break; case 's': // = sustained one-minute wind speed (in mph). sockptr->WindSpeed = Val; break; case 'g': // = gust (peak wind speed in mph in the last 5 minutes). sockptr->WindGust = Val; break; case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. sockptr->Temp = Val; break; case 'r': // = rainfall (in hundredths of an inch) in the last hour. sockptr->RainLastHour = Val; break; case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. sockptr->RainLastDay = Val; break; case 'P': // = rainfall (in hundredths of an inch) since midnight. sockptr->RainToday = Val; break; case 'h': // = humidity (in %. 00 = 100%). sockptr->Humidity = Val; break; case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). sockptr->Pressure = Val; break; default: return; } while(isdigit(*ptr)) { ptr++; } if (*ptr != ' ') goto WXLoop; } static char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; //char Header[] = "Accept: */*\r\nHost: tile.openstreetmap.org\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; char APRSMsg[300]; Dll struct STATIONRECORD * APIENTRY APPLFindStation(char * Call, BOOL AddIfNotFount) { // Called from APRS Appl struct STATIONRECORD * Stn; GetSemaphore(&Semaphore, 12); Stn = FindStation(Call, AddIfNotFount) ; FreeSemaphore(&Semaphore); return Stn; } Dll struct APRSMESSAGE * APIENTRY APRSGetMessageBuffer() { struct APRSMESSAGE * ptr = MessageRecordPool; if (ptr == NULL) { // try getting oldest ptr = SMEM->Messages; if (ptr) { SMEM->Messages = ptr->Next; memset(ptr, 0, sizeof(struct APRSMESSAGE)); } return ptr; } if (ptr) { MessageRecordPool = ptr->Next; // Unchain MessageCount++; ptr->Next = NULL; memset(ptr, 0, sizeof(struct APRSMESSAGE)); } return ptr; } struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) { int i = 0; struct STATIONRECORD * find; struct STATIONRECORD * ptr; struct STATIONRECORD * last = NULL; int sum = 0; if (APRSActive == 0 || StationRecords == 0) return FALSE; if (strlen(Call) > 9) { Debugprintf("APRS Call too long %s", Call); Call[9] = 0; } find = *StationRecords; while(find) { if (strlen(find->Callsign) > 9) { Debugprintf("APRS Call in Station List too long %s", find->Callsign); find->Callsign[9] = 0; } if (strcmp(find->Callsign, Call) == 0) return find; last = find; find = find->Next; i++; } // Not found - add on end if (AddIfNotFount) { // Get first from station record pool ptr = StationRecordPool; if (ptr) { StationRecordPool = ptr->Next; // Unchain StationCount++; } else { // Get First from Stations ptr = *StationRecords; if (ptr) *StationRecords = ptr->Next; } if (ptr == NULL) return NULL; memset(ptr, 0, sizeof(struct STATIONRECORD)); // EnterCriticalSection(&Crit); if (*StationRecords == NULL) *StationRecords = ptr; else last->Next = ptr; // LeaveCriticalSection(&Crit); // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); strcpy(ptr->Callsign, Call); ptr->TimeLastUpdated = ptr->TimeAdded = time(NULL); ptr->Index = i; ptr->NoTracks = DefaultNoTracks; for (i = 0; i < 9; i++) sum += Call[i]; sum %= 20; ptr->TrackColour = sum; ptr->Moved = TRUE; return ptr; } else return NULL; } struct STATIONRECORD * ProcessRFFrame(char * Msg, int len, int * ourMessage) { char * Payload; char * Path = NULL; char * Comment = NULL; char * Callsign; char * ptr; int Port = 0; struct STATIONRECORD * Station = NULL; Msg[len - 1] = 0; // Debugprintf("RF Frame %s", Msg); Msg += 10; // Skip Timestamp Payload = strchr(Msg, ':'); // End of Address String if (Payload == NULL) { Debugprintf("Invalid Msg %s", Msg); return Station; } ptr = strstr(Msg, "Port="); if (ptr) Port = atoi(&ptr[5]); Payload++; if (*Payload != 0x0d) return Station; *Payload++ = 0; Callsign = Msg; Path = strchr(Msg, '>'); if (Path == NULL) { Debugprintf("Invalid Header %s", Msg); return Station; } *Path++ = 0; ptr = strchr(Path, ' '); if (ptr) *ptr = 0; // Look up station - create a new one if not found if (strcmp(Callsign, "AIS") == 0) { if (needAIS) { Payload += 3; ProcessAISMessage(Payload, strlen(Payload)); } else Debugprintf(Payload); return 0; } Station = FindStation(Callsign, TRUE); strcpy(Station->Path, Path); strcpy(Station->LastPacket, Payload); Station->LastPort = Port; *ourMessage = DecodeAPRSPayload(Payload, Station); Station->TimeLastUpdated = time(NULL); return Station; } /* 2E0AYY>APU25N,TCPIP*,qAC,AHUBSWE2:=5105.18N/00108.19E-Paul in Folkestone Kent {UIV32N} G0AVP-12>APT310,MB7UC*,WIDE3-2,qAR,G3PWJ:!5047.19N\00108.45Wk074/000/Paul mobile G0CJM-12>CQ,TCPIP*,qAC,AHUBSWE2:=/3&Rio94sg M0HFC>APRS,WIDE2-1,qAR,G0MNI:!5342.83N/00013.79W# Humber Fortress ARC Look us up on QRZ G8WVW-3>APTT4,WIDE1-1,WIDE2-1,qAS,G8WVW:T#063,123,036,000,000,000,00000000 */ struct STATIONRECORD * DecodeAPRSISMsg(char * Msg) { char * Payload; char * Path = NULL; char * Comment = NULL; char * Callsign; struct STATIONRECORD * Station = NULL; // Debugprintf(Msg); Payload = strchr(Msg, ':'); // End of Address String if (Payload == NULL) { Debugprintf("Invalid Msg %s", Msg); return Station; } *Payload++ = 0; Callsign = Msg; Path = strchr(Msg, '>'); if (Path == NULL) { Debugprintf("Invalid Msg %s", Msg); return Station; } *Path++ = 0; // Look up station - create a new one if not found if (strlen(Callsign) > 11) { Debugprintf("Invalid Msg %s", Msg); return Station; } Station = FindStation(Callsign, TRUE); strcpy(Station->Path, Path); strcpy(Station->LastPacket, Payload); Station->LastPort = 0; DecodeAPRSPayload(Payload, Station); Station->TimeLastUpdated = time(NULL); return Station; } double Cube91 = 91.0 * 91.0 * 91.0; double Square91 = 91.0 * 91.0; BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station) { UCHAR SymChar; char SymSet; char NS; char EW; double NewLat, NewLon; char LatDeg[3], LonDeg[4]; char save; // Compressed has first character not a digit (it is symbol table) // /YYYYXXXX$csT if (Payload[0] == '!') return FALSE; // Ultimeter 2000 Weather Station if (!isdigit(*Payload)) { int C, S; SymSet = *Payload; SymChar = Payload[9]; NewLat = 90.0 - ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 380926.0; Payload += 4; NewLon = -180.0 + ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 190463.0; C = Payload[6] - 33; if (C >= 0 && C < 90 ) { S = Payload[7] - 33; Station->Course = C * 4; Station->Speed = (pow(1.08, S) - 1) * 1.15077945; // MPH; } } else { // Standard format ddmm.mmN/dddmm.mmE? NS = Payload[7] & 0xdf; // Mask Lower Case Bit EW = Payload[17] & 0xdf; SymSet = Payload[8]; SymChar = Payload[18]; if (SymChar == '_') // WX { if (strlen(Payload) > 30) strcpy(Station->LastWXPacket, Payload); } memcpy(LatDeg, Payload,2); LatDeg[2]=0; NewLat = atof(LatDeg) + (atof(Payload+2) / 60); if (NS == 'S') NewLat = -NewLat; else if (NS != 'N') return FALSE; memcpy(LonDeg,Payload + 9, 3); if (SymChar != '_' && Payload[22] == '/') // not if WX { Station->Course = atoi(Payload + 19); Station->Speed = atoi(Payload + 23); } LonDeg[3]=0; save = Payload[17]; Payload[17] = 0; NewLon = atof(LonDeg) + (atof(Payload+12) / 60); Payload[17] = save; if (EW == 'W') NewLon = -NewLon; else if (EW != 'E') return FALSE; } Station->Symbol = SymChar; if (SymChar > ' ' && SymChar < 0x7f) SymChar -= '!'; else SymChar = 0; Station->IconOverlay = 0; if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) { SymChar += 96; Station->IconOverlay = SymSet; } else if (SymSet == '\\') SymChar += 96; Station->iconRow = SymChar >> 4; Station->iconCol = SymChar & 15; if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) return TRUE; if (Station->Lat != NewLat || Station->Lon != NewLon) { time_t NOW = time(NULL); time_t Age = NOW - Station->TimeLastTracked; if (Age > 15) // Don't update too often { // Add to track Station->TimeLastTracked = NOW; // if (memcmp(Station->Callsign, "ISS ", 4) == 0) // Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); Station->LatTrack[Station->Trackptr] = NewLat; Station->LonTrack[Station->Trackptr] = NewLon; Station->TrackTime[Station->Trackptr] = NOW; Station->Trackptr++; Station->Moved = TRUE; if (Station->Trackptr == TRACKPOINTS) Station->Trackptr = 0; } Station->Lat = NewLat; Station->Lon = NewLon; Station->Approx = 0; } return TRUE; } int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station) { char * TimeStamp; char * ObjName; char ObjState; struct STATIONRECORD * Object; BOOL Item = FALSE; char * ptr; char * Callsign; char * Path; char * Msg; char * context; struct STATIONRECORD * TPStation; Station->Object = NULL; if (strcmp(Station->Callsign, "LA1ZDA-2") == 0) { int i = 1; } switch(*Payload) { case '`': case 0x27: // ' case 0x1c: case 0x1d: // MIC-E Decode_MIC_E_Packet(Payload, Station); return 0; case '$': // NMEA Debugprintf(Payload); break; case ')': // Item // Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); Item = TRUE; ObjName = ptr = Payload + 1; while (TRUE) { ObjState = *ptr; if (ObjState == 0) return 0; // Corrupt if (ObjState == '!' || ObjState == '_') // Item Terminator break; ptr++; } *ptr = 0; // Terminate Name Object = FindStation(ObjName, TRUE); Object->ObjState = *ptr++ = ObjState; strcpy(Object->Path, Station->Callsign); strcat(Object->Path, ">"); if (Object == Station) { char Temp[256]; strcpy(Temp, Station->Path); strcat(Object->Path, Temp); Debugprintf("item is station %s", Payload); } else strcat(Object->Path, Station->Path); strcpy(Object->LastPacket, Payload); if (ObjState != '_') // Deleted Objects may have odd positions DecodeLocationString(ptr, Object); Object->TimeLastUpdated = time(NULL); Station->Object = Object; return 0; case ';': // Object ObjName = Payload + 1; ObjState = Payload[10]; // * Live, _Killed Payload[10] = 0; Object = FindStation(ObjName, TRUE); Object->ObjState = Payload[10] = ObjState; strcpy(Object->Path, Station->Callsign); strcat(Object->Path, ">"); if (Object == Station) { char Temp[256]; strcpy(Temp, Station->Path); strcat(Object->Path, Temp); Debugprintf("Object is station %s", Payload); } else strcat(Object->Path, Station->Path); strcpy(Object->LastPacket, Payload); TimeStamp = Payload + 11; if (ObjState != '_') // Deleted Objects may have odd positions DecodeLocationString(Payload + 18, Object); Object->TimeLastUpdated = time(NULL); Object->LastPort = Station->LastPort; Station->Object = Object; return 0; case '@': case '/': // Timestamp, No Messaging TimeStamp = ++Payload; Payload += 6; case '=': case '!': Payload++; DecodeLocationString(Payload, Station); return 0; case '>': // Status strcpy(Station->Status, &Payload[1]); case '<': // Capabilities case '_': // Weather case 'T': // Telemetry break; case ':': // Message return ProcessMessage(Payload, Station); case '}': // Third Party Header // Process Payload as a new message // }GM7HHB-9>APDR12,TCPIP,MM1AVR*:=5556.62N/00303.55W>204/000/A=000213 http://www.dstartv.com Callsign = Msg = &Payload[1]; Path = strchr(Msg, '>'); if (Path == NULL) return 0; *Path++ = 0; Payload = strchr(Path, ':'); if (Payload == NULL) return 0; *(Payload++) = 0; // Check Dup Filter if (CheckforDups(Callsign, Payload, (int)strlen(Payload))) return 0; // Look up station - create a new one if not found TPStation = FindStation(Callsign, TRUE); strcpy(TPStation->Path, Path); strcpy(TPStation->LastPacket, Payload); TPStation->LastPort = 0; // Heard on RF, but info is from IS DecodeAPRSPayload(Payload, TPStation); TPStation->TimeLastUpdated = time(NULL); return 0; default: // Non - APRS Message. If Payload contains a valid 6 char locator derive a position from it if (Station->Lat != 0.0 || Station->Lon != 0.0) return 0; // already have position ptr = strtok_s(Payload, ",[](){} \n", &context); while (ptr && ptr[0]) { if (strlen(ptr) == 6) // could be locator { double Lat = 0.0, Lon = 0.0; if (FromLOC(ptr, &Lat, &Lon)) { if (Lat != 0.0 && Lon != 0.0) { // Randomise in locator square. Lat = Lat + ((rand() / 24.0) / RAND_MAX); Lon = Lon + ((rand() / 12.0) / RAND_MAX); Station->Lat = Lat; Station->Lon = Lon; Station->Approx = 1; Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); } } } ptr = strtok_s(NULL, ",[](){} \n", &context); } return 0; } return 0; } // Convert MIC-E Char to Lat Digit (offset by 0x30) // 0123456789 @ABCDEFGHIJKLMNOPQRSTUVWXYZ char MicELat[] = "0123456789???????0123456789 ???0123456789 " ; char MicECode[]= "0000000000???????111111111110???22222222222" ; VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station) { // Info is encoded in the Dest Addr (in Station->Path) as well as Payload. // See APRS Spec for full details char Lat[10]; // DDMMHH char LatDeg[3]; char * ptr; char c; int i, n; int LonDeg, LonMin; BOOL LonOffset = FALSE; char NS = 'S'; char EW = 'E'; UCHAR SymChar, SymSet; double NewLat, NewLon; int SP, DC, SE; // Course/Speed Encoded int Course, Speed; // Make sure packet is long enough to have an valid address if (strlen(Payload) < 9) return; ptr = &Station->Path[0]; for (i = 0; i < 6; i++) { n = (*(ptr++)) - 0x30; c = MicELat[n]; if (c == '?') // Illegal return; if (c == ' ') c = '0'; // Limited Precision Lat[i] = c; } Lat[6] = 0; if (Station->Path[3] > 'O') NS = 'N'; if (Station->Path[5] > 'O') EW = 'W'; if (Station->Path[4] > 'O') LonOffset = TRUE; n = Payload[1] - 28; // Lon Degrees S9PU0T,WIDE1-1,WIDE2-2,qAR,WB9TLH-15:`rB0oII>/]"6W}44 if (LonOffset) n += 100; if (n > 179 && n < 190) n -= 80; else if (n > 189 && n < 200) n -= 190; LonDeg = n; /* To decode the longitude degrees value: 1. subtract 28 from the d+28 value to obtain d. 2. if the longitude offset is +100 degrees, add 100 to d. 3. subtract 80 if 180 ˜ d ˜ 189 (i.e. the longitude is in the range 100–109 degrees). 4. or, subtract 190 if 190 ˜ d ˜ 199. (i.e. the longitude is in the range 0–9 degrees). */ n = Payload[2] - 28; // Lon Mins if (n > 59) n -= 60; LonMin = n; n = Payload[3] - 28; // Lon Mins/100; //1. subtract 28 from the m+28 value to obtain m. //2. subtract 60 if m ™ 60. //(i.e. the longitude minutes is in the range 0–9). memcpy(LatDeg, Lat, 2); LatDeg[2]=0; NewLat = atof(LatDeg) + (atof(Lat+2) / 6000.0); if (NS == 'S') NewLat = -NewLat; NewLon = LonDeg + LonMin / 60.0 + n / 6000.0; if (EW == 'W') // West NewLon = -NewLon; SP = Payload[4] - 28; DC = Payload[5] - 28; SE = Payload[6] - 28; // Course 100 and 10 degs Speed = DC / 10; // Quotient = Speed Units Course = DC - (Speed * 10); // Remainder = Course Deg/100 Course = SE + (Course * 100); Speed += SP * 10; if (Speed >= 800) Speed -= 800; if (Course >= 400) Course -= 400; Station->Course = Course; Station->Speed = Speed * 1.15077945; // MPH // Debugprintf("MIC-E Course/Speed %s %d %d", Station->Callsign, Course, Speed); SymChar = Payload[7]; // Symbol SymSet = Payload[8]; // Symbol SymChar -= '!'; Station->IconOverlay = 0; if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) { SymChar += 96; Station->IconOverlay = SymSet; } else if (SymSet == '\\') SymChar += 96; Station->iconRow = SymChar >> 4; Station->iconCol = SymChar & 15; if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) return; if (Station->Lat != NewLat || Station->Lon != NewLon) { time_t NOW = time(NULL); time_t Age = NOW - Station->TimeLastUpdated; if (Age > 15) // Don't update too often { // Add to track // if (memcmp(Station->Callsign, "ISS ", 4) == 0) // Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); Station->LatTrack[Station->Trackptr] = NewLat; Station->LonTrack[Station->Trackptr] = NewLon; Station->TrackTime[Station->Trackptr] = NOW; Station->Trackptr++; Station->Moved = TRUE; if (Station->Trackptr == TRACKPOINTS) Station->Trackptr = 0; } Station->Lat = NewLat; Station->Lon = NewLon; Station->Approx = 0; } return; } /* INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { // This processes messages from controls on the tab subpages int Command; // int retCode, disp; // char Key[80]; // HKEY hKey; // BOOL OK; // OPENFILENAME ofn; // char Digis[100]; int Port = PortNum[CurrentPage]; switch (message) { case WM_NOTIFY: switch (((LPNMHDR)lParam)->code) { case TCN_SELCHANGE: OnSelChanged(hDlg); return TRUE; // More cases on WM_NOTIFY switch. case NM_CHAR: return TRUE; } break; case WM_INITDIALOG: OnChildDialogInit( hDlg); return (INT_PTR)TRUE; case WM_CTLCOLORDLG: return (LONG)bgBrush; case WM_CTLCOLORSTATIC: { HDC hdcStatic = (HDC)wParam; SetTextColor(hdcStatic, RGB(0, 0, 0)); SetBkMode(hdcStatic, TRANSPARENT); return (LONG)bgBrush; } case WM_COMMAND: Command = LOWORD(wParam); if (Command == 2002) return TRUE; switch (Command) { /* case IDC_FILE: memset(&ofn, 0, sizeof (OPENFILENAME)); ofn.lStructSize = sizeof (OPENFILENAME); ofn.hwndOwner = hDlg; ofn.lpstrFile = &FN[Port][0]; ofn.nMaxFile = 250; ofn.lpstrTitle = "File to send as beacon"; ofn.lpstrInitialDir = GetBPQDirectory(); if (GetOpenFileName(&ofn)) SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]); break; case IDOK: GetDlgItemText(hDlg, IDC_UIDEST, &UIDEST[Port][0], 10); if (UIDigi[Port]) { free(UIDigi[Port]); UIDigi[Port] = NULL; } if (UIDigiAX[Port]) { free(UIDigiAX[Port]); UIDigiAX[Port] = NULL; } GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99); UIDigi[Port] = _strdup(Digis); GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255); GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000); Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE); MinCounter[Port] = Interval[Port]; SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE); sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]); retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); if (retCode == ERROR_SUCCESS) { retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIDEST[Port][0], strlen(&UIDEST[Port][0])); retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); retCode = RegSetValueEx(hKey, "Enabled", 0, REG_DWORD,(BYTE *)&UIEnabled[Port], 4); retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, strlen(Digis)); RegCloseKey(hKey); } SetupUI(Port); return (INT_PTR)TRUE; case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case ID_TEST: SendBeacon(Port); return TRUE; } break; } return (INT_PTR)FALSE; } VOID WINAPI OnTabbedDialogInit(HWND hDlg) { DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); DWORD dwDlgBase = GetDialogBaseUnits(); int cxMargin = LOWORD(dwDlgBase) / 4; int cyMargin = HIWORD(dwDlgBase) / 8; TC_ITEM tie; RECT rcTab; int i, pos, tab = 0; INITCOMMONCONTROLSEX init; char PortNo[60]; struct _EXTPORTDATA * PORTVEC; hwndDlg = hDlg; // Save Window Handle // Save a pointer to the DLGHDR structure. SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); // Create the tab control. init.dwICC = ICC_STANDARD_CLASSES; init.dwSize=sizeof(init); i=InitCommonControlsEx(&init); pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, 100, 100, hwndDlg, NULL, hInst, NULL); if (pHdr->hwndTab == NULL) { // handle error } // Add a tab for each of the child dialog boxes. tie.mask = TCIF_TEXT | TCIF_IMAGE; tie.iImage = -1; for (i = 1; i <= GetNumberofPorts(); i++) { // Only allow UI on ax.25 ports PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntry(i); if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR continue; sprintf(PortNo, "Port %2d", GetPortNumber(i)); PortNum[tab] = GetPortNumber(i); tie.pszText = PortNo; TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie); pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE"); } PageCount = tab; // Determine the bounding rectangle for all child dialog boxes. SetRectEmpty(&rcTab); for (i = 0; i < PageCount; i++) { if (pHdr->apRes[i]->cx > rcTab.right) rcTab.right = pHdr->apRes[i]->cx; if (pHdr->apRes[i]->cy > rcTab.bottom) rcTab.bottom = pHdr->apRes[i]->cy; } MapDialogRect(hwndDlg, &rcTab); // rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; // rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; // Calculate how large to make the tab control, so // the display area can accomodate all the child dialog boxes. TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); // Calculate the display rectangle. CopyRect(&pHdr->rcDisplay, &rcTab); TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); // Set the size and position of the tab control, buttons, // and dialog box. SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); // Move the Buttons to bottom of page pos=rcTab.left+cxMargin; // Size the dialog box. SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), SWP_NOMOVE | SWP_NOZORDER); // Simulate selection of the first item. OnSelChanged(hwndDlg); } // DoLockDlgRes - loads and locks a dialog template resource. // Returns a pointer to the locked resource. // lpszResName - name of the resource DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) { HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG); HGLOBAL hglb = LoadResource(hInst, hrsrc); return (DLGTEMPLATE *) LockResource(hglb); } //The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. // OnSelChanged - processes the TCN_SELCHANGE notification. // hwndDlg - handle of the parent dialog box VOID WINAPI OnSelChanged(HWND hwndDlg) { char PortDesc[40]; int Port; DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); // Destroy the current child dialog box, if any. if (pHdr->hwndDisplay != NULL) DestroyWindow(pHdr->hwndDisplay); // Create the new child dialog box. pHdr->hwndDisplay = CreateDialogIndirect(hInst, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); hwndDisplay = pHdr->hwndDisplay; // Save Port = PortNum[CurrentPage]; // Fill in the controls GetPortDescription(PortNum[CurrentPage], PortDesc); SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc); // CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]); // SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE); SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIDEST[Port][0]); SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIDigi[Port]); // SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]); // SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]); ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); } //The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. // OnChildDialogInit - Positions the child dialog box to fall // within the display area of the tab control. VOID WINAPI OnChildDialogInit(HWND hwndDlg) { HWND hwndParent = GetParent(hwndDlg); DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); } */ /* VOID ProcessMessage(char * Payload, struct STATIONRECORD * Station) { char MsgDest[10]; struct APRSMESSAGE * Message; struct APRSMESSAGE * ptr = Messages; char * TextPtr = &Payload[11]; char * SeqPtr; int n = 0; char FromCall[10] = " "; struct tm * TM; time_t NOW; memcpy(FromCall, Station->Callsign, (int)strlen(Station->Callsign)); memcpy(MsgDest, &Payload[1], 9); MsgDest[9] = 0; SeqPtr = strchr(TextPtr, '{'); if (SeqPtr) { *(SeqPtr++) = 0; if(strlen(SeqPtr) > 6) SeqPtr[7] = 0; } if (_memicmp(TextPtr, "ack", 3) == 0) { // Message Ack. See if for one of our messages ptr = OutstandingMsgs; if (ptr == 0) return; do { if (strcmp(ptr->FromCall, MsgDest) == 0 && strcmp(ptr->ToCall, FromCall) == 0 && strcmp(ptr->Seq, &TextPtr[3]) == 0) { // Message is acked ptr->Retries = 0; ptr->Acked = TRUE; // if (hMsgsOut) // UpdateTXMessageLine(hMsgsOut, n, ptr); return; } ptr = ptr->Next; n++; } while (ptr); return; } Message = malloc(sizeof(struct APRSMESSAGE)); memset(Message, 0, sizeof(struct APRSMESSAGE)); strcpy(Message->FromCall, Station->Callsign); strcpy(Message->ToCall, MsgDest); if (SeqPtr) { strcpy(Message->Seq, SeqPtr); // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message if (SeqPtr[2] == '}') { struct APRSMESSAGE * ptr1; int nn = 0; strcpy(Station->LastRXSeq, SeqPtr); ptr1 = OutstandingMsgs; while (ptr1) { if (strcmp(ptr1->FromCall, MsgDest) == 0 && strcmp(ptr1->ToCall, FromCall) == 0 && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) { // Message is acked ptr1->Acked = TRUE; ptr1->Retries = 0; // if (hMsgsOut) // UpdateTXMessageLine(hMsgsOut, nn, ptr); break; } ptr1 = ptr1->Next; nn++; } } else { // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanger Station->SimpleNumericSeq = TRUE; } } if (strlen(TextPtr) > 100) TextPtr[100] = 0; strcpy(Message->Text, TextPtr); NOW = time(NULL); if (DefaultLocalTime) TM = localtime(&NOW); else TM = gmtime(&NOW); sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); if (_stricmp(MsgDest, APRSCall) == 0 && SeqPtr) // ack it if it has a sequence { // For us - send an Ack char ack[30]; int n = sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); PutAPRSMessage(ack, n); } if (ptr == NULL) { Messages = Message; } else { n++; while(ptr->Next) { ptr = ptr->Next; n++; } ptr->Next = Message; } if (strcmp(MsgDest, APRSCall) == 0) // to me? { } } */ VOID APRSSecTimer() { // Check Message Retries struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; int n = 0; if (SendWX) SendWeatherBeacon(); while (ptr) { if (ptr->Acked == FALSE) { if (ptr->Retries) { ptr->RetryTimer--; if (ptr->RetryTimer == 0) { ptr->Retries--; if (ptr->Retries) { // Send Again char Msg[255]; APRSHEARDRECORD * STN; sprintf(Msg, ":%-9s:%s{%s", ptr->ToCall, ptr->Text, ptr->Seq); STN = FindStationInMH(ptr->ToCall); if (STN) SendAPRSMessage(Msg, STN->rfPort); else { if (memcmp(ptr->ToCall, "SERVER ", 9)) SendAPRSMessage(Msg, -1); // All RF ports unless to SERVER SendAPRSMessage(Msg, 0); // IS } ptr->RetryTimer = RetryTimer; } } } } ptr = ptr->Next; n++; } } double radians(double Degrees) { return M_PI * Degrees / 180; } double degrees(double Radians) { return Radians * 180 / M_PI; } double Distance(double laa, double loa, double lah, double loh, BOOL KM) { /* 'Great Circle Calculations. 'dif = longitute home - longitute away ' (this should be within -180 to +180 degrees) ' (Hint: This number should be non-zero, programs should check for ' this and make dif=0.0001 as a minimum) 'lah = latitude of home 'laa = latitude of away 'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) 'distance = dis / 180 * pi * ERAD 'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) 'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians */ loh = radians(loh); lah = radians(lah); loa = radians(loa); laa = radians(laa); loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; if (KM) loh *= 1.60934; return loh; } double myDistance(double laa, double loa, BOOL KM) { double lah, loh; GetAPRSLatLon(&lah, &loh); return Distance(laa, loa, lah, loh, KM); } /* 'Great Circle Calculations. 'dif = longitute home - longitute away ' (this should be within -180 to +180 degrees) ' (Hint: This number should be non-zero, programs should check for ' this and make dif=0.0001 as a minimum) 'lah = latitude of home 'laa = latitude of away 'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) 'distance = dis / 180 * pi * ERAD 'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) 'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians loh = radians(loh); lah = radians(lah); loa = radians(loa); laa = radians(laa); loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; if (KM) loh *= 1.60934; return loh; } */ double Bearing(double lat2, double lon2, double lat1, double lon1) { double dlat, dlon, TC1; lat1 = radians(lat1); lat2 = radians(lat2); lon1 = radians(lon1); lon2 = radians(lon2); dlat = lat2 - lat1; dlon = lon2 - lon1; if (dlat == 0 || dlon == 0) return 0; TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); TC1 = degrees(TC1); if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; if (dlat > 0) { if (dlon > 0) return -TC1; if (dlon < 0) return 360 - TC1; return 0; } if (dlat < 0) { if (dlon > 0) return TC1 = 180 - TC1; if (dlon < 0) return TC1 = 180 - TC1; // 'ok? return 180; } return 0; } double myBearing(double lat2, double lon2) { double lat1, lon1; GetAPRSLatLon(&lat1, &lon1); return Bearing(lat2, lon2, lat1, lon1); } /* lat1 = radians(lat1); lat2 = radians(lat2); lon1 = radians(lon1); lon2 = radians(lon2); dlat = lat2 - lat1; dlon = lon2 - lon1; if (dlat == 0 || dlon == 0) return 0; TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); TC1 = degrees(TC1); if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; if (dlat > 0) { if (dlon > 0) return -TC1; if (dlon < 0) return 360 - TC1; return 0; } if (dlat < 0) { if (dlon > 0) return TC1 = 180 - TC1; if (dlon < 0) return TC1 = 180 - TC1; // 'ok? return 180; } return 0; } */ // Weather Data static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; VOID SendWeatherBeacon() { char Msg[256]; char DD[3]=""; char HH[3]=""; char MM[3]=""; char Lat[10], Lon[10]; size_t Len; int index; char WXMessage[1024]; char * WXptr; char * WXend; time_t WXTime; time_t now = time(NULL); FILE * hFile; struct tm * TM; struct stat STAT; WXCounter++; if (WXCounter < WXInterval * 60) return; WXCounter = 0; // Debugprintf("BPQAPRS - Trying to open WX file %s", WXFileName); if (stat(WXFileName, &STAT)) { Debugprintf("APRS WX File %s stat() failed %d", WXFileName, GetLastError()); return; } WXTime = (now - STAT.st_mtime) /60; // Minutes if (WXTime > (3 * WXInterval)) { Debugprintf("APRS Send WX File %s too old - %d minutes", WXFileName, WXTime); return; } hFile = fopen(WXFileName, "rb"); if (hFile) Len = fread(WXMessage, 1, 1024, hFile); else { Debugprintf("APRS WX File %s open() failed %d", WXFileName, GetLastError()); return; } if (Len < 30) { Debugprintf("BPQAPRS - WX file %s is too short - %d Chars", WXFileName, Len); fclose(hFile); return; } WXMessage[Len] = 0; // see if wview format //04-09-13, 2245 //TempIn 23 //TempEx 18 //WindHi 0 //WindAv 0 //WindDr 200 //BarmPs 30167 //HumdIn 56 //HumdEx 100 //RnFall 0.00 //DailyRnFall 0.00 if (strstr(WXMessage, "TempIn")) { int Wind = 0; int Gust = 0; int Temp = 0; int Winddir = 0; int Humidity = 0; int Raintoday = 0; int Rain24hrs = 0; int Pressure = 0; char * ptr; ptr = strstr(WXMessage, "TempEx"); if (ptr) Temp = (int)(atof(ptr + 7) * 1.8) + 32; ptr = strstr(WXMessage, "WindHi"); if (ptr) Gust = atoi(ptr + 7); ptr = strstr(WXMessage, "WindAv"); if (ptr) Wind = atoi(ptr + 7); ptr = strstr(WXMessage, "WindDr"); if (ptr) Winddir = atoi(ptr + 7); ptr = strstr(WXMessage, "BarmPs"); if (ptr) Pressure = (int)(atof(ptr + 7) * 0.338638866667); // Inches to 1/10 millbars ptr = strstr(WXMessage, "HumdEx"); if (ptr) Humidity = atoi(ptr + 7); ptr = strstr(WXMessage, "RnFall"); if (ptr) Rain24hrs = (int)(atof(ptr + 7) * 100.0); ptr = strstr(WXMessage, "DailyRnFall"); if (ptr) Raintoday = (int)(atof(ptr + 12) * 100.0); if (Humidity > 99) Humidity = 99; sprintf(WXMessage, "%03d/%03dg%03dt%03dr%03dP%03dp%03dh%02db%05d", Winddir, Wind, Gust, Temp, 0, Raintoday, Rain24hrs, Humidity, Pressure); } WXptr = strchr(WXMessage, 10); if (WXptr) { WXend = strchr(++WXptr, 13); if (WXend == 0) WXend = strchr(WXptr, 10); if (WXend) *WXend = 0; } else WXptr = &WXMessage[0]; // Get DDHHMM from Filetime TM = gmtime(&STAT.st_mtime); sprintf(DD, "%02d", TM->tm_mday); sprintf(HH, "%02d", TM->tm_hour); sprintf(MM, "%02d", TM->tm_min); GetAPRSLatLonString(Lat, Lon); Len = sprintf(Msg, "@%s%s%sz%s/%s_%s%s", DD, HH, MM, Lat, Lon, WXptr, WXComment); Debugprintf(Msg); for (index = 0; index < MaxBPQPortNo; index++) { if (WXPort[index]) SendAPRSMessageEx(Msg, index, WXCall, FALSE); } fclose(hFile); } /* Jan 22 2012 14:10 123/005g011t031r000P000p000h00b10161 /MITWXN Mitchell IN weather Station N9LYA-3 {UIV32} < previous @221452z3844.42N/08628.33W_203/006g007t032r000P000p000h00b10171 Complete Weather Report Format — with Lat/Long position, no Timestamp ! or = Lat Sym Table ID Long Symbol Code _ Wind Directn/ Speed Weather Data APRS Software WX Unit uuuu 1 8 1 9 1 7 n 1 2-4 Examples !4903.50N/07201.75W_220/004g005t077r000p000P000h50b09900wRSW !4903.50N/07201.75W_220/004g005t077r000p000P000h50b.....wRSW */ // Web Server Code // The actual HTTP socket code is in bpq32.dll. Any requests for APRS data are passed in // using a Named Pipe. The request looks exactly like one from a local socket, and the respone is // a fully pormatted HTTP packet #define InputBufferLen 1000 #define MaxSessions 100 HANDLE PipeHandle; int HTTPPort = 80; BOOL IPV6 = TRUE; #define MAX_PENDING_CONNECTS 5 BOOL OpenSockets6(); char HTDocs[MAX_PATH] = "HTML"; char SpecialDocs[MAX_PATH] = "Special Pages"; char SymbolText[192][20] = { "Police Stn", "No Symbol", "Digi", "Phone", "DX Cluster", "HF Gateway", "Plane sm", "Mob Sat Stn", "WheelChair", "Snowmobile", "Red Cross", "Boy Scout", "Home", "X", "Red Dot", "Circle (0)", "Circle (1)", "Circle (2)", "Circle (3)", "Circle (4)", "Circle (5)", "Circle (6)", "Circle (7)", "Circle (8)", "Circle (9)", "Fire", "Campground", "Motorcycle", "Rail Eng.", "Car", "File svr", "HC Future", "Aid Stn", "BBS", "Canoe", "No Symbol", "Eyeball", "Tractor", "Grid Squ.", "Hotel", "Tcp/ip", "No Symbol", "School", "Usr Log-ON", "MacAPRS", "NTS Stn", "Balloon", "Police", "TBD", "Rec Veh'le", "Shuttle", "SSTV", "Bus", "ATV", "WX Service", "Helo", "Yacht", "WinAPRS", "Jogger", "Triangle", "PBBS", "Plane lrge", "WX Station", "Dish Ant.", "Ambulance", "Bike", "ICP", "Fire Station", "Horse", "Fire Truck", "Glider", "Hospital", "IOTA", "Jeep", "Truck", "Laptop", "Mic-E Rptr", "Node", "EOC", "Rover", "Grid squ.", "Antenna", "Power Boat", "Truck Stop", "Truck 18wh", "Van", "Water Stn", "XAPRS", "Yagi", "Shelter", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "", "", "Emergency", "No Symbol", "No. Digi", "Bank", "No Symbol", "No. Diam'd", "Crash site", "Cloudy", "MEO", "Snow", "Church", "Girl Scout", "Home (HF)", "UnknownPos", "Destination", "No. Circle", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "Petrol Stn", "Hail", "Park", "Gale Fl", "No Symbol", "No. Car", "Info Kiosk", "Hurricane", "No. Box", "Snow blwng", "Coast G'rd", "Drizzle", "Smoke", "Fr'ze Rain", "Snow Shwr", "Haze", "Rain Shwr", "Lightning", "Kenwood", "Lighthouse", "No Symbol", "Nav Buoy", "Rocket", "Parking ", "Quake", "Restaurant", "Sat/Pacsat", "T'storm", "Sunny", "VORTAC", "No. WXS", "Pharmacy", "No Symbol", "No Symbol", "Wall Cloud", "No Symbol", "No Symbol", "No. Plane", "No. WX Stn", "Rain", "No. Diamond", "Dust blwng", "No. CivDef", "DX Spot", "Sleet", "Funnel Cld", "Gale", "HAM store", "No. Blk Box", "WorkZone", "SUV", "Area Locns", "Milepost", "No. Triang", "Circle sm", "Part Cloud", "No Symbol", "Restrooms", "No. Boat", "Tornado", "No. Truck", "No. Van", "Flooding", "No Symbol", "Sky Warn", "No Symbol", "Fog", "No Symbol", "No Symbol", "No Symbol", "", ""}; // All Calls (8 per line) //EI7IG-1 //G7TKK-1 //GB7GL-B //GM1TCN //GM8BPQ //GM8BPQ-14 //LA2VPA-9 //LA3FIA-10 //LA6JF-2LD4STM0CHK-7M0OZH-7MB7UFO-1MB7UNMM0DXE-15PA2AYX-9 //PA3AQW-5PD1CPD5LWD-2PI1ECO 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, "%s", XCall, ptr->Callsign); x = ++n/Width; x = x * Width; if (x == n) strcat(Line2, ""); 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°%05.2f'%c", Degrees, Minutes, NS); Degrees = Lon; #pragma warning(pop) Minutes = Lon * 60 - 60 * Degrees; sprintf(LongString, "%3d°%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, " %s%s%s%s %s%s%s%s\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°%05.2f'%c",Degrees, Minutes, NS); Degrees = Lon; #pragma warning(pop) Minutes = Lon * 60 - 60 * Degrees; sprintf(val,"%s %3d°%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, ""); 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[] = "" "" "APRS Messaging" "" "" "" "" "" "" "" "" "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" "

%s's Messages

" ""; char WebTXHeader[] = "" "" "APRS Messaging" "" "
FromToSeqTime Message
" "" "" "" "" "" "" "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" "

Message Sent by %s

" ""; char WebLine[] = ""; char WebTXLine[] = "" ""; char WebTrailer[] = "
ToSeqTimeStatemessage
%s %s %s %s" "Reply %s
%s %s %s %s %s
"; char SendMsgPage[] = "BPQ32 APRS Messaging" "

APRS Message Input

" "
" "" "" "
To
Message
" "

"; char APRSIndexPage[] = "BPQ32 Web Server APRS Pages" "

" "

BPQ32 APRS Server

" "" "" "" "" "" "" "" "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
%s"; 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, "



Message Cancelled

"); 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], "

Invalid Callsign

"); 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], "

No Message

"); 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, "



Message Sent

"); HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); send(sockptr->sock, Header, HeaderLen, 0); send(sockptr->sock, OutBuffer, OutputLen, 0); return; } } URL = &MsgPtr[4]; ptr = strstr(URL, " HTTP"); if (ptr) *ptr = 0; if (_stricmp(URL, "/APRS") == 0) { // Return APRS Index Page OutputLen = sprintf(OutBuffer, APRSIndexPage, ""); HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); send(sockptr->sock, Header, HeaderLen, 0); send(sockptr->sock, OutBuffer, OutputLen, 0); return; } if (_memicmp(URL, "/aprs/msgs/entermsg", 19) == 0 || _memicmp(URL, "/aprs/entermsg", 14) == 0) { char * To = strchr(URL, '='); if (LOCAL == FALSE && COOKIE == FALSE) { // Send Not Authorized OutputLen = sprintf(OutBuffer, APRSIndexPage, "
Not authorized - please return to Node Menu and sign in"); HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(OutputLen + strlen(Tail))); send(sock, Header, HeaderLen, 0); send(sock, OutBuffer, OutputLen, 0); send(sock, Tail, (int)strlen(Tail), 0); return; } if (To) { To++; UndoTransparency(To); strlop(To, '&'); } else To = ""; OutputLen = sprintf(OutBuffer, SendMsgPage, To); HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); send(sockptr->sock, Header, HeaderLen, 0); send(sockptr->sock, OutBuffer, OutputLen, 0); return; } else if (_memicmp(URL, "/aprs/msgs", 10) == 0) { // Return Messages Received Page struct APRSMESSAGE * ptr = SMEM->Messages; int n = 0; char BaseCall[10]; char BaseFrom[10]; char * MsgCall = LoppedAPRSCall; BOOL OnlyMine = TRUE; BOOL AllSSID = TRUE; BOOL OnlySeq = FALSE; BOOL ShowBulls = TRUE; // Parse parameters // ?call=g8bpq&bulls=true&seqonly=true&onlymine=true char * params = strchr(URL, '?'); if (params) { char * param, * context; param = strtok_s(++params, "&", &context); while (param) { char * val = strlop(param, '='); if (val) { strlop(val, ' '); if (_stricmp(param, "call") == 0) MsgCall = _strupr(val); else if (_stricmp(param, "bulls") == 0) ShowBulls = !_stricmp(val, "true"); else if (_stricmp(param, "onlyseq") == 0) OnlySeq = !_stricmp(val, "true"); else if (_stricmp(param, "onlymine") == 0) OnlyMine = !_stricmp(val, "true"); else if (_stricmp(param, "AllSSID") == 0) AllSSID = !_stricmp(val, "true"); } param = strtok_s(NULL,"&", &context); } } if (AllSSID) { memcpy(BaseCall, MsgCall, 10); strlop(BaseCall, '-'); } OutputLen = sprintf(OutBuffer, WebHeader, MsgCall, MsgCall); while (ptr) { char ToLopped[11] = ""; memcpy(ToLopped, ptr->ToCall, 10); strlop(ToLopped, ' '); if (memcmp(ToLopped, "BLN", 3) == 0) if (ShowBulls == TRUE) goto wantit; if (strcmp(ToLopped, MsgCall) == 0) // to me? goto wantit; if (strcmp(ptr->FromCall, MsgCall) == 0) // to me? goto wantit; if (AllSSID) { memcpy(BaseFrom, ToLopped, 10); strlop(BaseFrom, '-'); if (strcmp(BaseFrom, BaseCall) == 0) goto wantit; memcpy(BaseFrom, ptr->FromCall, 10); strlop(BaseFrom, '-'); if (strcmp(BaseFrom, BaseCall) == 0) goto wantit; } if (OnlyMine == FALSE) // Want All if (OnlySeq == FALSE || ptr->Seq[0] != 0) goto wantit; // ignore ptr = ptr->Next; continue; wantit: OutputLen += sprintf(&OutBuffer[OutputLen], WebLine, ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, ptr->FromCall, ptr->ToCall, ptr->Text); ptr = ptr->Next; if (OutputLen > 99000) break; } OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer); HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); sendandcheck(sock, Header, HeaderLen); sendandcheck(sock, OutBuffer, OutputLen); return; } else if (_memicmp(URL, "/aprs/txmsgs", 12) == 0) { // Return Messages Received Page struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; char * MsgCall = LoppedAPRSCall; char Retries[10]; OutputLen = sprintf(OutBuffer, WebTXHeader, MsgCall, MsgCall); while (ptr) { char ToLopped[11] = ""; if (ptr->Acked) strcpy(Retries, "A"); else if (ptr->Retries == 0) strcpy(Retries, "F"); else sprintf(Retries, "%d", ptr->Retries); memcpy(ToLopped, ptr->ToCall, 10); strlop(ToLopped, ' '); OutputLen += sprintf(&OutBuffer[OutputLen], WebTXLine, ptr->ToCall, ptr->Seq, ptr->Time, Retries, ptr->Text); ptr = ptr->Next; if (OutputLen > 99000) break; } OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer); HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); sendandcheck(sock, Header, HeaderLen); sendandcheck(sock, OutBuffer, OutputLen); return; } if (_memicmp(URL, "/aprs/find.cgi?call=", 20) == 0) { // return Station details char * Call = &URL[20]; BOOL RFOnly, WX, Mobile, Object = FALSE; struct STATIONRECORD * stn; char * Referrer = strstr(ptr + 1, "Referer:"); // Undo any % transparency in call char * ptr1 = Call; char * ptr2 = Key; char c; c = *(ptr1++); while (c) { if (c == '%') { int n; int m = *(ptr1++) - '0'; if (m > 9) m = m - 7; n = *(ptr1++) - '0'; if (n > 9) n = n - 7; *(ptr2++) = m * 16 + n; } else if (c == '+') *(ptr2++) = ' '; else *(ptr2++) = c; c = *(ptr1++); } *(ptr2++) = 0; if (Referrer) { ptr = strchr(Referrer, 13); if (ptr) { *ptr = 0; RFOnly = !(strstr(Referrer, "rf") == NULL); WX = !(strstr(Referrer, "wx") == NULL); Mobile = !(strstr(Referrer, "mobile") == NULL); Object = !(strstr(Referrer, "obj") == NULL); if (WX) strcpy(URL, "/aprs/infowx_call.html"); else if (Mobile) strcpy(URL, "/aprs/infomobile_call.html"); else if (Object) strcpy(URL, "/aprs/infoobj_call.html"); else strcpy(URL, "/aprs/info_call.html"); } } if (Object) { // Name is space padded, and could have embedded spaces int Keylen = (int)strlen(Key); if (Keylen < 9) memset(&Key[Keylen], 32, 9 - Keylen); } stn = FindStation(Key, FALSE); if (stn == NULL) strcpy(URL, "/aprs/noinfo.html"); else sockptr->SelCall = stn; } strcpy(sockptr->Callsign, Key); APRSSendMessageFile(sockptr, URL); return; } // Code for handling APRS messages within BPQ32/LinBPQ instead of GUI int ProcessMessage(char * Payload, struct STATIONRECORD * Station) { char MsgDest[10]; struct APRSMESSAGE * Message; struct APRSMESSAGE * ptr = SMEM->Messages; char * TextPtr = &Payload[11]; char * SeqPtr; int n = 0; char FromCall[10] = " "; struct tm * TM; time_t NOW; char noSeq[] = ""; int ourMessage = 0; memcpy(FromCall, Station->Callsign, strlen(Station->Callsign)); memcpy(MsgDest, &Payload[1], 9); MsgDest[9] = 0; if (strcmp(MsgDest, CallPadded) == 0) // to me? { SMEM->NeedRefresh = 255; // Flag to control Msg popup ourMessage = 1; } else SMEM->NeedRefresh = 1; SeqPtr = strchr(TextPtr, '{'); if (SeqPtr) { *(SeqPtr++) = 0; if(strlen(SeqPtr) > 6) SeqPtr[7] = 0; } else SeqPtr = noSeq; if (_memicmp(TextPtr, "ack", 3) == 0) { // Message Ack. See if for one of our messages ptr = SMEM->OutstandingMsgs; if (ptr == 0) return ourMessage; do { if (strcmp(ptr->FromCall, MsgDest) == 0 && strcmp(ptr->ToCall, FromCall) == 0 && strcmp(ptr->Seq, &TextPtr[3]) == 0) { // Message is acked ptr->Retries = 0; ptr->Acked = TRUE; return ourMessage; } ptr = ptr->Next; n++; } while (ptr); return ourMessage; } // See if we already have this message ptr = SMEM->Messages; while(ptr) { if (strcmp(ptr->ToCall, MsgDest) == 0 && strcmp(ptr->FromCall, FromCall) == 0 && strcmp(ptr->Seq, SeqPtr) == 0 && strcmp(ptr->Text, TextPtr) == 0) // Duplicate return ourMessage; ptr = ptr->Next; } Message = APRSGetMessageBuffer(); if (Message == NULL) return ourMessage; memset(Message, 0, sizeof(struct APRSMESSAGE)); memset(Message->FromCall, ' ', 9); memcpy(Message->FromCall, Station->Callsign, strlen(Station->Callsign)); strcpy(Message->ToCall, MsgDest); if (SeqPtr) { strcpy(Message->Seq, SeqPtr); // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message if (SeqPtr[2] == '}') { struct APRSMESSAGE * ptr1; int nn = 0; strcpy(Station->LastRXSeq, SeqPtr); ptr1 = SMEM->OutstandingMsgs; while (ptr1) { if (strcmp(ptr1->FromCall, MsgDest) == 0 && strcmp(ptr1->ToCall, FromCall) == 0 && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) { // Message is acked ptr1->Acked = TRUE; ptr1->Retries = 0; break; } ptr1 = ptr1->Next; nn++; } } else { // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanges Station->SimpleNumericSeq = TRUE; } } if (strlen(TextPtr) > 100) TextPtr[100] = 0; strcpy(Message->Text, TextPtr); NOW = time(NULL); if (DefaultLocalTime) TM = localtime(&NOW); else TM = gmtime(&NOW); sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); if (_stricmp(MsgDest, CallPadded) == 0 && SeqPtr) // ack it if it has a sequence { // For us - send an Ack char ack[30]; APRSHEARDRECORD * STN; sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); if (memcmp(Message->FromCall, "SERVER ", 9) == 0) { SendAPRSMessage(ack, 0); // IS } else { STN = FindStationInMH(Message->ToCall); if (STN) SendAPRSMessage(ack, STN->rfPort); else { SendAPRSMessage(ack, -1); // All RF ports SendAPRSMessage(ack, 0); // IS } } } if (SaveAPRSMsgs) SaveAPRSMessage(Message); ptr = SMEM->Messages; if (ptr == NULL) { SMEM->Messages = Message; } else { n++; while(ptr->Next) { ptr = ptr->Next; n++; } ptr->Next = Message; } return ourMessage; } BOOL InternalSendAPRSMessage(char * Text, char * Call) { char Msg[255]; size_t len = strlen(Call); APRSHEARDRECORD * STN; struct tm * TM; time_t NOW; struct APRSMESSAGE * Message; struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; Message = APRSGetMessageBuffer(); if (Message == NULL) return FALSE; memset(Message, 0, sizeof(struct APRSMESSAGE)); strcpy(Message->FromCall, CallPadded); memset(Message->ToCall, ' ', 9); memcpy(Message->ToCall, Call, len); Message->ToStation = FindStation(Call, TRUE); if (Message->ToStation == NULL) return FALSE; SMEM->NeedRefresh = TRUE; if (Message->ToStation->LastRXSeq[0]) // Have we received a Reply-Ack message from him? sprintf(Message->Seq, "%02X}%c%c", ++Message->ToStation->NextSeq, Message->ToStation->LastRXSeq[0], Message->ToStation->LastRXSeq[1]); else { if (Message->ToStation->SimpleNumericSeq) sprintf(Message->Seq, "%d", ++Message->ToStation->NextSeq); else sprintf(Message->Seq, "%02X}", ++Message->ToStation->NextSeq); // Don't know, so assume message-ack capable } if (strlen(Text) > 100) Text[100] = 0; strcpy(Message->Text, Text); Message->Retries = RetryCount; Message->RetryTimer = RetryTimer; NOW = time(NULL); if (DefaultLocalTime) TM = localtime(&NOW); else TM = gmtime(&NOW); sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); // Chain to Outstanding Queue if (ptr == NULL) { SMEM->OutstandingMsgs = Message; } else { while(ptr->Next) { ptr = ptr->Next; } ptr->Next = Message; } sprintf(Msg, ":%-9s:%s{%s", Call, Text, Message->Seq); if (strcmp(Call, "SERVER") == 0) { SendAPRSMessage(Msg, 0); // IS return TRUE; } STN = FindStationInMH(Message->ToCall); if (STN && STN->MHTIME > (time(NULL) - 900)) // Heard in last 15 mins SendAPRSMessage(Msg, STN->rfPort); else { SendAPRSMessage(Msg, -1); // All RF ports SendAPRSMessage(Msg, 0); // IS } return TRUE; } extern BOOL APRSReconfigFlag; extern struct DATAMESSAGE * REPLYBUFFER; extern char COMMANDBUFFER[81]; extern char OrigCmdBuffer[81]; BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr); VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) { // APRS Subcommands. Default for compatibility is APRSMH // Others are STATUS ENABLEIGATE DISABLEIGATE RECONFIG APRSHEARDRECORD * MH = MHDATA; int n = MAXHEARDENTRIES; char * ptr; char * Pattern, * context; int Port = -1; char dummypattern[] =""; if (memcmp(CmdTail, "? ", 2) == 0) { Bufferptr = Cmdprintf(Session, Bufferptr, "APRS Subcommmands:\r"); Bufferptr = Cmdprintf(Session, Bufferptr, "STATUS SEND MSGS SENT ENABLEIGATE DISABLEIGATE BEACON RECONFIG\r"); Bufferptr = Cmdprintf(Session, Bufferptr, "Default is Station list - Params [Port] [Pattern]\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "RECONFIG ", 5) == 0) { if (isSYSOP(Session, Bufferptr) == FALSE) return; if (!ProcessConfig()) { Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check failed - will continue with old config"); } else { APRSReconfigFlag=TRUE; Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfiguration requested\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } } if (memcmp(CmdTail, "ENABLEIGATE ", 6) == 0) { if (isSYSOP(Session, Bufferptr) == FALSE) return; IGateEnabled = TRUE; Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "DISABLEIGATE ", 6) == 0) { if (isSYSOP(Session, Bufferptr) == FALSE) return; IGateEnabled = FALSE; Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "STATUS ", 7) == 0) { if (IGateEnabled == FALSE) Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); else { Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled "); if (APRSISOpen) Bufferptr = Cmdprintf(Session, Bufferptr, "and connected to %s\r", RealISHost); else Bufferptr = Cmdprintf(Session, Bufferptr, "but not connected\r"); } SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "BEACON ", 7) == 0) { if (isSYSOP(Session, Bufferptr) == FALSE) return; BeaconCounter = 2; Bufferptr = Cmdprintf(Session, Bufferptr, "Beacons requested\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "MSGS ", 5) == 0) { struct APRSMESSAGE * ptr = SMEM->Messages; char Addrs[32]; Bufferptr = Cmdprintf(Session, Bufferptr, "\rTime Calls Seq Text\r"); while (ptr) { char ToLopped[11] = ""; memcpy(ToLopped, ptr->ToCall, 10); strlop(ToLopped, ' '); sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %s\r", ptr->Time, Addrs, ptr->Seq, ptr->Text); ptr = ptr->Next; } SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "SENT ", 5) == 0) { struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; char Addrs[32]; Bufferptr = Cmdprintf(Session, Bufferptr, "\rTime Calls Seq State Text\r"); while (ptr) { char ToLopped[11] = ""; char Retries[10]; if (ptr->Acked) strcpy(Retries, "A"); else if (ptr->Retries == 0) strcpy(Retries, "F"); else sprintf(Retries, "%d", ptr->Retries); memcpy(ToLopped, ptr->ToCall, 10); strlop(ToLopped, ' '); sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %-2s %s\r", ptr->Time, Addrs, ptr->Seq, Retries, ptr->Text); ptr = ptr->Next; } SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (memcmp(CmdTail, "SEND ", 5) == 0) { // Send Message. Params are Call and Message char * Call = strtok_s(&CmdTail[5], " \r", &context); char * Text = strtok_s(NULL, " \r", &context); int len = 0; if (isSYSOP(Session, Bufferptr) == FALSE) return; if (Call) len = (int)strlen(Call); if (len < 3 || len > 9) { Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Callsign\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } if (Text == NULL) { Bufferptr = Cmdprintf(Session, Bufferptr, "No Message Text\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } // Replace command tail with original (before conversion to upper case Text = Text + (OrigCmdBuffer - COMMANDBUFFER); InternalSendAPRSMessage(Text, Call); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } // DISPLAY APRS HEARD LIST // APRS [Port] [Pattern] Pattern = strtok_s(CmdTail, " \r", &context); if (Pattern && (int)strlen(Pattern) < 3) { // could be port number if (isdigit(Pattern[0]) && (Pattern[1] == 0 || isdigit(Pattern[1]))) { Port = atoi(Pattern); Pattern = strtok_s(NULL, " \r", &context); } } // Param is a pattern to match if (Pattern == NULL) Pattern = dummypattern; if (Pattern[0] == ' ') { // Prepare Pattern char * ptr1 = Pattern + 1; char * ptr2 = Pattern; char c; do { c = *ptr1++; *(ptr2++) = c; } while (c != ' '); *(--ptr2) = 0; } strlop(Pattern, ' '); _strupr(Pattern); *(Bufferptr++) = 13; while (n--) { if (MH->MHCALL[0] == 0) break; if ((Port > -1) && Port != MH->rfPort) { MH++; continue; } ptr = FormatAPRSMH(MH); MH++; if (ptr) { if (Pattern[0] && strstr(ptr, Pattern) == 0) continue; Bufferptr = Cmdprintf(Session, Bufferptr, "%s", ptr); } } SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); } int GetPosnFromAPRS(char * Call, double * Lat, double * Lon) { struct STATIONRECORD * Station; Station = FindStation(Call, FALSE); if (Station) { *Lat = Station->Lat; *Lon = Station->Lon; return 1; } return 0; } // Station Name Font const unsigned char ASCII[][5] = { //const u08 ASCII[][5] = { {0x00, 0x00, 0x00, 0x00, 0x00} // 20 ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL }; // APRS Web Map Code // Not sure yet what is best way to do station icons but for now build and cache any needed icons extern int IconDataLen; extern unsigned long long IconData[]; // Symbols as a png image.& // IconData is a png image, so needs to be uncompressed to an RGB array // Will key cached icons by IconRow, IconCol, Overlay Char - xxxxA int cachedIconCount = 0; // We need key, icon data, icon len for each. Maybe a simple linked list - we never remove any struct iconCacheEntry { struct iconCacheEntry * Next; char key[8]; int pngimagelen; int pngmalloclen; unsigned char * pngimage; }; struct iconCacheEntry * iconCache = NULL; // Each icon has to be created as an RGB array, then converted to a png image, as // Leaflet icons need a png file #include "mypng.h" struct png_info_struct * info_ptr = NULL; unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon); void createIcon(char * Key, int iconRow, int iconCol, char Overlay) { int i, j, index, mask; int row; int col; // First row unsigned char * rgbData = malloc(68 * 22); // 1323 unsigned char * ptr = rgbData; png_color colour = {0, 0, 0}; int Pointer; char c; int bit; struct iconCacheEntry * Icon = zalloc(sizeof(struct iconCacheEntry)); strcpy(Icon->key, Key); // icon data is in info_ptr->row_pointers (we have 255 of them) row = iconRow * 21; col = iconCol * 21 * 3; for (j = 0; j < 22; j++) { memcpy(ptr, info_ptr->row_pointers[row + j] + col, 22 * 3); // One scan line ptr += 68; // Rounded up to mod 4 } // This code seems to assume an icon is 16 pixels, but image is 22 x 22 ??? // j = ptr->iconRow * 21 * 337 * 3 + ptr->iconCol * 21 * 3 + 9 + 337 * 9; // for (i = 0; i < 16; i++) // { // memcpy(nptr, &iconImage[j], 16 * 3); // nptr += 6144; // j += 337 * 3; // } // If an overlay is specified, add it if (Overlay) { Pointer = 68 * 7 + 7 * 3; // 7th row, 7th col mask = 1; for (index = 0 ; index < 7 ; index++) { rgbData[Pointer++] = 255; // Blank line above chars rgbData[Pointer++] = 255; rgbData[Pointer++] = 255; } Pointer = 68 * 8 + 7 * 3; // 8th row, 7th col for (i = 0; i < 7; i++) { rgbData[Pointer++] = 255; // Blank col rgbData[Pointer++] = 255; rgbData[Pointer++] = 255; for (index = 0 ; index < 5 ; index++) { c = ASCII[Overlay - 0x20][index]; // Font data bit = c & mask; if (bit) { rgbData[Pointer++] = 0; rgbData[Pointer++] = 0; rgbData[Pointer++] = 0; } else { rgbData[Pointer++] = 255; rgbData[Pointer++] = 255; rgbData[Pointer++] = 255; } } rgbData[Pointer++] = 255; // Blank col rgbData[Pointer++] = 255; rgbData[Pointer++] = 255; mask <<= 1; Pointer += 47; } for (index = 0 ; index < 7 ; index++) { rgbData[Pointer++] = 255; // Blank line above chars rgbData[Pointer++] = 255; rgbData[Pointer++] = 255; } } // Encode PngEncode(rgbData, 22, 22, Icon); if (iconCache) Icon->Next = iconCache; iconCache = Icon; } int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL) { char Key[8] = ""; struct iconCacheEntry * Icon = iconCache; memcpy(Key, &NodeURL[5], 5); while (Icon) { if (strcmp(Icon->key, Key) == 0) // Have it { memcpy(_REPLYBUFFER, Icon->pngimage, Icon->pngimagelen); return Icon->pngimagelen; } Icon = Icon->Next; } return 0; } char * doHTMLTransparency(char * string) { // Make sure string doesn't contain forbidden XML chars (<>"'&) char * newstring = malloc(5 * strlen(string) + 1); // If len is zero still need null terminator char * in = string; char * out = newstring; char c; c = *(in++); while (c) { switch (c) { case '<': strcpy(out, "<"); out += 4; break; case '>': strcpy(out, ">"); out += 4; break; case '"': strcpy(out, """); out += 6; break; case '\'': strcpy(out, "'"); out += 6; break; case '&': strcpy(out, "&"); out += 5; break; case ',': strcpy(out, ","); out += 5; break; case '|': strcpy(out, "|"); out += 5; break; default: *(out++) = c; } c = *(in++); } *(out++) = 0; return newstring; } int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb) { struct STATIONRECORD * ptr = *StationRecords; int n = 0, Len = 0; struct tm * TM; time_t NOW = time(NULL); char popup[65536] = ""; char Msg[2048]; int LocalTime = 0; int KM = DefaultDistKM; char * ptr1; while (ptr && aprs && Len < 240000) { if (ptr->Lat != 0.0 && ptr->Lon != 0.0) { if (ptr->Lat > S && ptr->Lat < N && ptr->Lon > W && ptr->Lon < E) { // See if we have the Icon - if not build char IconKey[6]; struct iconCacheEntry * Icon = iconCache; sprintf(IconKey, "%02X%02X ", ptr->iconRow, ptr->iconCol); if (ptr->IconOverlay) IconKey[4] = ptr->IconOverlay; else IconKey[4] = '@'; while (Icon) { if (strcmp(Icon->key, IconKey) == 0) // Have it break; Icon = Icon->Next; } if (Icon == NULL) createIcon(IconKey, ptr->iconRow, ptr->iconCol, ptr->IconOverlay); popup[0] = 0; if (ptr->Approx) { sprintf(Msg, "Approximate Position From Locator
"); strcat(popup, Msg); } ptr1 = doHTMLTransparency(ptr->Path); sprintf(Msg, "%s
", ptr1); strcat(popup, Msg); free(ptr1); ptr1 = doHTMLTransparency(ptr->LastPacket); sprintf(Msg, "%s
", ptr1); strcat(popup, Msg); free(ptr1); ptr1 = doHTMLTransparency(ptr->Status); sprintf(Msg, "%s
", ptr1); strcat(popup, Msg); free(ptr1); if (LocalTime) TM = localtime(&ptr->TimeLastUpdated); else TM = gmtime(&ptr->TimeLastUpdated); sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d
", TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); strcat(popup, Msg); sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f° Speed %3.1f
", myDistance(ptr->Lat, ptr->Lon, KM), myBearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); strcat(popup, Msg); if (ptr->LastWXPacket[0]) { //display wx info struct APRSConnectionInfo temp; memset(&temp, 0, sizeof(temp)); DecodeWXReport(&temp, ptr->LastWXPacket); sprintf(Msg, "Wind Speed %d MPH
", temp.WindSpeed); strcat(popup, Msg); sprintf(Msg, "Wind Gust %d MPH
", temp.WindGust); strcat(popup, Msg); sprintf(Msg, "Wind Direction %d\xC2\xB0
", temp.WindDirn); strcat(popup, Msg); sprintf(Msg, "Temperature %d\xC2\xB0 F
", temp.Temp); strcat(popup, Msg); sprintf(Msg, "Pressure %05.1f
", temp.Pressure / 10.0); strcat(popup, Msg); sprintf(Msg, "Humidity %d%%
", temp.Humidity); strcat(popup, Msg); sprintf(Msg, "Rainfall Last Hour/Last 24H/Today %5.2f, %5.2f, %5.2f (inches)", temp.RainLastHour / 100.0, temp.RainLastDay / 100.0, temp.RainToday / 100.0); ptr1 = doHTMLTransparency(Msg); sprintf(Msg, "%s
", ptr1); strcat(popup, Msg); free(ptr1); } Len += sprintf(&Buffer[Len],"A,%.4f,%.4f,%s,%s,%s,%d\r\n|", ptr->Lat, ptr->Lon, ptr->Callsign, popup, IconKey, NOW - ptr->TimeLastUpdated); if (ptr->TrackTime[0] && ptr->TrackTime[1]) // Have trackpoints { int n = ptr->Trackptr; int i; double lastLat = 0; // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros Len += sprintf(&Buffer[Len],"T,"); for (i = 0; i < TRACKPOINTS; i++) { if (ptr->TrackTime[n]) { Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); lastLat = ptr->LatTrack[n]; } n++; if (n == TRACKPOINTS) n = 0; } if (lastLat != ptr->Lat) Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->Lat, ptr->Lon); //Add current position to end of track else Len += sprintf(&Buffer[Len],"\r\n|"); } } } ptr = ptr->Next; } return Len; } /* The png_jmpbuf() macro, used in error handling, became available in * libpng version 1.0.6. If you want to be able to run your code with older * versions of libpng, you must define the macro yourself (but only if it * is not already defined by libpng!). */ #ifndef png_jmpbuf # define png_jmpbuf(png_ptr) ((png_ptr)->png_jmpbuf) #endif /* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() * returns zero if the image is a PNG and nonzero if it isn't a PNG. * * The function check_if_png() shown here, but not used, returns nonzero (true) * if the file can be opened and is a PNG, 0 (false) otherwise. * * If this call is successful, and you are going to keep the file open, * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once * you have created the png_ptr, so that libpng knows your application * has read that many bytes from the start of the file. Make sure you * don't call png_set_sig_bytes() with more than 8 bytes read or give it * an incorrect number of bytes read, or you will either have read too * many bytes (your fault), or you are telling libpng to read the wrong * number of magic bytes (also your fault). * * Many applications already read the first 2 or 4 bytes from the start * of the image to determine the file type, so it would be easiest just * to pass the bytes to png_sig_cmp() or even skip that if you know * you have a PNG file, and call png_set_sig_bytes(). */ unsigned char * user_io_ptr = 0; void __cdecl user_read_fn(png_struct * png, png_bytep Buffer, png_size_t Len) { unsigned char ** ptr = png->io_ptr; unsigned char * ptr1; ptr1 = ptr[0]; memcpy(Buffer, ptr1, Len); ptr[0]+= Len; } // This is based on example https://www1.udel.edu/CIS/software/dist/libpng-1.2.8/example.c // This decodes a png encoded image from memory int read_png(unsigned char *bytes) { png_structp png_ptr; unsigned int sig_read = 0; /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also supply the * the compiler header file version, so that we know if the application * was compiled with a compatible version of the library. REQUIRED */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { return (0); } /* Allocate/initialize the memory for image information. REQUIRED. */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, NULL, NULL); return (0); } /* Set error handling if you are using the setjmp/longjmp method (this is * the normal method of doing things with libpng). REQUIRED unless you * set up your own error handlers in the png_create_read_struct() earlier. */ user_io_ptr = (unsigned char *)&IconData; png_set_read_fn(png_ptr, (void *)&user_io_ptr,(png_rw_ptr)user_read_fn); png_set_sig_bytes(png_ptr, sig_read); png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, NULL); // Data is in info->row_pointers. Can we use it from there ?? // printf("%d %d %d\n", info_ptr->width, info_ptr->height, info_ptr->valid); return TRUE; } void Myabort() {} // This is based on pngfile.c //------------------------------------- // PNGFILE.C -- Image File Functions //------------------------------------- // Copyright 2000, Willem van Schaik. For conditions of distribution and // use, see the copyright/license/disclaimer notice in png.h // Encodes pDiData to png format in memory void my_png_write_data(struct png_struct_def * png_ptr, png_bytep data, png_size_t length) { struct iconCacheEntry * Icon = png_ptr->io_ptr; if (Icon->pngimagelen + (int)length > Icon->pngmalloclen) { Icon->pngmalloclen += length; Icon->pngimage = realloc(Icon->pngimage, Icon->pngmalloclen); } memcpy(&Icon->pngimage[Icon->pngimagelen], data, length); Icon->pngimagelen += length; } // io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); // Area png_uint_32 check; static void png_flush(png_structp png_ptr) { } unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon) { const int ciBitDepth = 8; const int ciChannels = 3; png_structp png_ptr; png_infop info_ptr = NULL; png_uint_32 ulRowBytes; static png_byte **ppbRowPointers = NULL; int i; // Set up image array and pointer. First allocate a buffer as big as the original // in the unlikely event of it being too small write_data will realloc it Icon->pngmalloclen = iWidth * iHeight * 3; Icon->pngimage = malloc(Icon->pngmalloclen); Icon->pngimagelen = 0; // prepare the standard PNG structures png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { return FALSE; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp) NULL); return FALSE; } { // initialize the png structure png_set_write_fn(png_ptr, Icon, my_png_write_data, png_flush); png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // write the file header information png_write_info(png_ptr, info_ptr); // row_bytes is the width x number of channels ulRowBytes = iWidth * ciChannels; // we can allocate memory for an array of row-pointers if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL) Debugprintf( "Visualpng: Out of memory"); // set the individual row-pointers to point at the correct offsets for (i = 0; i < iHeight; i++) ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2); // write out the entire image data in one call png_write_image (png_ptr, ppbRowPointers); // write the additional chunks to the PNG file (not really needed) png_write_end(png_ptr, info_ptr); // and we're done free (ppbRowPointers); ppbRowPointers = NULL; // clean up after the write, and free any memory allocated png_destroy_write_struct(&png_ptr, (png_infopp) NULL); // yepp, done } return Icon->pngimage; } void SaveAPRSMessage(struct APRSMESSAGE * ptr) { // Save messages in case of a restart char FN[250]; FILE *file; // Set up filename if (BPQDirectory[0] == 0) { strcpy(FN,"APRSMsgs.dat"); } else { strcpy(FN,BPQDirectory); strcat(FN,"/"); strcat(FN,"APRSMsgs.dat"); } if ((file = fopen(FN, "a")) == NULL) return ; fprintf(file, "%d %s,%s,%s,%s,%s\n", time(NULL), ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, ptr->Text); fclose(file); } void ClearSavedMessages() { char FN[250]; FILE *file; // Set up filename if (BPQDirectory[0] == 0) { strcpy(FN,"APRSMsgs.dat"); } else { strcpy(FN,BPQDirectory); strcat(FN,"/"); strcat(FN,"APRSMsgs.dat"); } if ((file = fopen(FN, "w")) == NULL) return ; fclose(file); } void GetSavedAPRSMessages() { // Get Saved messages // 1668768157 SERVER ,GM8BPQ-2 ,D7Yx,10:42,filter m/200 active char FN[250]; FILE *file; struct APRSMESSAGE * Message; struct APRSMESSAGE * ptr; char Line[256]; 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); }