2022-08-28 09:35:46 +01:00
/*
2022-11-14 14:02:28 +00:00
Copyright 2001 - 2022 John Wiseman G8BPQ
2022-08-28 09:35:46 +01:00
This file is part of LinBPQ / BPQ32 .
LinBPQ / BPQ32 is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
LinBPQ / BPQ32 is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with LinBPQ / BPQ32 . If not , see http : //www.gnu.org/licenses
*/
// Module to implement APRS "New Paradigm" Digipeater and APRS-IS Gateway
// First Version, November 2011
# pragma data_seg("_BPQDATA")
# define _CRT_SECURE_NO_DEPRECATE
# include <stdio.h>
# include "CHeaders.h"
# include "bpq32.h"
# include <time.h>
# include "kernelresource.h"
# include "tncinfo.h"
# include "bpqaprs.h"
# ifndef WIN32
# include <unistd.h>
# include <sys/mman.h>
# include <sys/un.h>
int sfd ;
struct sockaddr_un my_addr , peer_addr ;
socklen_t peer_addr_size ;
# endif
# define MAXAGE 3600 * 12 // 12 Hours
# define MAXCALLS 20 // Max Flood, Trace and Digi
# define GATETIMELIMIT 40 * 60 // Don't gate to RF if station not heard for this time (40 mins)
static BOOL APIENTRY GETSENDNETFRAMEADDR ( ) ;
static VOID DoSecTimer ( ) ;
static VOID DoMinTimer ( ) ;
static int APRSProcessLine ( char * buf ) ;
static BOOL APRSReadConfigFile ( ) ;
VOID APRSISThread ( void * Report ) ;
VOID __cdecl Debugprintf ( const char * format , . . . ) ;
VOID __cdecl Consoleprintf ( const char * format , . . . ) ;
BOOL APIENTRY Send_AX ( PMESSAGE Block , DWORD Len , UCHAR Port ) ;
VOID Send_AX_Datagram ( PDIGIMESSAGE Block , DWORD Len , UCHAR Port ) ;
char * strlop ( char * buf , char delim ) ;
int APRSDecodeFrame ( char * msg , char * buffer , time_t Stamp , UINT 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 ) ;
2022-11-18 17:03:39 +00:00
void SaveAPRSMessage ( struct APRSMESSAGE * ptr ) ;
void ClearSavedMessages ( ) ;
void GetSavedAPRSMessages ( ) ;
2022-08-28 09:35:46 +01:00
extern int SemHeldByAPI ;
extern int APRSMONDECODE ( ) ;
extern struct ConsoleInfo MonWindow ;
extern char VersionString [ ] ;
2022-11-18 17:03:39 +00:00
BOOL SaveAPRSMsgs = 0 ;
2022-08-28 09:35:46 +01:00
BOOL LogAPRSIS = FALSE ;
// All data should be initialised to force into shared segment
static char ConfigClassName [ ] = " CONFIG " ;
BPQVECSTRUC * APRSMONVECPTR ;
extern int MONDECODE ( ) ;
extern VOID * zalloc ( int len ) ;
extern BOOL StartMinimized ;
extern char * PortConfig [ ] ;
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
UINT 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 [ 32 ] ; // 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 ;
extern int ADSBPort ;
extern char ADSBHost [ ] ;
BOOL BlueNMEAOK = FALSE ;
int BlueNMEATimer = 0 ;
BOOL GPSSetsLocator = 0 ; // Update Map Location from GPS
double SOG , COG ; // From GPS
double Lat = 0.0 ;
double Lon = 0.0 ;
BOOL PosnSet = FALSE ;
/*
The null position should be include the \ . symbol ( unknown / indeterminate
position ) . For example , a Position Report for a station with unknown position
will contain the coordinates <EFBFBD> 0000.00 N \ 00000.00 W . <EFBFBD>
*/
char * FloodCalls = 0 ; // Calls to relay using N-n without tracing
char * TraceCalls = 0 ; // Calls to relay using N-n with tracing
char * DigiCalls = 0 ; // Calls for normal relaying
UCHAR FloodAX [ MAXCALLS ] [ 7 ] = { 0 } ;
UCHAR TraceAX [ MAXCALLS ] [ 7 ] = { 0 } ;
UCHAR DigiAX [ MAXCALLS ] [ 7 ] = { 0 } ;
int FloodLen [ MAXCALLS ] ;
int TraceLen [ MAXCALLS ] ;
int DigiLen [ MAXCALLS ] ;
int ISPort = 0 ;
char ISHost [ 256 ] = " " ;
int ISPasscode = 0 ;
char NodeFilter [ 1000 ] = " m/50 " ; // Filter when the isn't an application
char ISFilter [ 1000 ] = " m/50 " ; // Current Filter
char APPLFilter [ 1000 ] = " " ; // Filter when an Applcation is running
extern BOOL IGateEnabled ;
char StatusMsg [ 256 ] = " " ; // Must be in shared segment
int StatusMsgLen = 0 ;
char * BeaconPath [ 33 ] = { 0 } ;
char CrossPortMap [ 33 ] [ 33 ] = { 0 } ;
char APRSBridgeMap [ 33 ] [ 33 ] = { 0 } ;
UCHAR BeaconHeader [ 33 ] [ 10 ] [ 7 ] = { " " } ; // Dest, Source and up to 8 digis
int BeaconHddrLen [ 33 ] = { 0 } ; // Actual Length used
UCHAR GatedHeader [ 33 ] [ 10 ] [ 7 ] = { " " } ; // Dest, Source and up to 8 digis for messages gated from IS
int GatedHddrLen [ 33 ] = { 0 } ; // Actual Length used
char CFGSYMBOL = ' a ' ;
char CFGSYMSET = ' B ' ;
char SYMBOL = ' = ' ; // Unknown Locaton
char SYMSET = ' / ' ;
BOOL TraceDigi = FALSE ; // Add Trace to packets relayed on Digi Calls
BOOL SATGate = FALSE ; // Delay Gating to IS directly heard packets
2022-11-23 10:56:20 +00:00
BOOL RXOnly = FALSE ; // Run as RX only IGATE, ie don't gate anything to RF
2022-08-28 09:35:46 +01:00
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 [ 33 ] ;
int Interval ;
int Timer ;
} ;
struct OBJECT * ObjectList ; // List of objects to send;
int ObjectCount = 0 ;
# include <math.h>
# define M_PI 3.14159265358979323846
int RetryCount = 4 ;
int RetryTimer = 45 ;
int ExpireTime = 120 ;
int TrackExpireTime = 1440 ;
BOOL SuppressNullPosn = FALSE ;
BOOL DefaultNoTracks = FALSE ;
int MaxStations = 1000 ;
int SharedMemorySize = 0 ;
RECT Rect , MsgRect , StnRect ;
char Key [ 80 ] ;
// function prototypes
VOID RefreshMessages ( ) ;
// a few global variables
char APRSDir [ MAX_PATH ] = " BPQAPRS " ;
char DF [ MAX_PATH ] ;
# define FEND 0xC0 // KISS CONTROL CODES
# define FESC 0xDB
# define TFEND 0xDC
# define TFESC 0xDD
int StationCount = 0 ;
UCHAR NextSeq = 1 ;
// Stationrecords are stored in a shared memory segment. based at APRSStationMemory (normally 0x43000000)
// A pointer to the first is placed at the start of this
struct STATIONRECORD * * StationRecords = NULL ;
struct STATIONRECORD * StationRecordPool = NULL ;
struct APRSMESSAGE * MessageRecordPool = NULL ;
struct SharedMem * SMEM ;
UCHAR * Shared ;
UCHAR * StnRecordBase ;
VOID SendObject ( struct OBJECT * Object ) ;
VOID MonitorAPRSIS ( char * Msg , int MsgLen , BOOL TX ) ;
# ifndef WIN32
# define WSAEWOULDBLOCK 11
# endif
HANDLE hMapFile ;
// Logging
static int LogAge = 14 ;
# ifdef WIN32
int DeleteAPRSLogFiles ( )
{
WIN32_FIND_DATA ffd ;
char szDir [ MAX_PATH ] ;
char File [ MAX_PATH ] ;
HANDLE hFind = INVALID_HANDLE_VALUE ;
DWORD dwError = 0 ;
LARGE_INTEGER ft ;
time_t now = time ( NULL ) ;
int Age ;
// Prepare string for use with FindFile functions. First, copy the
// string to a buffer, then append '\*' to the directory name.
strcpy ( szDir , GetLogDirectory ( ) ) ;
strcat ( szDir , " /logs/APRS*.log " ) ;
// Find the first file in the directory.
hFind = FindFirstFile ( szDir , & ffd ) ;
if ( INVALID_HANDLE_VALUE = = hFind )
return dwError ;
// Walk directory
do
{
if ( ffd . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
OutputDebugString ( ffd . cFileName ) ;
}
else
{
ft . HighPart = ffd . ftCreationTime . dwHighDateTime ;
ft . LowPart = ffd . ftCreationTime . dwLowDateTime ;
ft . QuadPart - = 116444736000000000 ;
ft . QuadPart / = 10000000 ;
Age = ( int ) ( ( now - ft . LowPart ) / 86400 ) ;
if ( Age > LogAge )
{
sprintf ( File , " %s/logs/%s%c " , GetLogDirectory ( ) , ffd . cFileName , 0 ) ;
Debugprintf ( " Deleting %s " , File ) ;
DeleteFile ( File ) ;
}
}
}
while ( FindNextFile ( hFind , & ffd ) ! = 0 ) ;
FindClose ( hFind ) ;
return dwError ;
}
# else
# include <dirent.h>
int APRSFilter ( const struct dirent * dir )
{
return ( memcmp ( dir - > d_name , " APRS " , 4 ) = = 0 & & strstr ( dir - > d_name , " .log " ) ) ;
}
int DeleteAPRSLogFiles ( )
{
struct dirent * * namelist ;
int n ;
struct stat STAT ;
time_t now = time ( NULL ) ;
int Age = 0 , res ;
char FN [ 256 ] ;
n = scandir ( " logs " , & namelist , APRSFilter , alphasort ) ;
if ( n < 0 )
perror ( " scandir " ) ;
else
{
while ( n - - )
{
sprintf ( FN , " logs/%s " , namelist [ n ] - > d_name ) ;
if ( stat ( FN , & STAT ) = = 0 )
{
Age = ( now - STAT . st_mtime ) / 86400 ;
if ( Age > LogAge )
{
Debugprintf ( " Deleting %s \n " , FN ) ;
unlink ( FN ) ;
}
}
free ( namelist [ n ] ) ;
}
free ( namelist ) ;
}
return 0 ;
}
# endif
int APRSWriteLog ( char * msg )
{
FILE * file ;
UCHAR Value [ MAX_PATH ] ;
time_t T ;
struct tm * tm ;
if ( LogAPRSIS = = 0 )
return 0 ;
if ( strchr ( msg , ' \n ' ) = = 0 )
strcat ( msg , " \r \n " ) ;
T = time ( NULL ) ;
tm = gmtime ( & T ) ;
if ( GetLogDirectory ( ) [ 0 ] = = 0 )
{
strcpy ( Value , " logs/APRS_ " ) ;
}
else
{
strcpy ( Value , GetLogDirectory ( ) ) ;
strcat ( Value , " / " ) ;
strcat ( Value , " logs/APRS_ " ) ;
}
sprintf ( Value , " %s%02d%02d%02d.log " , Value ,
tm - > tm_year - 100 , tm - > tm_mon + 1 , tm - > tm_mday ) ;
if ( ( file = fopen ( Value , " ab " ) ) = = NULL )
return FALSE ;
fputs ( msg , file ) ;
fclose ( file ) ;
return 0 ;
}
int ISSend ( SOCKET sock , char * Msg , int Len , int flags )
{
int Loops = 0 ;
int Sent ;
MonitorAPRSIS ( Msg , Len , TRUE ) ;
Sent = send ( sock , Msg , Len , flags ) ;
while ( Sent ! = Len & & Loops + + < 300 ) // 10 secs max
{
if ( ( Sent = = SOCKET_ERROR ) & & ( WSAGetLastError ( ) ! = WSAEWOULDBLOCK ) )
return SOCKET_ERROR ;
if ( Sent > 0 ) // something sent
{
Len - = Sent ;
memmove ( Msg , & Msg [ Sent ] , Len ) ;
}
Sleep ( 30 ) ;
Sent = send ( sock , Msg , Len , flags ) ;
}
return Sent ;
}
void * endofStations ;
Dll BOOL APIENTRY Init_APRS ( )
{
int i ;
char * DCall ;
# ifdef WIN32
HKEY hKey = 0 ;
int retCode , Vallen , Type ;
# else
int fd ;
char RX_SOCK_PATH [ ] = " BPQAPRSrxsock " ;
char TX_SOCK_PATH [ ] = " BPQAPRStxsock " ;
char SharedName [ 256 ] ;
char * ptr1 ;
# endif
struct STATIONRECORD * Stn1 , * Stn2 ;
struct APRSMESSAGE * Msg1 , * Msg2 ;
// Clear tables in case a restart
StationRecords = NULL ;
StationCount = 0 ;
HEARDENTRIES = 0 ;
MAXHEARDENTRIES = 0 ;
MobileBeaconInterval = 0 ;
BeaconInterval = 0 ;
DeleteAPRSLogFiles ( ) ;
memset ( MHTABLE , 0 , sizeof ( MHTABLE ) ) ;
ConvToAX25 ( MYNODECALL , MYCALL ) ;
ConvToAX25 ( " TCPIP " , axTCPIP ) ;
ConvToAX25 ( " RFONLY " , axRFONLY ) ;
ConvToAX25 ( " NOGATE " , axNOGATE ) ;
memset ( & FloodAX [ 0 ] [ 0 ] , 0 , sizeof ( FloodAX ) ) ;
memset ( & TraceAX [ 0 ] [ 0 ] , 0 , sizeof ( TraceAX ) ) ;
memset ( & DigiAX [ 0 ] [ 0 ] , 0 , sizeof ( DigiAX ) ) ;
APRSPortMask = 0 ;
memset ( BeaconPath , sizeof ( BeaconPath ) , 0 ) ;
memset ( & CrossPortMap [ 0 ] [ 0 ] , 0 , sizeof ( CrossPortMap ) ) ;
memset ( & APRSBridgeMap [ 0 ] [ 0 ] , 0 , sizeof ( APRSBridgeMap ) ) ;
for ( i = 1 ; i < = 32 ; 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 , & param ) ; // 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 ) ;
2022-11-18 17:03:39 +00:00
// Reload saved messages
if ( SaveAPRSMsgs )
GetSavedAPRSMessages ( ) ;
2022-08-28 09:35:46 +01:00
// 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.
2022-11-18 17:03:39 +00:00
// Fork and Exec program
2022-08-28 09:35:46 +01:00
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
}
2022-10-03 15:55:07 +01:00
time_t lastSecTimer = 0 ;
2022-08-28 09:35:46 +01:00
Dll VOID APIENTRY Poll_APRS ( )
{
2022-10-03 15:55:07 +01:00
time_t Now = time ( NULL ) ;
2022-08-28 09:35:46 +01:00
2022-10-03 15:55:07 +01:00
if ( lastSecTimer ! = Now )
2022-08-28 09:35:46 +01:00
{
2022-10-03 15:55:07 +01:00
lastSecTimer = Now ;
2022-08-28 09:35:46 +01:00
DoSecTimer ( ) ;
MinTimer - - ;
if ( MinTimer = = 0 )
{
2022-10-03 15:55:07 +01:00
MinTimer = 60 ;
2022-08-28 09:35:46 +01:00
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 ;
2022-11-18 17:03:39 +00:00
ClearSavedMessages ( ) ;
2022-08-28 09:35:46 +01:00
}
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 & ( 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 )
2022-11-23 10:56:20 +00:00
// Too many digis or Last digis is NOGATE or RFONLY - dont send to IS
2022-08-28 09:35:46 +01:00
NoGate = TRUE ;
}
if ( AdjBuff - > CTL ! = 3 | | AdjBuff - > PID ! = 0xf0 ) // Only UI
{
ReleaseBuffer ( monbuff ) ;
continue ;
}
// Bridge if requested
for ( toPort = 1 ; toPort < = 32 ; toPort + + )
{
if ( APRSBridgeMap [ Port ] [ toPort ] )
{
MESSAGE * Buffer = GetBuff ( ) ;
struct PORTCONTROL * PORT ;
if ( Buffer )
{
memcpy ( Buffer , Orig , Orig - > LENGTH ) ;
Buffer - > PORT = toPort ;
PORT = GetPortTableEntryFromPortNum ( toPort ) ;
2022-11-14 14:02:28 +00:00
2022-08-28 09:35:46 +01:00
if ( PORT )
2022-11-14 14:02:28 +00:00
{
if ( PORT - > SmartIDInterval & & PORT - > SmartIDNeeded = = 0 )
{
// Using Smart ID, but none scheduled
PORT - > SmartIDNeeded = time ( NULL ) + PORT - > SmartIDInterval ;
}
2022-08-28 09:35:46 +01:00
PUT_ON_PORT_Q ( PORT , Buffer ) ;
2022-11-14 14:02:28 +00:00
}
2022-08-28 09:35:46 +01:00
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
}
2022-11-23 10:56:20 +00:00
if ( NoGate | | RXOnly )
2022-08-28 09:35:46 +01:00
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
{
2022-11-23 10:56:20 +00:00
// 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 ;
}
}
2022-08-28 09:35:46 +01:00
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 ) ;
2022-11-23 10:56:20 +00:00
2022-08-28 09:35:46 +01:00
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 , 21 + ( 7 * Digis ) ) ; // Header, Dest, Source, Addresses and Digis
len = Msg . LENGTH - 21 - ( 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 < = 32 ; 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 [ 34 ] ; // Config fnom 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 > 32 )
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 ;
}
2022-11-23 10:56:20 +00:00
if ( _stricmp ( ptr , " RXOnly " ) = = 0 )
{
RXOnly = TRUE ;
return TRUE ;
}
2022-08-28 09:35:46 +01:00
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 ;
}
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 | = 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 ;
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 > 32 )
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 , " 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 , " 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 < 32 ; 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 < = 32 ; 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 ;
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 ;
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 ) ;
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 [ 10 ] = " " ;
struct STATIONRECORD * Station ;
struct PORTCONTROL * PORT ;
if ( PosnSet = = FALSE )
return ;
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
GetSemaphore ( & Semaphore , 12 ) ;
Send_AX_Datagram ( & Msg , Len + 2 , toPort ) ;
FreeSemaphore ( & Semaphore ) ;
return ;
}
for ( Port = 1 ; Port < = 32 ; 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 ) ;
GetSemaphore ( & Semaphore , 12 ) ;
Send_AX_Datagram ( & Msg , Len + 2 , Port ) ;
FreeSemaphore ( & Semaphore ) ;
// if Port has interlock set pause before next
PORT = GetPortTableEntryFromPortNum ( Port ) ;
2022-10-18 10:09:06 +01:00
// Just pause for all ports
// if (PORT && PORT->PORTINTERLOCK)
2022-08-28 09:35:46 +01:00
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 < = 32 ; 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 ) ;
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
2022-11-23 10:56:20 +00:00
if ( APRSISOpen & & BeacontoIS & & RXOnly = = 0 )
2022-08-28 09:35:46 +01:00
{
Msg . PID = 0xf0 ;
Msg . CTL = 3 ;
Len = sprintf ( Msg . L2DATA , " <IGATE,MSG_CNT=%d,LOC_CNT=%d " , MessageCount , CountLocalStations ( ) ) ;
for ( Port = 1 ; Port < = 32 ; 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*:<IGATE,MSG_CNT=%d,LOC_CNT=%d \r \n " , APRSCall , APRSDest , MessageCount , CountLocalStations ( ) ) ;
ISSend ( sock , Msg . L2DATA , Len , 0 ) ;
// Debugprintf(">%s", Msg.L2DATA);
}
}
VOID DoSecTimer ( )
{
struct OBJECT * Object = ObjectList ;
while ( Object )
{
Object - > Timer - - ;
if ( Object - > Timer = = 0 )
{
Object - > Timer = 60 * Object - > Interval ;
SendObject ( Object ) ;
}
Object = Object - > Next ;
}
// Check SatGate Mode delay Q
if ( SatISQueue )
{
time_t NOW = time ( NULL ) ;
ISDELAY * SatISEntry = SatISQueue ;
ISDELAY * Prev = NULL ;
while ( SatISEntry )
{
if ( SatISEntry - > SendTIme < NOW )
{
// Send it
ISSend ( sock , SatISEntry - > ISMSG , ( int ) strlen ( SatISEntry - > ISMSG ) , 0 ) ;
free ( SatISEntry - > ISMSG ) ;
if ( Prev )
Prev - > Next = SatISEntry - > Next ;
else
SatISQueue = SatISEntry - > Next ;
free ( SatISEntry ) ;
return ; // unlinkely to get 2 in sam esecond and doesn;t matter if we delay a bit more
}
Prev = SatISEntry ;
SatISEntry = SatISEntry - > Next ;
}
}
if ( ISPort & & APRSISOpen = = 0 & & IGateEnabled )
{
ISDelayTimer + + ;
if ( ISDelayTimer > 60 )
{
ISDelayTimer = 0 ;
_beginthread ( APRSISThread , 0 , ( VOID * ) TRUE ) ;
}
}
if ( HostName [ 0 ] )
{
if ( BlueNMEAOK = = 0 )
{
BlueNMEATimer + + ;
if ( BlueNMEATimer > 15 )
{
BlueNMEATimer = 0 ;
_beginthread ( TCPConnect , 0 , 0 ) ;
}
}
}
if ( 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 ( 3 rd - party ) packets are seen on RF gated by the station
and containing TCPIP or TCPXX in the 3 rd - 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 ) )
2022-11-23 10:56:20 +00:00
if ( memcmp ( & GPSMsg [ 3 ] , " RMC " , 3 ) = = 0 )
2022-08-28 09:35:46 +01:00
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 " ) ;
# 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
{
2022-11-23 10:56:20 +00:00
if ( memcmp ( & UDPMsg [ 3 ] , " RMC " , 3 ) = = 0 )
2022-08-28 09:35:46 +01:00
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 ) ;
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 , & param ) ;
}
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 ;
}
}
}
// 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 \n Host: %s \r \n Connection: close \r \n Content-Length: 0 \r \n User-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 )
{
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 ;
}
/*
2E0 AYY > APU25N , TCPIP * , qAC , AHUBSWE2 : = 5105.18 N / 00108.19 E - Paul in Folkestone Kent { UIV32N }
G0AVP - 12 > APT310 , MB7UC * , WIDE3 - 2 , qAR , G3PWJ : ! 5047.19 N \ 00108.45 Wk074 / 000 / Paul mobile
G0CJM - 12 > CQ , TCPIP * , qAC , AHUBSWE2 : = / 3 & R < NDEp / B > io94sg
M0HFC > APRS , WIDE2 - 1 , qAR , G0MNI : ! 5342.83 N / 00013.79 W # 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 ) > 50 )
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 ;
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 ) ;
Station - > Object = Object ;
return 0 ;
case ' @ ' :
case ' / ' : // Timestamp, No Messaging
TimeStamp = + + Payload ;
Payload + = 6 ;
case ' = ' :
case ' ! ' :
Payload + + ;
DecodeLocationString ( Payload , Station ) ;
return 0 ;
case ' > ' : // Status
strcpy ( Station - > Status , & Payload [ 1 ] ) ;
case ' < ' : // Capabilities
case ' _ ' : // Weather
case ' T ' : // Telemetry
break ;
case ' : ' : // Message
return ProcessMessage ( Payload , Station ) ;
case ' } ' : // Third Party Header
// Process Payload as a new message
// }GM7HHB-9>APDR12,TCPIP,MM1AVR*:=5556.62N/00303.55W>204/000/A=000213 http://www.dstartv.com
Callsign = Msg = & Payload [ 1 ] ;
Path = strchr ( Msg , ' > ' ) ;
if ( Path = = NULL )
return 0 ;
* Path + + = 0 ;
Payload = strchr ( Path , ' : ' ) ;
if ( Payload = = NULL )
return 0 ;
* ( Payload + + ) = 0 ;
// Check Dup Filter
if ( CheckforDups ( Callsign , Payload , ( int ) strlen ( Payload ) ) )
return 0 ;
// Look up station - create a new one if not found
TPStation = FindStation ( Callsign , TRUE ) ;
strcpy ( TPStation - > Path , Path ) ;
strcpy ( TPStation - > LastPacket , Payload ) ;
TPStation - > LastPort = 0 ; // Heard on RF, but info is from IS
DecodeAPRSPayload ( Payload , TPStation ) ;
TPStation - > TimeLastUpdated = time ( NULL ) ;
return 0 ;
default :
// Non - APRS Message. If Payload contains a valid 6 char locator derive a position from it
if ( Station - > Lat ! = 0.0 | | Station - > Lon ! = 0.0 )
return 0 ; // already have position
ptr = strtok_s ( Payload , " ,[](){} \n " , & context ) ;
while ( ptr & & ptr [ 0 ] )
{
if ( strlen ( ptr ) = = 6 ) // could be locator
{
double Lat = 0.0 , Lon = 0.0 ;
if ( FromLOC ( ptr , & Lat , & Lon ) )
{
if ( Lat ! = 0.0 & & Lon ! = 0.0 )
{
// Randomise in locator square.
Lat = Lat + ( ( rand ( ) / 24.0 ) / RAND_MAX ) ;
Lon = Lon + ( ( rand ( ) / 12.0 ) / RAND_MAX ) ;
Station - > Lat = Lat ;
Station - > Lon = Lon ;
Station - > Approx = 1 ;
Debugprintf ( " %s %s %s " , Station - > Callsign , Station - > Path , Payload ) ;
}
}
}
ptr = strtok_s ( NULL , " ,[](){} \n " , & context ) ;
}
return 0 ;
}
return 0 ;
}
// Convert MIC-E Char to Lat Digit (offset by 0x30)
// 0123456789 @ABCDEFGHIJKLMNOPQRSTUVWXYZ
char MicELat [ ] = " 0123456789???????0123456789 ???0123456789 " ;
char MicECode [ ] = " 0000000000???????111111111110???22222222222 " ;
VOID Decode_MIC_E_Packet ( char * Payload , struct STATIONRECORD * Station )
{
// Info is encoded in the Dest Addr (in Station->Path) as well as Payload.
// See APRS Spec for full details
char Lat [ 10 ] ; // DDMMHH
char LatDeg [ 3 ] ;
char * ptr ;
char c ;
int i , n ;
int LonDeg , LonMin ;
BOOL LonOffset = FALSE ;
char NS = ' S ' ;
char EW = ' E ' ;
UCHAR SymChar , SymSet ;
double NewLat , NewLon ;
int SP , DC , SE ; // Course/Speed Encoded
int Course , Speed ;
// Make sure packet is long enough to have an valid address
if ( strlen ( Payload ) < 9 )
return ;
ptr = & Station - > Path [ 0 ] ;
for ( i = 0 ; i < 6 ; i + + )
{
n = ( * ( ptr + + ) ) - 0x30 ;
c = MicELat [ n ] ;
if ( c = = ' ? ' ) // Illegal
return ;
if ( c = = ' ' )
c = ' 0 ' ; // Limited Precision
Lat [ i ] = c ;
}
Lat [ 6 ] = 0 ;
if ( Station - > Path [ 3 ] > ' O ' )
NS = ' N ' ;
if ( Station - > Path [ 5 ] > ' O ' )
EW = ' W ' ;
if ( Station - > Path [ 4 ] > ' O ' )
LonOffset = TRUE ;
n = Payload [ 1 ] - 28 ; // Lon Degrees S9PU0T,WIDE1-1,WIDE2-2,qAR,WB9TLH-15:`rB0oII>/]"6W}44
if ( LonOffset )
n + = 100 ;
if ( n > 179 & & n < 190 )
n - = 80 ;
else
if ( n > 189 & & n < 200 )
n - = 190 ;
LonDeg = n ;
/*
To decode the longitude degrees value :
1. subtract 28 from the d + 28 value to obtain d .
2. if the longitude offset is + 100 degrees , add 100 to d .
3. subtract 80 if 180 <EFBFBD> d <EFBFBD> 189
( i . e . the longitude is in the range 100 <EFBFBD> 109 degrees ) .
4. or , subtract 190 if 190 <EFBFBD> d <EFBFBD> 199.
( i . e . the longitude is in the range 0 <EFBFBD> 9 degrees ) .
*/
n = Payload [ 2 ] - 28 ; // Lon Mins
if ( n > 59 )
n - = 60 ;
LonMin = n ;
n = Payload [ 3 ] - 28 ; // Lon Mins/100;
//1. subtract 28 from the m+28 value to obtain m.
//2. subtract 60 if m <20> 60.
//(i.e. the longitude minutes is in the range 0<> 9).
memcpy ( LatDeg , Lat , 2 ) ;
LatDeg [ 2 ] = 0 ;
NewLat = atof ( LatDeg ) + ( atof ( Lat + 2 ) / 6000.0 ) ;
if ( NS = = ' S ' )
NewLat = - NewLat ;
NewLon = LonDeg + LonMin / 60.0 + n / 6000.0 ;
if ( EW = = ' W ' ) // West
NewLon = - NewLon ;
SP = Payload [ 4 ] - 28 ;
DC = Payload [ 5 ] - 28 ;
SE = Payload [ 6 ] - 28 ; // Course 100 and 10 degs
Speed = DC / 10 ; // Quotient = Speed Units
Course = DC - ( Speed * 10 ) ; // Remainder = Course Deg/100
Course = SE + ( Course * 100 ) ;
Speed + = SP * 10 ;
if ( Speed > = 800 )
Speed - = 800 ;
if ( Course > = 400 )
Course - = 400 ;
Station - > Course = Course ;
Station - > Speed = Speed * 1.15077945 ; // MPH
// Debugprintf("MIC-E Course/Speed %s %d %d", Station->Callsign, Course, Speed);
SymChar = Payload [ 7 ] ; // Symbol
SymSet = Payload [ 8 ] ; // Symbol
SymChar - = ' ! ' ;
Station - > IconOverlay = 0 ;
if ( ( SymSet > = ' 0 ' & & SymSet < = ' 9 ' ) | | ( SymSet > = ' A ' & & SymSet < = ' Z ' ) )
{
SymChar + = 96 ;
Station - > IconOverlay = SymSet ;
}
else
if ( SymSet = = ' \\ ' )
SymChar + = 96 ;
Station - > iconRow = SymChar > > 4 ;
Station - > iconCol = SymChar & 15 ;
if ( NewLat > 90 | | NewLat < - 90 | | NewLon > 180 | | NewLon < - 180 )
return ;
if ( Station - > Lat ! = NewLat | | Station - > Lon ! = NewLon )
{
time_t NOW = time ( NULL ) ;
time_t Age = NOW - Station - > TimeLastUpdated ;
if ( Age > 15 ) // Don't update too often
{
// Add to track
// if (memcmp(Station->Callsign, "ISS ", 4) == 0)
// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket);
Station - > LatTrack [ Station - > Trackptr ] = NewLat ;
Station - > LonTrack [ Station - > Trackptr ] = NewLon ;
Station - > TrackTime [ Station - > Trackptr ] = NOW ;
Station - > Trackptr + + ;
Station - > Moved = TRUE ;
if ( Station - > Trackptr = = TRACKPOINTS )
Station - > Trackptr = 0 ;
}
Station - > Lat = NewLat ;
Station - > Lon = NewLon ;
Station - > Approx = 0 ;
}
return ;
}
/*
INT_PTR CALLBACK ChildDialogProc ( HWND hDlg , UINT message , WPARAM wParam , LPARAM lParam )
{
// This processes messages from controls on the tab subpages
int Command ;
// int retCode, disp;
// char Key[80];
// HKEY hKey;
// BOOL OK;
// OPENFILENAME ofn;
// char Digis[100];
int Port = PortNum [ CurrentPage ] ;
switch ( message )
{
case WM_NOTIFY :
switch ( ( ( LPNMHDR ) lParam ) - > code )
{
case TCN_SELCHANGE :
OnSelChanged ( hDlg ) ;
return TRUE ;
// More cases on WM_NOTIFY switch.
case NM_CHAR :
return TRUE ;
}
break ;
case WM_INITDIALOG :
OnChildDialogInit ( hDlg ) ;
return ( INT_PTR ) TRUE ;
case WM_CTLCOLORDLG :
return ( LONG ) bgBrush ;
case WM_CTLCOLORSTATIC :
{
HDC hdcStatic = ( HDC ) wParam ;
SetTextColor ( hdcStatic , RGB ( 0 , 0 , 0 ) ) ;
SetBkMode ( hdcStatic , TRANSPARENT ) ;
return ( LONG ) bgBrush ;
}
case WM_COMMAND :
Command = LOWORD ( wParam ) ;
if ( Command = = 2002 )
return TRUE ;
switch ( Command )
{
/* case IDC_FILE:
memset ( & ofn , 0 , sizeof ( OPENFILENAME ) ) ;
ofn . lStructSize = sizeof ( OPENFILENAME ) ;
ofn . hwndOwner = hDlg ;
ofn . lpstrFile = & FN [ Port ] [ 0 ] ;
ofn . nMaxFile = 250 ;
ofn . lpstrTitle = " File to send as beacon " ;
ofn . lpstrInitialDir = GetBPQDirectory ( ) ;
if ( GetOpenFileName ( & ofn ) )
SetDlgItemText ( hDlg , IDC_FILENAME , & FN [ Port ] [ 0 ] ) ;
break ;
case IDOK :
GetDlgItemText ( hDlg , IDC_UIDEST , & UIDEST [ Port ] [ 0 ] , 10 ) ;
if ( UIDigi [ Port ] )
{
free ( UIDigi [ Port ] ) ;
UIDigi [ Port ] = NULL ;
}
if ( UIDigiAX [ Port ] )
{
free ( UIDigiAX [ Port ] ) ;
UIDigiAX [ Port ] = NULL ;
}
GetDlgItemText ( hDlg , IDC_UIDIGIS , Digis , 99 ) ;
UIDigi [ Port ] = _strdup ( Digis ) ;
GetDlgItemText ( hDlg , IDC_FILENAME , & FN [ Port ] [ 0 ] , 255 ) ;
GetDlgItemText ( hDlg , IDC_MESSAGE , & Message [ Port ] [ 0 ] , 1000 ) ;
Interval [ Port ] = GetDlgItemInt ( hDlg , IDC_INTERVAL , & OK , FALSE ) ;
MinCounter [ Port ] = Interval [ Port ] ;
SendFromFile [ Port ] = IsDlgButtonChecked ( hDlg , IDC_FROMFILE ) ;
sprintf ( Key , " SOFTWARE \\ G8BPQ \\ BPQ32 \\ UIUtil \\ UIPort%d " , PortNum [ CurrentPage ] ) ;
retCode = RegCreateKeyEx ( REGTREE ,
Key , 0 , 0 , 0 , KEY_ALL_ACCESS , NULL , & hKey , & disp ) ;
if ( retCode = = ERROR_SUCCESS )
{
retCode = RegSetValueEx ( hKey , " UIDEST " , 0 , REG_SZ , ( BYTE * ) & UIDEST [ Port ] [ 0 ] , strlen ( & UIDEST [ Port ] [ 0 ] ) ) ;
retCode = RegSetValueEx ( hKey , " FileName " , 0 , REG_SZ , ( BYTE * ) & FN [ Port ] [ 0 ] , strlen ( & FN [ Port ] [ 0 ] ) ) ;
retCode = RegSetValueEx ( hKey , " Message " , 0 , REG_SZ , ( BYTE * ) & Message [ Port ] [ 0 ] , strlen ( & Message [ Port ] [ 0 ] ) ) ;
retCode = RegSetValueEx ( hKey , " Interval " , 0 , REG_DWORD , ( BYTE * ) & Interval [ Port ] , 4 ) ;
retCode = RegSetValueEx ( hKey , " SendFromFile " , 0 , REG_DWORD , ( BYTE * ) & SendFromFile [ Port ] , 4 ) ;
retCode = RegSetValueEx ( hKey , " Enabled " , 0 , REG_DWORD , ( BYTE * ) & UIEnabled [ Port ] , 4 ) ;
retCode = RegSetValueEx ( hKey , " Digis " , 0 , REG_SZ , Digis , strlen ( Digis ) ) ;
RegCloseKey ( hKey ) ;
}
SetupUI ( Port ) ;
return ( INT_PTR ) TRUE ;
case IDCANCEL :
EndDialog ( hDlg , LOWORD ( wParam ) ) ;
return ( INT_PTR ) TRUE ;
case ID_TEST :
SendBeacon ( Port ) ;
return TRUE ;
}
break ;
}
return ( INT_PTR ) FALSE ;
}
VOID WINAPI OnTabbedDialogInit ( HWND hDlg )
{
DLGHDR * pHdr = ( DLGHDR * ) LocalAlloc ( LPTR , sizeof ( DLGHDR ) ) ;
DWORD dwDlgBase = GetDialogBaseUnits ( ) ;
int cxMargin = LOWORD ( dwDlgBase ) / 4 ;
int cyMargin = HIWORD ( dwDlgBase ) / 8 ;
TC_ITEM tie ;
RECT rcTab ;
int i , pos , tab = 0 ;
INITCOMMONCONTROLSEX init ;
char PortNo [ 60 ] ;
struct _EXTPORTDATA * PORTVEC ;
hwndDlg = hDlg ; // Save Window Handle
// Save a pointer to the DLGHDR structure.
SetWindowLong ( hwndDlg , GWL_USERDATA , ( LONG ) pHdr ) ;
// Create the tab control.
init . dwICC = ICC_STANDARD_CLASSES ;
init . dwSize = sizeof ( init ) ;
i = InitCommonControlsEx ( & init ) ;
pHdr - > hwndTab = CreateWindow ( WC_TABCONTROL , " " , WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE ,
0 , 0 , 100 , 100 , hwndDlg , NULL , hInst , NULL ) ;
if ( pHdr - > hwndTab = = NULL ) {
// handle error
}
// Add a tab for each of the child dialog boxes.
tie . mask = TCIF_TEXT | TCIF_IMAGE ;
tie . iImage = - 1 ;
for ( i = 1 ; i < = GetNumberofPorts ( ) ; i + + )
{
// Only allow UI on ax.25 ports
PORTVEC = ( struct _EXTPORTDATA * ) GetPortTableEntry ( i ) ;
if ( PORTVEC - > PORTCONTROL . PORTTYPE = = 16 ) // EXTERNAL
if ( PORTVEC - > PORTCONTROL . PROTOCOL = = 10 ) // Pactor/WINMOR
continue ;
sprintf ( PortNo , " Port %2d " , GetPortNumber ( i ) ) ;
PortNum [ tab ] = GetPortNumber ( i ) ;
tie . pszText = PortNo ;
TabCtrl_InsertItem ( pHdr - > hwndTab , tab , & tie ) ;
pHdr - > apRes [ tab + + ] = DoLockDlgRes ( " PORTPAGE " ) ;
}
PageCount = tab ;
// Determine the bounding rectangle for all child dialog boxes.
SetRectEmpty ( & rcTab ) ;
for ( i = 0 ; i < PageCount ; i + + )
{
if ( pHdr - > apRes [ i ] - > cx > rcTab . right )
rcTab . right = pHdr - > apRes [ i ] - > cx ;
if ( pHdr - > apRes [ i ] - > cy > rcTab . bottom )
rcTab . bottom = pHdr - > apRes [ i ] - > cy ;
}
MapDialogRect ( hwndDlg , & rcTab ) ;
// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4;
// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8;
// Calculate how large to make the tab control, so
// the display area can accomodate all the child dialog boxes.
TabCtrl_AdjustRect ( pHdr - > hwndTab , TRUE , & rcTab ) ;
OffsetRect ( & rcTab , cxMargin - rcTab . left , cyMargin - rcTab . top ) ;
// Calculate the display rectangle.
CopyRect ( & pHdr - > rcDisplay , & rcTab ) ;
TabCtrl_AdjustRect ( pHdr - > hwndTab , FALSE , & pHdr - > rcDisplay ) ;
// Set the size and position of the tab control, buttons,
// and dialog box.
SetWindowPos ( pHdr - > hwndTab , NULL , rcTab . left , rcTab . top , rcTab . right - rcTab . left , rcTab . bottom - rcTab . top , SWP_NOZORDER ) ;
// Move the Buttons to bottom of page
pos = rcTab . left + cxMargin ;
// Size the dialog box.
SetWindowPos ( hwndDlg , NULL , 0 , 0 , rcTab . right + cyMargin + 2 * GetSystemMetrics ( SM_CXDLGFRAME ) ,
rcTab . bottom + 2 * cyMargin + 2 * GetSystemMetrics ( SM_CYDLGFRAME ) + GetSystemMetrics ( SM_CYCAPTION ) ,
SWP_NOMOVE | SWP_NOZORDER ) ;
// Simulate selection of the first item.
OnSelChanged ( hwndDlg ) ;
}
// DoLockDlgRes - loads and locks a dialog template resource.
// Returns a pointer to the locked resource.
// lpszResName - name of the resource
DLGTEMPLATE * WINAPI DoLockDlgRes ( LPCSTR lpszResName )
{
HRSRC hrsrc = FindResource ( NULL , lpszResName , RT_DIALOG ) ;
HGLOBAL hglb = LoadResource ( hInst , hrsrc ) ;
return ( DLGTEMPLATE * ) LockResource ( hglb ) ;
}
//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page.
// OnSelChanged - processes the TCN_SELCHANGE notification.
// hwndDlg - handle of the parent dialog box
VOID WINAPI OnSelChanged ( HWND hwndDlg )
{
char PortDesc [ 40 ] ;
int Port ;
DLGHDR * pHdr = ( DLGHDR * ) GetWindowLong ( hwndDlg , GWL_USERDATA ) ;
CurrentPage = TabCtrl_GetCurSel ( pHdr - > hwndTab ) ;
// Destroy the current child dialog box, if any.
if ( pHdr - > hwndDisplay ! = NULL )
DestroyWindow ( pHdr - > hwndDisplay ) ;
// Create the new child dialog box.
pHdr - > hwndDisplay = CreateDialogIndirect ( hInst , pHdr - > apRes [ CurrentPage ] , hwndDlg , ChildDialogProc ) ;
hwndDisplay = pHdr - > hwndDisplay ; // Save
Port = PortNum [ CurrentPage ] ;
// Fill in the controls
GetPortDescription ( PortNum [ CurrentPage ] , PortDesc ) ;
SetDlgItemText ( hwndDisplay , IDC_PORTNAME , PortDesc ) ;
// CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]);
// SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE);
SetDlgItemText ( hwndDisplay , IDC_UIDEST , & UIDEST [ Port ] [ 0 ] ) ;
SetDlgItemText ( hwndDisplay , IDC_UIDIGIS , UIDigi [ Port ] ) ;
// SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]);
// SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]);
ShowWindow ( pHdr - > hwndDisplay , SW_SHOWNORMAL ) ;
}
//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area.
// OnChildDialogInit - Positions the child dialog box to fall
// within the display area of the tab control.
VOID WINAPI OnChildDialogInit ( HWND hwndDlg )
{
HWND hwndParent = GetParent ( hwndDlg ) ;
DLGHDR * pHdr = ( DLGHDR * ) GetWindowLong ( hwndParent , GWL_USERDATA ) ;
SetWindowPos ( hwndDlg , HWND_TOP , pHdr - > rcDisplay . left , pHdr - > rcDisplay . top , 0 , 0 , SWP_NOSIZE ) ;
}
*/
/*
VOID ProcessMessage ( char * Payload , struct STATIONRECORD * Station )
{
char MsgDest [ 10 ] ;
struct APRSMESSAGE * Message ;
struct APRSMESSAGE * ptr = Messages ;
char * TextPtr = & Payload [ 11 ] ;
char * SeqPtr ;
int n = 0 ;
char FromCall [ 10 ] = " " ;
struct tm * TM ;
time_t NOW ;
memcpy ( FromCall , Station - > Callsign , ( int ) strlen ( Station - > Callsign ) ) ;
memcpy ( MsgDest , & Payload [ 1 ] , 9 ) ;
MsgDest [ 9 ] = 0 ;
SeqPtr = strchr ( TextPtr , ' { ' ) ;
if ( SeqPtr )
{
* ( SeqPtr + + ) = 0 ;
if ( strlen ( SeqPtr ) > 6 )
SeqPtr [ 7 ] = 0 ;
}
if ( _memicmp ( TextPtr , " ack " , 3 ) = = 0 )
{
// Message Ack. See if for one of our messages
ptr = OutstandingMsgs ;
if ( ptr = = 0 )
return ;
do
{
if ( strcmp ( ptr - > FromCall , MsgDest ) = = 0
& & strcmp ( ptr - > ToCall , FromCall ) = = 0
& & strcmp ( ptr - > Seq , & TextPtr [ 3 ] ) = = 0 )
{
// Message is acked
ptr - > Retries = 0 ;
ptr - > Acked = TRUE ;
// if (hMsgsOut)
// UpdateTXMessageLine(hMsgsOut, n, ptr);
return ;
}
ptr = ptr - > Next ;
n + + ;
} while ( ptr ) ;
return ;
}
Message = malloc ( sizeof ( struct APRSMESSAGE ) ) ;
memset ( Message , 0 , sizeof ( struct APRSMESSAGE ) ) ;
strcpy ( Message - > FromCall , Station - > Callsign ) ;
strcpy ( Message - > ToCall , MsgDest ) ;
if ( SeqPtr )
{
strcpy ( Message - > Seq , SeqPtr ) ;
// If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message
if ( SeqPtr [ 2 ] = = ' } ' )
{
struct APRSMESSAGE * ptr1 ;
int nn = 0 ;
strcpy ( Station - > LastRXSeq , SeqPtr ) ;
ptr1 = OutstandingMsgs ;
while ( ptr1 )
{
if ( strcmp ( ptr1 - > FromCall , MsgDest ) = = 0
& & strcmp ( ptr1 - > ToCall , FromCall ) = = 0
& & memcmp ( & ptr1 - > Seq , & SeqPtr [ 3 ] , 2 ) = = 0 )
{
// Message is acked
ptr1 - > Acked = TRUE ;
ptr1 - > Retries = 0 ;
// if (hMsgsOut)
// UpdateTXMessageLine(hMsgsOut, nn, ptr);
break ;
}
ptr1 = ptr1 - > Next ;
nn + + ;
}
}
else
{
// Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanger
Station - > SimpleNumericSeq = TRUE ;
}
}
if ( strlen ( TextPtr ) > 100 )
TextPtr [ 100 ] = 0 ;
strcpy ( Message - > Text , TextPtr ) ;
NOW = time ( NULL ) ;
if ( DefaultLocalTime )
TM = localtime ( & NOW ) ;
else
TM = gmtime ( & NOW ) ;
sprintf ( Message - > Time , " %.2d:%.2d " , TM - > tm_hour , TM - > tm_min ) ;
if ( _stricmp ( MsgDest , APRSCall ) = = 0 & & SeqPtr ) // ack it if it has a sequence
{
// For us - send an Ack
char ack [ 30 ] ;
int n = sprintf ( ack , " :%-9s:ack%s " , Message - > FromCall , Message - > Seq ) ;
PutAPRSMessage ( ack , n ) ;
}
if ( ptr = = NULL )
{
Messages = Message ;
}
else
{
n + + ;
while ( ptr - > Next )
{
ptr = ptr - > Next ;
n + + ;
}
ptr - > Next = Message ;
}
if ( strcmp ( MsgDest , APRSCall ) = = 0 ) // to me?
{
}
}
*/
VOID APRSSecTimer ( )
{
// Check Message Retries
struct APRSMESSAGE * ptr = SMEM - > OutstandingMsgs ;
int n = 0 ;
if ( SendWX )
SendWeatherBeacon ( ) ;
while ( ptr )
{
if ( ptr - > Acked = = FALSE )
{
if ( ptr - > Retries )
{
ptr - > RetryTimer - - ;
if ( ptr - > RetryTimer = = 0 )
{
ptr - > Retries - - ;
if ( ptr - > Retries )
{
// Send Again
char Msg [ 255 ] ;
APRSHEARDRECORD * STN ;
sprintf ( Msg , " :%-9s:%s{%s " , ptr - > ToCall , ptr - > Text , ptr - > Seq ) ;
STN = FindStationInMH ( ptr - > ToCall ) ;
if ( STN )
SendAPRSMessage ( Msg , STN - > rfPort ) ;
else
{
if ( memcmp ( ptr - > ToCall , " SERVER " , 9 ) )
SendAPRSMessage ( Msg , - 1 ) ; // All RF ports unless to SERVER
SendAPRSMessage ( Msg , 0 ) ; // IS
}
ptr - > RetryTimer = RetryTimer ;
}
}
}
}
ptr = ptr - > Next ;
n + + ;
}
}
double radians ( double Degrees )
{
return M_PI * Degrees / 180 ;
}
double degrees ( double Radians )
{
return Radians * 180 / M_PI ;
}
double Distance ( double laa , double loa , double lah , double loh , BOOL KM )
{
/*
' Great Circle Calculations .
' dif = longitute home - longitute away
' ( this should be within - 180 to + 180 degrees )
' ( Hint : This number should be non - zero , programs should check for
' this and make dif = 0.0001 as a minimum )
' lah = latitude of home
' laa = latitude of away
' dis = ArcCOS ( Sin ( lah ) * Sin ( laa ) + Cos ( lah ) * Cos ( laa ) * Cos ( dif ) )
' distance = dis / 180 * pi * ERAD
' angle = ArcCOS ( ( Sin ( laa ) - Sin ( lah ) * Cos ( dis ) ) / ( Cos ( lah ) * Sin ( dis ) ) )
' p1 = 3.1415926535 : P2 = p1 / 180 : Rem - - PI , Deg = > = Radians
*/
loh = radians ( loh ) ; lah = radians ( lah ) ;
loa = radians ( loa ) ; laa = radians ( laa ) ;
loh = 60 * degrees ( acos ( sin ( lah ) * sin ( laa ) + cos ( lah ) * cos ( laa ) * cos ( loa - loh ) ) ) * 1.15077945 ;
if ( KM )
loh * = 1.60934 ;
return loh ;
}
double myDistance ( double laa , double loa , BOOL KM )
{
double lah , loh ;
GetAPRSLatLon ( & lah , & loh ) ;
return Distance ( laa , loa , lah , loh , KM ) ;
}
/*
' Great Circle Calculations .
' dif = longitute home - longitute away
' ( this should be within - 180 to + 180 degrees )
' ( Hint : This number should be non - zero , programs should check for
' this and make dif = 0.0001 as a minimum )
' lah = latitude of home
' laa = latitude of away
' dis = ArcCOS ( Sin ( lah ) * Sin ( laa ) + Cos ( lah ) * Cos ( laa ) * Cos ( dif ) )
' distance = dis / 180 * pi * ERAD
' angle = ArcCOS ( ( Sin ( laa ) - Sin ( lah ) * Cos ( dis ) ) / ( Cos ( lah ) * Sin ( dis ) ) )
' p1 = 3.1415926535 : P2 = p1 / 180 : Rem - - PI , Deg = > = Radians
loh = radians ( loh ) ; lah = radians ( lah ) ;
loa = radians ( loa ) ; laa = radians ( laa ) ;
loh = 60 * degrees ( acos ( sin ( lah ) * sin ( laa ) + cos ( lah ) * cos ( laa ) * cos ( loa - loh ) ) ) * 1.15077945 ;
if ( KM )
loh * = 1.60934 ;
return loh ;
}
*/
double Bearing ( double lat2 , double lon2 , double lat1 , double lon1 )
{
double dlat , dlon , TC1 ;
lat1 = radians ( lat1 ) ;
lat2 = radians ( lat2 ) ;
lon1 = radians ( lon1 ) ;
lon2 = radians ( lon2 ) ;
dlat = lat2 - lat1 ;
dlon = lon2 - lon1 ;
if ( dlat = = 0 | | dlon = = 0 ) return 0 ;
TC1 = atan ( ( sin ( lon1 - lon2 ) * cos ( lat2 ) ) / ( cos ( lat1 ) * sin ( lat2 ) - sin ( lat1 ) * cos ( lat2 ) * cos ( lon1 - lon2 ) ) ) ;
TC1 = degrees ( TC1 ) ;
if ( fabs ( TC1 ) > 89.5 ) if ( dlon > 0 ) return 90 ; else return 270 ;
if ( dlat > 0 )
{
if ( dlon > 0 ) return - TC1 ;
if ( dlon < 0 ) return 360 - TC1 ;
return 0 ;
}
if ( dlat < 0 )
{
if ( dlon > 0 ) return TC1 = 180 - TC1 ;
if ( dlon < 0 ) return TC1 = 180 - TC1 ; // 'ok?
return 180 ;
}
return 0 ;
}
double myBearing ( double lat2 , double lon2 )
{
double lat1 , lon1 ;
GetAPRSLatLon ( & lat1 , & lon1 ) ;
return Bearing ( lat2 , lon2 , lat1 , lon1 ) ;
}
/*
lat1 = radians ( lat1 ) ;
lat2 = radians ( lat2 ) ;
lon1 = radians ( lon1 ) ;
lon2 = radians ( lon2 ) ;
dlat = lat2 - lat1 ;
dlon = lon2 - lon1 ;
if ( dlat = = 0 | | dlon = = 0 ) return 0 ;
TC1 = atan ( ( sin ( lon1 - lon2 ) * cos ( lat2 ) ) / ( cos ( lat1 ) * sin ( lat2 ) - sin ( lat1 ) * cos ( lat2 ) * cos ( lon1 - lon2 ) ) ) ;
TC1 = degrees ( TC1 ) ;
if ( fabs ( TC1 ) > 89.5 ) if ( dlon > 0 ) return 90 ; else return 270 ;
if ( dlat > 0 )
{
if ( dlon > 0 ) return - TC1 ;
if ( dlon < 0 ) return 360 - TC1 ;
return 0 ;
}
if ( dlat < 0 )
{
if ( dlon > 0 ) return TC1 = 180 - TC1 ;
if ( dlon < 0 ) return TC1 = 180 - TC1 ; // 'ok?
return 180 ;
}
return 0 ;
}
*/
// Weather Data
static char * month [ ] = { " Jan " , " Feb " , " Mar " , " Apr " , " May " , " Jun " , " Jul " , " Aug " , " Sep " , " Oct " , " Nov " , " Dec " } ;
VOID SendWeatherBeacon ( )
{
char Msg [ 256 ] ;
char DD [ 3 ] = " " ;
char HH [ 3 ] = " " ;
char MM [ 3 ] = " " ;
char Lat [ 10 ] , Lon [ 10 ] ;
size_t Len ;
int index ;
char WXMessage [ 1024 ] ;
char * WXptr ;
char * WXend ;
time_t WXTime ;
time_t now = time ( NULL ) ;
FILE * hFile ;
struct tm * TM ;
struct stat STAT ;
WXCounter + + ;
if ( WXCounter < WXInterval * 60 )
return ;
WXCounter = 0 ;
// Debugprintf("BPQAPRS - Trying to open WX file %s", WXFileName);
if ( stat ( WXFileName , & STAT ) )
{
Debugprintf ( " APRS WX File %s stat() failed %d " , WXFileName , GetLastError ( ) ) ;
return ;
}
WXTime = ( now - STAT . st_mtime ) / 60 ; // Minutes
if ( WXTime > ( 3 * WXInterval ) )
{
Debugprintf ( " APRS Send WX File %s too old - %d minutes " , WXFileName , WXTime ) ;
return ;
}
hFile = fopen ( WXFileName , " rb " ) ;
if ( hFile )
Len = fread ( WXMessage , 1 , 1024 , hFile ) ;
else
{
Debugprintf ( " APRS WX File %s open() failed %d " , WXFileName , GetLastError ( ) ) ;
return ;
}
if ( Len < 30 )
{
Debugprintf ( " BPQAPRS - WX file %s is too short - %d Chars " , WXFileName , Len ) ;
fclose ( hFile ) ;
return ;
}
WXMessage [ Len ] = 0 ;
// see if wview format
//04-09-13, 2245
//TempIn 23
//TempEx 18
//WindHi 0
//WindAv 0
//WindDr 200
//BarmPs 30167
//HumdIn 56
//HumdEx 100
//RnFall 0.00
//DailyRnFall 0.00
if ( strstr ( WXMessage , " TempIn " ) )
{
int Wind = 0 ;
int Gust = 0 ;
int Temp = 0 ;
int Winddir = 0 ;
int Humidity = 0 ;
int Raintoday = 0 ;
int Rain24hrs = 0 ;
int Pressure = 0 ;
char * ptr ;
ptr = strstr ( WXMessage , " TempEx " ) ;
if ( ptr )
Temp = ( int ) ( atof ( ptr + 7 ) * 1.8 ) + 32 ;
ptr = strstr ( WXMessage , " WindHi " ) ;
if ( ptr )
Gust = atoi ( ptr + 7 ) ;
ptr = strstr ( WXMessage , " WindAv " ) ;
if ( ptr )
Wind = atoi ( ptr + 7 ) ;
ptr = strstr ( WXMessage , " WindDr " ) ;
if ( ptr )
Winddir = atoi ( ptr + 7 ) ;
ptr = strstr ( WXMessage , " BarmPs " ) ;
if ( ptr )
Pressure = ( int ) ( atof ( ptr + 7 ) * 0.338638866667 ) ; // Inches to 1/10 millbars
ptr = strstr ( WXMessage , " HumdEx " ) ;
if ( ptr )
Humidity = atoi ( ptr + 7 ) ;
ptr = strstr ( WXMessage , " RnFall " ) ;
if ( ptr )
Rain24hrs = ( int ) ( atof ( ptr + 7 ) * 100.0 ) ;
ptr = strstr ( WXMessage , " DailyRnFall " ) ;
if ( ptr )
Raintoday = ( int ) ( atof ( ptr + 12 ) * 100.0 ) ;
if ( Humidity > 99 )
Humidity = 99 ;
sprintf ( WXMessage , " %03d/%03dg%03dt%03dr%03dP%03dp%03dh%02db%05d " ,
Winddir , Wind , Gust , Temp , 0 , Raintoday , Rain24hrs , Humidity , Pressure ) ;
}
WXptr = strchr ( WXMessage , 10 ) ;
if ( WXptr )
{
WXend = strchr ( + + WXptr , 13 ) ;
if ( WXend = = 0 )
WXend = strchr ( WXptr , 10 ) ;
if ( WXend )
* WXend = 0 ;
}
else
WXptr = & WXMessage [ 0 ] ;
// Get DDHHMM from Filetime
TM = gmtime ( & STAT . st_mtime ) ;
sprintf ( DD , " %02d " , TM - > tm_mday ) ;
sprintf ( HH , " %02d " , TM - > tm_hour ) ;
sprintf ( MM , " %02d " , TM - > tm_min ) ;
GetAPRSLatLonString ( Lat , Lon ) ;
Len = sprintf ( Msg , " @%s%s%sz%s/%s_%s%s " , DD , HH , MM , Lat , Lon , WXptr , WXComment ) ;
Debugprintf ( Msg ) ;
for ( index = 0 ; index < 32 ; index + + )
{
if ( WXPort [ index ] )
SendAPRSMessageEx ( Msg , index , WXCall , FALSE ) ;
}
fclose ( hFile ) ;
}
/*
Jan 22 2012 14 : 10
123 / 005 g011t031r000P000p000h00b10161
/ MITWXN Mitchell IN weather Station N9LYA - 3 { UIV32 }
< previous
@ 221452 z3844 .42 N / 08628.33 W_203 / 006 g007t032r000P000p000h00b10171
Complete Weather Report Format <EFBFBD> with Lat / Long position , no Timestamp
! or = Lat Sym Table ID Long Symbol Code _ Wind Directn / Speed Weather Data APRS Software WX Unit uuuu
1 8 1 9 1 7 n 1 2 - 4
Examples
! 4903.50 N / 07201.75 W_220 / 004 g005t077r000p000P000h50b09900wRSW
! 4903.50 N / 07201.75 W_220 / 004 g005t077r000p000P000h50b . . . . . wRSW
*/
// Web Server Code
// The actual HTTP socket code is in bpq32.dll. Any requests for APRS data are passed in
// using a Named Pipe. The request looks exactly like one from a local socket, and the respone is
// a fully pormatted HTTP packet
# define InputBufferLen 1000
# define MaxSessions 100
HANDLE PipeHandle ;
int HTTPPort = 80 ;
BOOL IPV6 = TRUE ;
# define MAX_PENDING_CONNECTS 5
BOOL OpenSockets6 ( ) ;
char HTDocs [ MAX_PATH ] = " HTML " ;
char SpecialDocs [ MAX_PATH ] = " Special Pages " ;
char SymbolText [ 192 ] [ 20 ] = {
" Police Stn " , " No Symbol " , " Digi " , " Phone " , " DX Cluster " , " HF Gateway " , " Plane sm " , " Mob Sat Stn " ,
" WheelChair " , " Snowmobile " , " Red Cross " , " Boy Scout " , " Home " , " X " , " Red Dot " , " Circle (0) " ,
" Circle (1) " , " Circle (2) " , " Circle (3) " , " Circle (4) " , " Circle (5) " , " Circle (6) " , " Circle (7) " , " Circle (8) " ,
" Circle (9) " , " Fire " , " Campground " , " Motorcycle " , " Rail Eng. " , " Car " , " File svr " , " HC Future " ,
" Aid Stn " , " BBS " , " Canoe " , " No Symbol " , " Eyeball " , " Tractor " , " Grid Squ. " , " Hotel " ,
" Tcp/ip " , " No Symbol " , " School " , " Usr Log-ON " , " MacAPRS " , " NTS Stn " , " Balloon " , " Police " ,
" TBD " , " Rec Veh'le " , " Shuttle " , " SSTV " , " Bus " , " ATV " , " WX Service " , " Helo " ,
" Yacht " , " WinAPRS " , " Jogger " , " Triangle " , " PBBS " , " Plane lrge " , " WX Station " , " Dish Ant. " ,
" Ambulance " , " Bike " , " ICP " , " Fire Station " , " Horse " , " Fire Truck " , " Glider " , " Hospital " ,
" IOTA " , " Jeep " , " Truck " , " Laptop " , " Mic-E Rptr " , " Node " , " EOC " , " Rover " ,
" Grid squ. " , " Antenna " , " Power Boat " , " Truck Stop " , " Truck 18wh " , " Van " , " Water Stn " , " XAPRS " ,
" Yagi " , " Shelter " , " No Symbol " , " No Symbol " , " No Symbol " , " No Symbol " , " " , " " ,
" Emergency " , " No Symbol " , " No. Digi " , " Bank " , " No Symbol " , " No. Diam'd " , " Crash site " , " Cloudy " ,
" MEO " , " Snow " , " Church " , " Girl Scout " , " Home (HF) " , " UnknownPos " , " Destination " , " No. Circle " ,
" No Symbol " , " No Symbol " , " No Symbol " , " No Symbol " , " No Symbol " , " No Symbol " , " No Symbol " , " No Symbol " ,
" Petrol Stn " , " Hail " , " Park " , " Gale Fl " , " No Symbol " , " No. Car " , " Info Kiosk " , " Hurricane " ,
" No. Box " , " Snow blwng " , " Coast G'rd " , " Drizzle " , " Smoke " , " Fr'ze Rain " , " Snow Shwr " , " Haze " ,
" Rain Shwr " , " Lightning " , " Kenwood " , " Lighthouse " , " No Symbol " , " Nav Buoy " , " Rocket " , " Parking " ,
" Quake " , " Restaurant " , " Sat/Pacsat " , " T'storm " , " Sunny " , " VORTAC " , " No. WXS " , " Pharmacy " ,
" No Symbol " , " No Symbol " , " Wall Cloud " , " No Symbol " , " No Symbol " , " No. Plane " , " No. WX Stn " , " Rain " ,
" No. Diamond " , " Dust blwng " , " No. CivDef " , " DX Spot " , " Sleet " , " Funnel Cld " , " Gale " , " HAM store " ,
" No. Blk Box " , " WorkZone " , " SUV " , " Area Locns " , " Milepost " , " No. Triang " , " Circle sm " , " Part Cloud " ,
" No Symbol " , " Restrooms " , " No. Boat " , " Tornado " , " No. Truck " , " No. Van " , " Flooding " , " No Symbol " ,
" Sky Warn " , " No Symbol " , " Fog " , " No Symbol " , " No Symbol " , " No Symbol " , " " , " " } ;
// All Calls (8 per line)
//<td><a href="find.cgi?call=EI7IG-1">EI7IG-1</a></td>
//<td><a href="find.cgi?call=G7TKK-1">G7TKK-1</a></td>
//<td><a href="find.cgi?call=GB7GL-B">GB7GL-B</a></td>
//<td><a href="find.cgi?call=GM1TCN">GM1TCN</a></td>
//<td><a href="find.cgi?call=GM8BPQ">GM8BPQ</a></td>
//<td><a href="find.cgi?call=GM8BPQ-14">GM8BPQ-14</a></td>
//<td><a href="find.cgi?call=LA2VPA-9">LA2VPA-9</a></td>
//<td><a href="find.cgi?call=LA3FIA-10">LA3FIA-10</a></td></tr><tr>
//<td><a href="find.cgi?call=LA6JF-2">LA6JF-2</a></td><td><a href="find.cgi?call=LD4ST">LD4ST</a></td><td><a href="find.cgi?call=M0CHK-7">M0CHK-7</a></td><td><a href="find.cgi?call=M0OZH-7">M0OZH-7</a></td><td><a href="find.cgi?call=MB7UFO-1">MB7UFO-1</a></td><td><a href="find.cgi?call=MB7UN">MB7UN</a></td><td><a href="find.cgi?call=MM0DXE-15">MM0DXE-15</a></td><td><a href="find.cgi?call=PA2AYX-9">PA2AYX-9</a></td></tr><tr>
//<td><a href="find.cgi?call=PA3AQW-5">PA3AQW-5</a></td><td><a href="find.cgi?call=PD1C">PD1C</a></td><td><a href="find.cgi?call=PD5LWD-2">PD5LWD-2</a></td><td><a href="find.cgi?call=PI1ECO">PI1ECO</a></td></tr>
char * DoSummaryLine ( struct STATIONRECORD * ptr , int n , int Width )
{
static char Line2 [ 80 ] ;
int x ;
char XCall [ 256 ] ;
char * ptr1 = ptr - > Callsign ;
char * ptr2 = XCall ;
// Object Names can contain spaces
while ( * ptr1 )
{
if ( * ptr1 = = ' ' )
{
memcpy ( ptr2 , " %20 " , 3 ) ;
ptr2 + = 3 ;
}
else
* ( ptr2 + + ) = * ptr1 ;
ptr1 + + ;
}
* ptr2 = 0 ;
// Object Names can contain spaces
sprintf ( Line2 , " <td><a href= " " find.cgi?call=%s " " >%s</a></td> " ,
XCall , ptr - > Callsign ) ;
x = + + n / Width ;
x = x * Width ;
if ( x = = n )
strcat ( Line2 , " </tr><tr> " ) ;
return Line2 ;
}
char * DoDetailLine ( struct STATIONRECORD * ptr , BOOL LocalTime , BOOL KM )
{
static char Line [ 512 ] ;
double Lat = ptr - > Lat ;
double Lon = ptr - > Lon ;
char NS = ' N ' , EW = ' E ' ;
char LatString [ 20 ] , LongString [ 20 ] , DistString [ 20 ] , BearingString [ 20 ] ;
int Degrees ;
double Minutes ;
char Time [ 80 ] ;
struct tm * TM ;
char XCall [ 256 ] ;
char * ptr1 = ptr - > Callsign ;
char * ptr2 = XCall ;
// Object Names can contain spaces
while ( * ptr1 )
{
if ( * ptr1 = = ' ' )
{
memcpy ( ptr2 , " %20 " , 3 ) ;
ptr2 + = 3 ;
}
else
* ( ptr2 + + ) = * ptr1 ;
ptr1 + + ;
}
* ptr2 = 0 ;
// if (ptr->ObjState == '_') // Killed Object
// return;
if ( LocalTime )
TM = localtime ( & ptr - > TimeLastUpdated ) ;
else
TM = gmtime ( & ptr - > TimeLastUpdated ) ;
sprintf ( Time , " %.2d:%.2d:%.2d " , TM - > tm_hour , TM - > tm_min , TM - > tm_sec ) ;
if ( ptr - > Lat < 0 )
{
NS = ' S ' ;
Lat = - Lat ;
}
if ( Lon < 0 )
{
EW = ' W ' ;
Lon = - Lon ;
}
# pragma warning(push)
# pragma warning(disable:4244)
Degrees = Lat ;
Minutes = Lat * 60.0 - ( 60 * Degrees ) ;
sprintf ( LatString , " %2d<32> %05.2f'%c " , Degrees , Minutes , NS ) ;
Degrees = Lon ;
# pragma warning(pop)
Minutes = Lon * 60 - 60 * Degrees ;
sprintf ( LongString , " %3d<33> %05.2f'%c " , Degrees , Minutes , EW ) ;
sprintf ( DistString , " %6.1f " , myDistance ( ptr - > Lat , ptr - > Lon , KM ) ) ;
sprintf ( BearingString , " %3.0f " , myBearing ( ptr - > Lat , ptr - > Lon ) ) ;
sprintf ( Line , " <tr><td align= " " left " " ><a href= " " find.cgi?call=%s " " > %s%s</a></td><td align= " " left " " >%s</td><td align= " " center " " >%s %s</td><td align= " " right " " >%s</td><td align= " " right " " >%s</td><td align= " " left " " >%s</td></tr> \r \n " ,
XCall , ptr - > Callsign ,
( strchr ( ptr - > Path , ' * ' ) ) ? " * " : " " , & SymbolText [ ptr - > iconRow < < 4 | ptr - > iconCol ] [ 0 ] , LatString , LongString , DistString , BearingString , Time ) ;
return Line ;
}
int CompareFN ( const void * a , const void * b )
{
const struct STATIONRECORD * x = a ;
const struct STATIONRECORD * y = b ;
x = x - > Next ;
y = y - > Next ;
return strcmp ( x - > Callsign , y - > Callsign ) ;
/* strcmp functions works exactly as expected from
comparison function */
}
char * CreateStationList ( BOOL RFOnly , BOOL WX , BOOL Mobile , char Objects , int * Count , char * Param , BOOL KM )
{
char * Line = malloc ( 100000 ) ;
struct STATIONRECORD * ptr = * StationRecords ;
int n = 0 , i ;
struct STATIONRECORD * List [ 1000 ] ;
int TableWidth = 8 ;
BOOL LocalTime = DefaultLocalTime ;
if ( strstr ( Param , " time=local " ) )
LocalTime = TRUE ;
else if ( strstr ( Param , " time=utc " ) )
LocalTime = FALSE ;
Line [ 0 ] = 0 ;
if ( Param & & Param [ 0 ] )
{
char * Key , * Context ;
Key = strtok_s ( Param , " = " , & Context ) ;
if ( _stricmp ( Key , " width " ) = = 0 )
TableWidth = atoi ( Context ) ;
if ( TableWidth = = 0 )
TableWidth = 8 ;
}
// Build list of calls
while ( ptr )
{
if ( ptr - > ObjState = = Objects & & ptr - > Lat ! = 0.0 & & ptr - > Lon ! = 0.0 )
{
if ( ( WX & & ( ptr - > LastWXPacket [ 0 ] = = 0 ) ) | | ( RFOnly & & ( ptr - > LastPort = = 0 ) ) | |
( Mobile & & ( ( ptr - > Speed < 0.1 ) | | ptr - > LastWXPacket [ 0 ] ! = 0 ) ) )
{
ptr = ptr - > Next ;
continue ;
}
List [ n + + ] = ptr ;
if ( n > 999 )
break ;
}
ptr = ptr - > Next ;
}
if ( n > 1 )
qsort ( List , n , sizeof ( void * ) , CompareFN ) ;
for ( i = 0 ; i < n ; i + + )
{
if ( RFOnly )
strcat ( Line , DoDetailLine ( List [ i ] , LocalTime , KM ) ) ;
else
strcat ( Line , DoSummaryLine ( List [ i ] , i , TableWidth ) ) ;
}
* Count = n ;
return Line ;
}
char * APRSLookupKey ( struct APRSConnectionInfo * sockptr , char * Key , BOOL KM )
{
struct STATIONRECORD * stn = sockptr - > SelCall ;
if ( strcmp ( Key , " ##MY_CALLSIGN## " ) = = 0 )
return _strdup ( LoppedAPRSCall ) ;
if ( strcmp ( Key , " ##CALLSIGN## " ) = = 0 )
return _strdup ( sockptr - > Callsign ) ;
if ( strcmp ( Key , " ##CALLSIGN_NOSSID## " ) = = 0 )
{
char * Call = _strdup ( sockptr - > Callsign ) ;
char * ptr = strchr ( Call , ' - ' ) ;
if ( ptr )
* ptr = 0 ;
return Call ;
}
if ( strcmp ( Key , " ##MY_WX_CALLSIGN## " ) = = 0 )
return _strdup ( LoppedAPRSCall ) ;
if ( strcmp ( Key , " ##MY_BEACON_COMMENT## " ) = = 0 )
return _strdup ( StatusMsg ) ;
if ( strcmp ( Key , " ##MY_WX_BEACON_COMMENT## " ) = = 0 )
return _strdup ( WXComment ) ;
if ( strcmp ( Key , " ##MILES_KM## " ) = = 0 )
if ( KM )
return _strdup ( " KM " ) ;
else
return _strdup ( " Miles " ) ;
if ( strcmp ( Key , " ##EXPIRE_TIME## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %d " , ExpireTime ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##LOCATION## " ) = = 0 )
{
char val [ 80 ] ;
double Lat = sockptr - > SelCall - > Lat ;
double Lon = sockptr - > SelCall - > Lon ;
char NS = ' N ' , EW = ' E ' ;
char LatString [ 20 ] ;
int Degrees ;
double Minutes ;
if ( Lat < 0 )
{
NS = ' S ' ;
Lat = - Lat ;
}
if ( Lon < 0 )
{
EW = ' W ' ;
Lon = - Lon ;
}
# pragma warning(push)
# pragma warning(disable:4244)
Degrees = Lat ;
Minutes = Lat * 60.0 - ( 60 * Degrees ) ;
sprintf ( LatString , " %2d<32> %05.2f'%c " , Degrees , Minutes , NS ) ;
Degrees = Lon ;
# pragma warning(pop)
Minutes = Lon * 60 - 60 * Degrees ;
sprintf ( val , " %s %3d<33> %05.2f'%c " , LatString , Degrees , Minutes , EW ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##LOCDDMMSS## " ) = = 0 )
{
char val [ 80 ] ;
double Lat = sockptr - > SelCall - > Lat ;
double Lon = sockptr - > SelCall - > Lon ;
char NS = ' N ' , EW = ' E ' ;
char LatString [ 20 ] ;
int Degrees ;
double Minutes ;
// 48.45.18N, 002.18.37E
if ( Lat < 0 )
{
NS = ' S ' ;
Lat = - Lat ;
}
if ( Lon < 0 )
{
EW = ' W ' ;
Lon = - Lon ;
}
# pragma warning(push)
# pragma warning(disable:4244)
Degrees = Lat ;
Minutes = Lat * 60.0 - ( 60 * Degrees ) ;
// IntMins = Minutes;
// Seconds = Minutes * 60.0 - (60 * IntMins);
sprintf ( LatString , " %2d.%05.2f%c " , Degrees , Minutes , NS ) ;
Degrees = Lon ;
Minutes = Lon * 60.0 - 60 * Degrees ;
// IntMins = Minutes;
// Seconds = Minutes * 60.0 - (60 * IntMins);
# pragma warning(pop)
sprintf ( val , " %s, %03d.%05.2f%c " , LatString , Degrees , Minutes , EW ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##LATLON## " ) = = 0 )
{
char val [ 80 ] ;
double Lat = sockptr - > SelCall - > Lat ;
double Lon = sockptr - > SelCall - > Lon ;
char NS = ' N ' , EW = ' E ' ;
// 58.5, -6.2
sprintf ( val , " %f, %f " , Lat , Lon ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##STATUS_TEXT## " ) = = 0 )
return _strdup ( stn - > Status ) ;
if ( strcmp ( Key , " ##LASTPACKET## " ) = = 0 )
return _strdup ( stn - > LastPacket ) ;
if ( strcmp ( Key , " ##LAST_HEARD## " ) = = 0 )
{
char Time [ 80 ] ;
struct tm * TM ;
time_t Age = time ( NULL ) - stn - > TimeLastUpdated ;
TM = gmtime ( & Age ) ;
sprintf ( Time , " %.2d:%.2d:%.2d " , TM - > tm_hour , TM - > tm_min , TM - > tm_sec ) ;
return _strdup ( Time ) ;
}
if ( strcmp ( Key , " ##FRAME_HEADER## " ) = = 0 )
return _strdup ( stn - > Path ) ;
if ( strcmp ( Key , " ##FRAME_INFO## " ) = = 0 )
return _strdup ( stn - > LastWXPacket ) ;
if ( strcmp ( Key , " ##BEARING## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %03.0f " , myBearing ( sockptr - > SelCall - > Lat , sockptr - > SelCall - > Lon ) ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##COURSE## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %03.0f " , stn - > Course ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##SPEED_MPH## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %5.1f " , stn - > Speed ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##DISTANCE## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %5.1f " , myDistance ( sockptr - > SelCall - > Lat , sockptr - > SelCall - > Lon , KM ) ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##WIND_DIRECTION## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %03d " , sockptr - > WindDirn ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##WIND_SPEED_MPH## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %d " , sockptr - > WindSpeed ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##WIND_GUST_MPH## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %d " , sockptr - > WindGust ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##TEMPERATURE_F## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %d " , sockptr - > Temp ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##HUMIDITY## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %d " , sockptr - > Humidity ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##PRESSURE_HPA## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %05.1f " , sockptr - > Pressure / 10.0 ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##RAIN_TODAY_IN## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %5.2f " , sockptr - > RainToday / 100.0 ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##RAIN_24_IN## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %5.2f " , sockptr - > RainLastDay / 100.0 ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##RAIN_HOUR_IN## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %5.2f " , sockptr - > RainLastHour / 100.0 ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##MAP_LAT_LON## " ) = = 0 )
{
char val [ 256 ] ;
sprintf ( val , " %f,%f " , stn - > Lat , stn - > Lon ) ;
return _strdup ( val ) ;
}
if ( strcmp ( Key , " ##SYMBOL_DESCRIPTION## " ) = = 0 )
return _strdup ( & SymbolText [ stn - > iconRow < < 4 | stn - > iconCol ] [ 0 ] ) ;
/*
# #WIND_SPEED_MS## - wind speed metres / sec
# #WIND_SPEED_KMH## - wind speed km / hour
# #WIND_GUST_MPH## - wind gust miles / hour
# #WIND_GUST_MS## - wind gust metres / sec
# #WIND_GUST_KMH## - wind gust km / hour
# #WIND_CHILL_F## - wind chill F
# #WIND_CHILL_C## - wind chill C
# #TEMPERATURE_C## - temperature C
# #DEWPOINT_F## - dew point temperature F
# #DEWPOINT_C## - dew point temperature C
# #PRESSURE_IN## - pressure inches of mercury
# #PRESSURE_HPA## - pressure hPa (mb)
# #RAIN_HOUR_MM## - rain in last hour mm
# #RAIN_TODAY_MM## - rain today mm
# #RAIN_24_MM## - rain in last 24 hours mm
# #FRAME_HEADER## - frame header of the last posit heard from the station
# #FRAME_INFO## - information field of the last posit heard from the station
# #MAP_LARGE_SCALE##" - URL of a suitable large scale map on www.vicinity.com
# #MEDIUM_LARGE_SCALE##" - URL of a suitable medium scale map on www.vicinity.com
# #MAP_SMALL_SCALE##" - URL of a suitable small scale map on www.vicinity.com
# #MY_LOCATION## - 'Latitude', 'Longitude' in 'Station Setup'
# #MY_STATUS_TEXT## - status text configured in 'Status Text'
# #MY_SYMBOL_DESCRIPTION## - 'Symbol' that would be shown for our station in 'Station List'
# #HIT_COUNTER## - The number of times the page has been accessed
# #DOCUMENT_LAST_CHANGED## - The date / time the page was last edited
# #FRAME_HEADER## - frame header of the last posit heard from the station
# #FRAME_INFO## - information field of the last posit heard from the station
*/
return NULL ;
}
VOID APRSProcessSpecialPage ( struct APRSConnectionInfo * sockptr , char * Buffer , int FileSize , char * StationTable , int Count , BOOL WX , BOOL KM )
{
// replaces ##xxx### constructs with the requested data
char * NewMessage = malloc ( 250000 ) ;
char * ptr1 = Buffer , * ptr2 , * ptr3 , * ptr4 , * NewPtr = NewMessage ;
size_t PrevLen ;
size_t BytesLeft = FileSize ;
size_t NewFileSize = FileSize ;
char * StripPtr = ptr1 ;
int HeaderLen ;
char Header [ 256 ] ;
if ( WX & & sockptr - > SelCall & & sockptr - > SelCall - > LastWXPacket )
{
DecodeWXReport ( sockptr , sockptr - > SelCall - > LastWXPacket ) ;
}
// strip comments blocks
while ( ptr4 = strstr ( ptr1 , " <!-- " ) )
{
ptr2 = strstr ( ptr4 , " --> " ) ;
if ( ptr2 )
{
PrevLen = ( ptr4 - ptr1 ) ;
memcpy ( StripPtr , ptr1 , PrevLen ) ;
StripPtr + = PrevLen ;
ptr1 = ptr2 + 3 ;
BytesLeft = FileSize - ( ptr1 - Buffer ) ;
}
}
memcpy ( StripPtr , ptr1 , BytesLeft ) ;
StripPtr + = BytesLeft ;
BytesLeft = StripPtr - Buffer ;
FileSize = ( int ) BytesLeft ;
NewFileSize = FileSize ;
ptr1 = Buffer ;
ptr1 [ FileSize ] = 0 ;
loop :
ptr2 = strstr ( ptr1 , " ## " ) ;
if ( ptr2 )
{
PrevLen = ( ptr2 - ptr1 ) ; // Bytes before special text
ptr3 = strstr ( ptr2 + 2 , " ## " ) ;
if ( ptr3 )
{
char Key [ 80 ] = " " ;
size_t KeyLen ;
char * NewText ;
size_t NewTextLen ;
ptr3 + = 2 ;
KeyLen = ptr3 - ptr2 ;
if ( KeyLen < 80 )
memcpy ( Key , ptr2 , KeyLen ) ;
if ( strcmp ( Key , " ##STATION_TABLE## " ) = = 0 )
{
NewText = _strdup ( StationTable ) ;
}
else
{
if ( strcmp ( Key , " ##TABLE_COUNT## " ) = = 0 )
{
char val [ 80 ] ;
sprintf ( val , " %d " , Count ) ;
NewText = _strdup ( val ) ;
}
else
NewText = APRSLookupKey ( sockptr , Key , KM ) ;
}
if ( NewText )
{
NewTextLen = strlen ( NewText ) ;
NewFileSize = NewFileSize + NewTextLen - KeyLen ;
// NewMessage = realloc(NewMessage, NewFileSize);
memcpy ( NewPtr , ptr1 , PrevLen ) ;
NewPtr + = PrevLen ;
memcpy ( NewPtr , NewText , NewTextLen ) ;
NewPtr + = NewTextLen ;
free ( NewText ) ;
NewText = NULL ;
}
else
{
// Key not found, so just leave
memcpy ( NewPtr , ptr1 , PrevLen + KeyLen ) ;
NewPtr + = ( PrevLen + KeyLen ) ;
}
ptr1 = ptr3 ; // Continue scan from here
BytesLeft = Buffer + FileSize - ptr3 ;
}
else // Unmatched ##
{
memcpy ( NewPtr , ptr1 , PrevLen + 2 ) ;
NewPtr + = ( PrevLen + 2 ) ;
ptr1 = ptr2 + 2 ;
}
goto loop ;
}
// Copy Rest
memcpy ( NewPtr , ptr1 , BytesLeft ) ;
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-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 \n Content-Length: 16 \r \n \r \n Page 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 \n Content-Length: %d \r \n Content-Type: image \r \n \r \n " , FileSize ) ;
else
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-Type: text/html \r \n \r \n " , FileSize ) ;
send ( sockptr - > sock , Header , HeaderLen , 0 ) ;
Sent = send ( sockptr - > sock , MsgBytes , FileSize , 0 ) ;
// printf("Send %d %d\n", FileSize, Sent);
while ( Sent < FileSize )
{
FileSize - = Sent ;
MsgBytes + = Sent ;
Sent = send ( sockptr - > sock , MsgBytes , FileSize , 0 ) ;
// printf("Send %d %d\n", FileSize, Sent);
if ( Sent = = - 1 )
{
Sleep ( 10 ) ;
Sent = 0 ;
}
}
free ( SaveMsgBytes ) ;
}
char WebHeader [ ] = " <HTML><HEAD><meta http-equiv= \" expires \" content= \" -1 \" > "
" <meta http-equiv= \" pragma \" content= \" no-cache \" > "
" <TITLE>APRS Messaging</TITLE></HEAD> "
" <BODY alink= \" #008000 \" bgcolor= \" #F5F5DC \" link= \" #0000FF \" vlink= \" #000080 \" background=/background.jpg> "
" <table align=center border=2 cellpadding=2 cellspacing=2 bgcolor=white><tr> "
" <td align=center><a href=/aprs.html>APRS Map</a></td> "
" <td align=center><a href=/aprs/msgs>Received Messages</a></td> "
" <td align=center><a href=/aprs/txmsgs>Sent Messages</a></td> "
" <td align=center><a href=/aprs/msgs/entermsg>Send Message</a></td> "
" <td align=center><a href=/aprs/all.html>Station Pages</a></td> "
" <td align=center><a href=/Node/NodeMenu.html>Return to Node Pages</a></td> "
" </tr></table> "
" <center><h2>%s's Messages</h2><TABLE BORDER= \" 3 \" CELLSPACING= \" 2 \" CELLPADDING= \" 1 \" > "
" <tr><td>From</td><td>To</td><td>Seq</td><td>Time</td><td> </td><td>Message</td></tr> " ;
char WebTXHeader [ ] = " <HTML><HEAD><meta http-equiv= \" expires \" content= \" -1 \" > "
" <meta http-equiv= \" pragma \" content= \" no-cache \" > "
" <TITLE>APRS Messaging</TITLE></HEAD> "
" <BODY alink= \" #008000 \" bgcolor= \" #F5F5DC \" link= \" #0000FF \" vlink= \" #000080 \" background=/background.jpg> "
" <table align=center border=2 cellpadding=2 cellspacing=2 bgcolor=white><tr> "
" <td align=center><a href=/aprs.html>APRS Map</a></td> "
" <td align=center><a href=/aprs/msgs>Received Messages</a></td> "
" <td align=center><a href=/aprs/txmsgs>Sent Messages</a></td> "
" <td align=center><a href=/aprs/msgs/entermsg>Send Message</a></td> "
" <td align=center><a href=/aprs/all.html>Station Pages</a></td> "
" <td align=center><a href=/Node/NodeMenu.html>Return to Node Pages</a></td> "
" </tr></table> "
" <center><h2>Message Sent by %s</h2><TABLE BORDER= \" 3 \" CELLSPACING= \" 2 \" CELLPADDING= \" 1 \" > "
" <tr><td>To</td><td>Seq</td><td>Time</td><td>State</td><td>message</td></tr> " ;
char WebLine [ ] = " <tr bgcolor= \" #ffcccc \" ><td>%s </td><td> %s </td><td> %s </td><td> %s</td><td> "
" <a href= \" entermsg?tocall=%s&fromcall=%s \" >Reply</a></td><td> %s</td></tr> " ;
char WebTXLine [ ] = " <tr bgcolor= \" #ffcccc \" > "
" <td>%s </td><td> %s </td><td> %s </td><td> %s </td><td> %s</td></tr> " ;
char WebTrailer [ ] = " </table></BODY></HTML> " ;
char SendMsgPage [ ] = " <html><head><title>BPQ32 APRS Messaging</title></head><body background= \" /background.jpg \" > "
" <center><h2>APRS Message Input</h1> "
" <form method=post action=/APRS/Msgs/SendMsg> "
" <table align=center bgcolor=white> "
" <tr><td>To</td><td><input type=text name=call tabindex=1 size=10 maxlength=12 value= \" %s \" /></td></tr> "
" <tr><td>Message</td><td><input type=text name=message tabindex=2 size=80 maxlength=100 /></td></tr></table> "
" <p align=center><input type=submit value=Submit /><input type=submit value=Cancel name=Cancel /></form> " ;
char APRSIndexPage [ ] = " <html><head><title>BPQ32 Web Server APRS Pages</title></head> "
" <body background=/background.jpg><P align=center> "
" <h2 align=center>BPQ32 APRS Server</h2><P align=center> "
" <table border=2 cellpadding=2 cellspacing=2 bgcolor=white><tr> "
" <td align=center><a href=/aprs.html>APRS Map</a></td> "
" <td align=center><a href=/aprs/msgs>Received Messages</a></td> "
" <td align=center><a href=/aprs/txmsgs>Sent Messages</a></td> "
" <td align=center><a href=/aprs/msgs/entermsg>Send Message</a></td> "
" <td align=center><a href=/aprs/all.html>Station Pages</a></td> "
" <td align=center><a href=/Node/NodeMenu.html>Return to Node Pages</a></td> "
" </tr></table>%s</body></html> " ;
extern char Tail [ ] ;
VOID APRSProcessHTTPMessage ( SOCKET sock , char * MsgPtr , BOOL LOCAL , BOOL COOKIE )
{
int InputLen = 0 ;
int OutputLen = 0 ;
char * URL ;
char * ptr ;
struct APRSConnectionInfo CI ;
struct APRSConnectionInfo * sockptr = & CI ;
char Key [ 12 ] = " " ;
char OutBuffer [ 100000 ] ;
char Header [ 1024 ] ;
int HeaderLen = 0 ;
memset ( & CI , 0 , sizeof ( CI ) ) ;
sockptr - > sock = sock ;
if ( memcmp ( MsgPtr , " POST " , 3 ) = = 0 )
{
char * To ;
char * Msg = " " ;
URL = & MsgPtr [ 5 ] ;
ptr = strstr ( URL , " \r \n \r \n " ) ;
if ( ptr )
{
char * param , * context ;
UndoTransparency ( ptr ) ;
param = strtok_s ( ptr + 4 , " & " , & context ) ;
while ( param )
{
char * val = strlop ( param , ' = ' ) ;
if ( val )
{
if ( _stricmp ( param , " call " ) = = 0 )
To = _strupr ( val ) ;
else if ( _stricmp ( param , " message " ) = = 0 )
Msg = val ;
else if ( _stricmp ( param , " Cancel " ) = = 0 )
{
// Return APRS Index Page
OutputLen = sprintf ( OutBuffer , APRSIndexPage , " <br><br><br><br><h2 align=center>Message Cancelled</h2> " ) ;
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-Type: text/html \r \n \r \n " , OutputLen ) ;
send ( sockptr - > sock , Header , HeaderLen , 0 ) ;
send ( sockptr - > sock , OutBuffer , OutputLen , 0 ) ;
return ;
}
}
param = strtok_s ( NULL , " & " , & context ) ;
}
strlop ( To , ' ' ) ;
if ( strlen ( To ) < 2 )
{
OutputLen = sprintf ( OutBuffer , SendMsgPage , To ) ;
OutputLen + = sprintf ( & OutBuffer [ OutputLen ] , " <br><br><h2 align=center>Invalid Callsign</h2> " ) ;
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-Type: text/html \r \n \r \n " , OutputLen ) ;
send ( sockptr - > sock , Header , HeaderLen , 0 ) ;
send ( sockptr - > sock , OutBuffer , OutputLen , 0 ) ;
return ;
}
if ( Msg [ 0 ] = = 0 )
{
OutputLen = sprintf ( OutBuffer , SendMsgPage , To ) ;
OutputLen + = sprintf ( & OutBuffer [ OutputLen ] , " <br><br><h2 align=center>No Message</h2> " ) ;
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-Type: text/html \r \n \r \n " , OutputLen ) ;
send ( sockptr - > sock , Header , HeaderLen , 0 ) ;
send ( sockptr - > sock , OutBuffer , OutputLen , 0 ) ;
return ;
}
// Send APRS Messsage
if ( strlen ( Msg ) > 100 )
Msg [ 100 ] = 0 ;
InternalSendAPRSMessage ( Msg , To ) ;
OutputLen = sprintf ( OutBuffer , APRSIndexPage , " <br><br><br><br><h2 align=center>Message Sent</h2> " ) ;
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-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 \n Content-Length: %d \r \n Content-Type: text/html \r \n \r \n " , OutputLen ) ;
send ( sockptr - > sock , Header , HeaderLen , 0 ) ;
send ( sockptr - > sock , OutBuffer , OutputLen , 0 ) ;
return ;
}
if ( _memicmp ( URL , " /aprs/msgs/entermsg " , 19 ) = = 0 | | _memicmp ( URL , " /aprs/entermsg " , 14 ) = = 0 )
{
char * To = strchr ( URL , ' = ' ) ;
if ( LOCAL = = FALSE & & COOKIE = = FALSE )
{
// Send Not Authorized
OutputLen = sprintf ( OutBuffer , APRSIndexPage , " <br><B>Not authorized - please return to Node Menu and sign in</B> " ) ;
HeaderLen = sprintf ( Header , " HTTP/1.1 200 OK \r \n Content-Length: %d \r \n Content-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 \n Content-Length: %d \r \n Content-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 \n Content-Length: %d \r \n Content-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 \n Content-Length: %d \r \n Content-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
}
}
}
2022-11-18 17:03:39 +00:00
if ( SaveAPRSMsgs )
SaveAPRSMessage ( Message ) ;
2022-08-28 09:35:46 +01:00
ptr = SMEM - > Messages ;
if ( ptr = = NULL )
{
SMEM - > Messages = Message ;
}
else
{
n + + ;
while ( ptr - > Next )
{
ptr = ptr - > Next ;
n + + ;
}
ptr - > Next = Message ;
}
2022-11-18 17:03:39 +00:00
2022-08-28 09:35:46 +01:00
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 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 , " MSGS " , 5 ) = = 0 )
{
struct APRSMESSAGE * ptr = SMEM - > Messages ;
char Addrs [ 32 ] ;
Bufferptr = Cmdprintf ( Session , Bufferptr ,
" \r Time 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 ,
" \r Time 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 )
{
if ( ptr - > Lat ! = 0.0 & & ptr - > Lon ! = 0.0 )
{
if ( ptr - > Lat > S & & ptr - > Lat < N & & ptr - > Lon > W & & ptr - > Lon < E )
{
// See if we have the Icon - if not build
char IconKey [ 6 ] ;
struct iconCacheEntry * Icon = iconCache ;
sprintf ( IconKey , " %02X%02X " , ptr - > iconRow , ptr - > iconCol ) ;
if ( ptr - > IconOverlay )
IconKey [ 4 ] = ptr - > IconOverlay ;
else
IconKey [ 4 ] = ' @ ' ;
while ( Icon )
{
if ( strcmp ( Icon - > key , IconKey ) = = 0 ) // Have it
break ;
Icon = Icon - > Next ;
}
if ( Icon = = NULL )
createIcon ( IconKey , ptr - > iconRow , ptr - > iconCol , ptr - > IconOverlay ) ;
popup [ 0 ] = 0 ;
if ( ptr - > Approx )
{
sprintf ( Msg , " Approximate Position From Locator<br> " ) ;
strcat ( popup , Msg ) ;
}
ptr1 = doHTMLTransparency ( ptr - > Path ) ;
sprintf ( Msg , " %s<br> " , ptr1 ) ;
strcat ( popup , Msg ) ;
free ( ptr1 ) ;
ptr1 = doHTMLTransparency ( ptr - > LastPacket ) ;
sprintf ( Msg , " %s<br> " , ptr1 ) ;
strcat ( popup , Msg ) ;
free ( ptr1 ) ;
ptr1 = doHTMLTransparency ( ptr - > Status ) ;
sprintf ( Msg , " %s<br> " , ptr1 ) ;
strcat ( popup , Msg ) ;
free ( ptr1 ) ;
if ( LocalTime )
TM = localtime ( & ptr - > TimeLastUpdated ) ;
else
TM = gmtime ( & ptr - > TimeLastUpdated ) ;
sprintf ( Msg , " Last Heard: %.2d:%.2d:%.2d on Port %d<br> " ,
TM - > tm_hour , TM - > tm_min , TM - > tm_sec , ptr - > LastPort ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Distance %6.1f Bearing %3.0f Course %1.0f° Speed %3.1f<br> " ,
myDistance ( ptr - > Lat , ptr - > Lon , KM ) ,
myBearing ( ptr - > Lat , ptr - > Lon ) , ptr - > Course , ptr - > Speed ) ;
strcat ( popup , Msg ) ;
if ( ptr - > LastWXPacket [ 0 ] )
{
//display wx info
struct APRSConnectionInfo temp ;
memset ( & temp , 0 , sizeof ( temp ) ) ;
DecodeWXReport ( & temp , ptr - > LastWXPacket ) ;
sprintf ( Msg , " Wind Speed %d MPH<br> " , temp . WindSpeed ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Wind Gust %d MPH<br> " , temp . WindGust ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Wind Direction %d \xC2 \xB0 <br> " , temp . WindDirn ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Temperature %d \xC2 \xB0 F<br> " , temp . Temp ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Pressure %05.1f<br> " , temp . Pressure / 10.0 ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Humidity %d%%<br> " , temp . Humidity ) ;
strcat ( popup , Msg ) ;
sprintf ( Msg , " Rainfall Last Hour/Last 24H/Today %5.2f, %5.2f, %5.2f (inches) " ,
temp . RainLastHour / 100.0 , temp . RainLastDay / 100.0 , temp . RainToday / 100.0 ) ;
ptr1 = doHTMLTransparency ( Msg ) ;
sprintf ( Msg , " %s<br> " , ptr1 ) ;
strcat ( popup , Msg ) ;
free ( ptr1 ) ;
}
Len + = sprintf ( & Buffer [ Len ] , " A,%.4f,%.4f,%s,%s,%s,%d \r \n | " ,
ptr - > Lat , ptr - > Lon , ptr - > Callsign , popup , IconKey ,
NOW - ptr - > TimeLastUpdated ) ;
if ( ptr - > TrackTime [ 0 ] & & ptr - > TrackTime [ 1 ] ) // Have trackpoints
{
int n = ptr - > Trackptr ;
int i ;
double lastLat = 0 ;
// We read from next track point (oldest) for TRACKPOINT records, ignoring zeros
Len + = sprintf ( & Buffer [ Len ] , " T, " ) ;
for ( i = 0 ; i < TRACKPOINTS ; i + + )
{
if ( ptr - > TrackTime [ n ] )
{
Len + = sprintf ( & Buffer [ Len ] , " %.4f,%.4f, " , ptr - > LatTrack [ n ] , ptr - > LonTrack [ n ] ) ;
lastLat = ptr - > LatTrack [ n ] ;
}
n + + ;
if ( n = = TRACKPOINTS )
n = 0 ;
}
if ( lastLat ! = ptr - > Lat )
Len + = sprintf ( & Buffer [ Len ] , " %.4f,%.4f, \r \n | " , ptr - > Lat , ptr - > Lon ) ; //Add current position to end of track
else
Len + = sprintf ( & Buffer [ Len ] , " \r \n | " , ptr - > Lat , ptr - > Lon ) ;
}
}
}
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 ;
}
2022-11-18 17:03:39 +00:00
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 ) ;
}