#define WIN32_LEAN_AND_MEAN #define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #include #include #include #include "time.h" #include "CHeaders.h" //#include "tncinfo.h" //#include "adif.h" //#include "telnetserver.h" VOID __cdecl Debugprintf(const char * format, ...); #define TRACKPOINTS 50 typedef struct TARGETRECORD { UINT ID; char name[21]; char Callsign[8]; char Dest[21]; UINT IMO; double ROT; int LengthA; int LengthB; int WidthC; int WidthD; int Draft; double lat; double Long; double course; double speed; double Heading; int NavStatus; UINT TCA; UINT BCA; // Bearing at closest UINT TimeAdded; UINT TimeLastUpdated; char Type; char Class; // Fields for Intellegent TrackLog double LatTrack[TRACKPOINTS]; // Cyclic Tracklog double LonTrack[TRACKPOINTS]; time_t TrackTime[TRACKPOINTS]; int Trackptr; // Next record in Tracklog // info about last track point written double Lastlat; double LastLong; double LastCourse; double LastSpeed; time_t LastTime; int Alt; // For SAR Aircraft } TargetRecord; typedef struct NAVAIDRECORD { UINT ID; char name[21]; int NavAidType; double lat; double Long; int LengthA; int LengthB; int WidthC; int WidthD; int FixType; time_t TimeAdded; time_t TimeLastUpdated; } NavaidRecord; typedef struct BASESTATIONRECORD { UINT ID; int FixType; double lat; double Long; int NumberofStations; time_t TimeAdded; time_t TimeLastUpdated; } BaseStationRecord; typedef struct SHIPDATABASERECORD { UINT ID; char name[21]; char Callsign[8]; int LengthA; int LengthB; int WidthC; int WidthD; int Draft; time_t TimeAdded; time_t TimeLastUpdated; char Type; char Class; } ShipDatabaseRecord; void LookupVessel(UINT UserID); void LoadVesselDataBase(); void SaveVesselDataBase(); void LoadNavAidDataBase(); void SaveNavAidDataBase(); typedef struct SARRECORD { UINT ID; char name[21]; char Callsign[8]; char Dest[21]; UINT IMO; double ROT; int LengthA; int LengthB; int WidthC; int WidthD; int Draft; double lat; double Long; double course; double speed; double Heading; int NavStatus; UINT TCA; UINT BCA; // Bearing at closest time_t TimeAdded; time_t TimeLastUpdated; char Type; char Class; // Fields for Intellegent TrackLog double LatTrack[TRACKPOINTS]; // Cyclic Tracklog double LonTrack[TRACKPOINTS]; time_t TrackTime[TRACKPOINTS]; int Trackptr; // Next record in Tracklog // info about last track point written double Lastlat; double LastLong; double LastCourse; double LastSpeed; time_t LastTime; int Alt; // For SAR Aircraft } SARRecord; typedef struct ADSBRECORD { char hex[8]; //the 24-bit ICAO identifier of the aircraft, as 6 hex digits. The identifier may //start with '~', this means that the address is a non-ICAO address (e.g. from TIS-B). char squawk[5]; // the 4-digit squawk (octal representation) char flight[32]; // the flight name / callsign double lat; double lon; // the aircraft position in decimal degrees // nucp: the NUCp (navigational uncertainty category) reported for the position int seen_pos; // how long ago (in seconds before "now") the position was last updated int altitude; // the aircraft altitude in feet, or "ground" if it is reporting it is on the ground int vert_rate; // vertical rate in feet/minute int track; // true track over ground in degrees (0-359) int speed; // reported speed in kt. This is usually speed over ground, but might be IAS - you can't tell the difference here, sorry! int messages; // total number of Mode S messages received from this aircraft int seen; // how long ago (in seconds before "now") a message was last received from this aircraft int rssi; // recent average RSSI (signal power), in dbFS; this will always be negative. time_t TimeAdded; time_t TimeLastUpdated; // Fields for Intellegent TrackLog double LatTrack[TRACKPOINTS]; // Cyclic Tracklog double LonTrack[TRACKPOINTS]; time_t TrackTime[TRACKPOINTS]; int Trackptr; // Next record in Tracklog // info about last track point written double Lastlat; double LastLong; int LastCourse; int LastSpeed; time_t LastTime; } ADSBRecord; extern BOOL TraceAIS; extern BOOL NoVDO; extern BOOL NoAlarms; UINT PaintColour; //char Msg[1000]; //UINT Colour; char CloseString[100]; //extern int ListItem; SOCKET udpsock; SOCKADDR_IN sinx; SOCKADDR_IN rxaddr, txaddr, pitxaddr; double pi = 3.14159265389754; double OurSog, OurCog, OurLatitude, OurLongtitude; double OurLatIncr, OurLongIncr; unsigned int UserID; int NavStatus; double V_Lat, V_Lon, V_COG, V_SOG, V_ROT; char V_Name[21], V_Callsign[8], V_Dest[21], V_VendorID[8]; int V_IMO, V_Heading, V_Type, B_PartNo; int Msg_Type, Accuracy, FixType; int UTCyear, UTCMonth, UTCDay, UTCHour, UTCMinute, UTCSecond; int NavAidType, V_Draught, ETA; int Alt; int V_LenA, V_LenB, V_WidthC, V_WidthD; int SyncState, SlotTO, SubMsg; int GpsOK; int DecayTimer; static int MaxAge = 7200; // 2 Hours int VessselDBChanged = 0; int NavAidDBChanged = 0; BOOL SoundActive; extern BOOL KeepTracks; HANDLE TrackHandle; HANDLE hTrack; UINT LastTrackFileTime; UINT NOW; char Stack[500]; BOOL Stacked=FALSE; int NavAidCount=0; struct NAVAIDRECORD ** NavRecords; int SARCount=0; struct SARRECORD ** SARRecords; int TargetCount=0; struct TARGETRECORD ** TargetRecords; int BaseStationCount=0; struct BASESTATIONRECORD ** BaseStationRecords; int ADSBCount = 0; struct ADSBRECORD ** ADSBRecords; UCHAR conv[256]={99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 00 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 30 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 40 @ 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 34, 35, 36, 37, 38, 39, // 50 P 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, // 60 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, // 77 = 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}; char *NavAidString[] = { "Default, Type of A to N not specified", "Reference Point", "RACON", "Structure Off shore Structure", "Spare", "Light, without sectors", "Light, with sectors", "Leading Light Front", "Leading Light Rear", "Beacon, Cardinal N", "Beacon, Cardinal E", "Beacon, Cardinal S", "Beacon, Cardinal W", "Beacon, Port hand", "Beacon, Starboard hand", "Beacon, Preferred Channel port hand", "Beacon, Preferred Channel starboard hand", "Beacon, Isolated danger", "Beacon, Safe water", "Beacon, Special mark", "Cardinal Mark N", "Cardinal Mark E", "Cardinal Mark S", "Cardinal Mark W", "Port hand Mark", "Starboard hand Mark", "Preferred Channel Port hand", "Preferred Channel Starboard hand", "Isolated danger", "Safe Water", "Special Mark", "Light Vessel / LANBY / Rigs"}; char * FixTypeString[] = { "Undefined", "GPS", "GLONASS", "Combined GPS/GLONASS", "Loran - C", "Chayka", "Integrated Navigation System", "surveyed"}; char * NavStatusString[] = { "Under way using engine", "At anchor", "Not under command", "Restricted manoeuvrability", "Constrained by draught", "Moored", "Aground", "Engaged in Fishing", "Under way sailing", "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", "Not defined"}; char * VesselType[] = { // First Digit "Undefined", "Reserved for future use", "WIG", // 2 "", // 3 "HSC", "", "Passenger ship", "Cargo ship", "Tanker", "Other types of ship"}; char * VesselType3[] = { "Fishing", "Towing", "Towing Len > 200 m or breadth > 25 m", "Dredging or underwater operations", "Diving", "Military", "Sailing", "Pleasure Craft", "Reserved", "Reserved"}; char * VesselType5[] = { "Pilot vessel", "Search and rescue vessel", "Tug", "Port tender", "Vessel with anti-pollution facilities or equipment", "Law enforcement vessel", "Spare", "Spare", "Medical transport", "Ships according to Resolution No 18 (Mob-83)"}; extern struct SHIPDATABASERECORD * ShipDataRec; int ACount = 0, BCount = 0; char ADSBHost[64] = ""; //"10.8.0.50"; int ADSBPort = 30003; int ADSBConnected = 0; extern double Lat; extern double Lon; // Forward declarations of functions included in this code module: double radians(double Degrees); double degrees(double radians); BOOL Check0183CheckSum(char * msg,int len); int ProcessNMEAMessage(char * msg, int len); int DecodeVDM(char * Data); int ConvertAsciito6Bit(char * msg, char * buffer, int Len); void ProcessAISVesselMessage(); void ProcessSARMessage(); void ProcessBaseStationMessage(); void ProcessAISVesselDetails(char Type); void ProcessAISNavAidMessage(); void MM_Reinit(); void CalculateClosestApproach(struct TARGETRECORD * ptr); static double Distance(double lah, double loh, double laa, double loa); static double Bearing(double lat1, double lon1, double lat2, double lon2); double CalcClosest(struct TARGETRECORD * ptr); void RefreshTargetList(); void RefreshTargetSub(struct TARGETRECORD * ptr, int j); void CheckAgeofTargets(UINT MaxAge); void ActivateTarget(); void Write_Target_Track(struct TARGETRECORD * ptr); void Write_Our_Track(); void MaintainVesselDatabase(UINT UserID, char Type); VOID AutoPilotUpdate(); VOID ProcessNMEAMessageCE(char * msg, int len); void ProcessADSBJSON(char * FN); void CheckAgeofPlanes(UINT MaxAge); static VOID ADSBConnect(void * unused); char ADSBFN[256] = "/dev/shm/adsb/aircraft.json"; char DataBasefn[MAX_PATH]; int ShipDatabaseCount=0; struct SHIPDATABASERECORD ** ShipDatabaseRecords; struct SHIPDATABASERECORD * ShipDataRec; void SaveTrackPoint(struct TARGETRECORD * ptr) { // If course and speed have changed, and distance travelled or time is significant, add a track point to log if ((NOW - ptr->LastTime) < 15) // Not More that once per 15 secs return; if (fabs(ptr->LastCourse - ptr->course) < 1.0 && fabs(ptr->LastSpeed - ptr->speed) < 0.2 && ptr->speed > 0.2) // if very slow, COG may be random { // Steady Course - update once per 5 minutes if moving if ((NOW - ptr->LastTime) > 600 && ptr->speed != 0) goto writeRec; } if (fabs(ptr->Lastlat - ptr->lat) < 0.01 && fabs(ptr->LastLong - ptr->Long) < 0.01) // Not moved much return; writeRec: ptr->LastCourse = ptr->course; ptr->LastSpeed = ptr->speed; ptr->LastTime = NOW; ptr->Lastlat = ptr->lat; ptr->LastLong = ptr->Long; ptr->LatTrack[ptr->Trackptr] = ptr->lat; ptr->LonTrack[ptr->Trackptr] = ptr->Long; ptr->TrackTime[ptr->Trackptr] = NOW; ptr->Trackptr++; if (ptr->Trackptr == TRACKPOINTS) ptr->Trackptr = 0; } void SaveSARTrackPoint(struct SARRECORD * ptr) { // If course and speed have changed, and distance travelled or time is significant, add a track point to log if ((NOW - ptr->LastTime) < 15) // Not More that once per 15 secs return; if (fabs(ptr->LastCourse - ptr->course) < 1.0 && fabs(ptr->LastSpeed - ptr->speed) < 0.2 && ptr->speed > 0.2) // if very slow, COG may be random { // Steady Course - update once per 5 minutes if moving if ((NOW - ptr->LastTime) > 600 && ptr->speed != 0) goto writeRec; } if (fabs(ptr->Lastlat - ptr->lat) < 0.01 && fabs(ptr->LastLong - ptr->Long) < 0.01) // Not moved much return; writeRec: ptr->LastCourse = ptr->course; ptr->LastSpeed = ptr->speed; ptr->LastTime = NOW; ptr->Lastlat = ptr->lat; ptr->LastLong = ptr->Long; ptr->LatTrack[ptr->Trackptr] = ptr->lat; ptr->LonTrack[ptr->Trackptr] = ptr->Long; ptr->TrackTime[ptr->Trackptr] = NOW; ptr->Trackptr++; if (ptr->Trackptr == TRACKPOINTS) ptr->Trackptr = 0; } void MaintainVesselDatabase(UINT UserID, char Type) { LookupVessel(UserID); // Find Vessel - if not present, add it if (ShipDataRec == NULL) return; // Malloc failed!! if (Type == 'A') ShipDataRec->Class='A'; else ShipDataRec->Class='B'; if (Type == 'A') { memcpy(ShipDataRec->name, V_Name, 21); memcpy(ShipDataRec->Callsign,V_Callsign,8); ShipDataRec->Type = V_Type; ShipDataRec->LengthA = V_LenA; ShipDataRec->LengthB = V_LenB; ShipDataRec->WidthC = V_WidthC; ShipDataRec->WidthD = V_WidthD; } if (Type == 'B') // Type 19 Class B Details { memcpy(ShipDataRec->name, V_Name, 21); ShipDataRec->Type = V_Type; ShipDataRec->LengthA = V_LenA; ShipDataRec->LengthB = V_LenB; ShipDataRec->WidthC = V_WidthC; ShipDataRec->WidthD = V_WidthD; } if (Type == '0') // Class B record 24A { memcpy(ShipDataRec->name, V_Name, 21); } if (Type == '1') // Class B record 24B { memcpy(ShipDataRec->Callsign,V_Callsign,8); ShipDataRec->Type = V_Type; ShipDataRec->LengthA = V_LenA; ShipDataRec->LengthB = V_LenB; ShipDataRec->WidthC = V_WidthC; ShipDataRec->WidthD = V_WidthD; } ShipDataRec->TimeLastUpdated = NOW; VessselDBChanged = 1; // Save on next timer tick return; } void LookupVessel(UINT UserID) { int i; for (i = 0; i < ShipDatabaseCount; i++) { ShipDataRec=ShipDatabaseRecords[i]; if (ShipDataRec->ID == UserID) return; } // Not found if (ShipDatabaseCount == 0) ShipDatabaseRecords=(struct SHIPDATABASERECORD **)malloc(sizeof(void *)); else ShipDatabaseRecords=(struct SHIPDATABASERECORD **)realloc(ShipDatabaseRecords,(ShipDatabaseCount+1)*sizeof(void *)); ShipDataRec=(struct SHIPDATABASERECORD *)malloc(sizeof(struct SHIPDATABASERECORD)); memset(ShipDataRec, 0, sizeof(struct SHIPDATABASERECORD)); if (ShipDataRec == NULL) return; ShipDatabaseRecords[ShipDatabaseCount]=ShipDataRec; ShipDataRec->ID = UserID; ShipDataRec->TimeAdded = NOW; ShipDatabaseCount++; sprintf(ShipDataRec->name,"%d",UserID); } void LoadVesselDataBase() { int i; FILE *file; char buf[256]; char *token; char FN[256]; if (BPQDirectory[0] == 0) { strcpy(FN, "AIS_Vessels.txt"); } else { strcpy(FN, BPQDirectory); strcat(FN, "/"); strcat(FN, "AIS_Vessels.txt"); } if ((file = fopen(FN,"r")) == NULL) return; fgets(buf, 255, file); sscanf(buf,"%d", &ShipDatabaseCount); if (ShipDatabaseCount == 0) { fclose(file); return; } ShipDatabaseRecords = (struct SHIPDATABASERECORD **)malloc(ShipDatabaseCount * sizeof(void *)); for (i = 0; i < ShipDatabaseCount; i++) { ShipDataRec = (struct SHIPDATABASERECORD *)malloc(sizeof(struct SHIPDATABASERECORD)); ShipDatabaseRecords[i] = ShipDataRec; memset(ShipDataRec, 0, sizeof(struct SHIPDATABASERECORD)); fgets(buf, 255, file); token = strtok(buf, "|\n" ); ShipDataRec->ID = atoi(token); token = strtok( NULL, "|\n" ); strcpy(&ShipDataRec->name[0],token); token = strtok( NULL, "|\n" ); strcpy(&ShipDataRec->Callsign[0],token); token = strtok( NULL, "|\n" ); ShipDataRec->LengthA = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->LengthB = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->WidthC = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->WidthD = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->Draft = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->TimeAdded = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->TimeLastUpdated = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->Type = atoi(token); token = strtok( NULL, "|\n" ); ShipDataRec->Class = atoi(token); } fclose(file); } void SaveVesselDataBase() { int i, n = 0; FILE *file; char buf[256]; char FN[256]; if (BPQDirectory[0] == 0) { strcpy(FN, "AIS_Vessels.txt"); } else { strcpy(FN, BPQDirectory); strcat(FN, "/"); strcat(FN, "AIS_Vessels.txt"); } if ((file = fopen(FN,"w")) == NULL) return; for (i = 0; i < ShipDatabaseCount; i++) { if (ShipDatabaseRecords[i]->Callsign[0] > 32) // Count those with details n++; } sprintf(buf,"%d\n", n); fputs(buf, file); for (i = 0; i < ShipDatabaseCount; i++) { ShipDataRec = ShipDatabaseRecords[i]; if (ShipDataRec->Callsign[0] > 32) // No point in saving if no vessel details { sprintf(buf,"%d|%s|%s|%d|%d|%d|%d|%d|%d|%d|%d|%d\n", ShipDataRec->ID, ShipDataRec->name, ShipDataRec->Callsign, ShipDataRec->LengthA, ShipDataRec->LengthB, ShipDataRec->WidthC, ShipDataRec->WidthD, ShipDataRec->Draft, ShipDataRec->TimeAdded, ShipDataRec->TimeLastUpdated, ShipDataRec->Type, ShipDataRec->Class); fputs(buf, file); } } fclose(file); } void LoadNavAidDataBase() { int i; FILE *file; char buf[256]; char *token; char FN[256]; struct NAVAIDRECORD * navptr; if (BPQDirectory[0] == 0) { strcpy(FN, "AIS_NavAids.txt"); } else { strcpy(FN, BPQDirectory); strcat(FN, "/"); strcat(FN, "AIS_NavAids.txt"); } if ((file = fopen(FN,"r")) == NULL) return; fgets(buf, 255, file); sscanf(buf,"%d", &NavAidCount); if (NavAidCount == 0) { fclose(file); return; } NavRecords = (struct NAVAIDRECORD **)malloc(NavAidCount * sizeof(void *)); for (i = 0; i < NavAidCount; i++) { navptr = (struct NAVAIDRECORD *)malloc(sizeof(struct NAVAIDRECORD)); NavRecords[i] = navptr; memset(navptr, 0, sizeof(struct NAVAIDRECORD)); fgets(buf, 255, file); token = strtok(buf, "|\n" ); navptr->ID = atoi(token); token = strtok(NULL, "|\n" ); strcpy(&navptr->name[0],token); token = strtok(NULL, "|\n" ); navptr->lat = atof(token); token = strtok(NULL, "|\n" ); navptr->Long = atof(token); token = strtok(NULL, "|\n" ); navptr->TimeAdded = atoi(token); token = strtok(NULL, "|\n" ); navptr->TimeLastUpdated = atoi(token); } fclose(file); } void SaveNavAidDataBase() { int i; FILE *file; char FN[256]; struct NAVAIDRECORD * navptr; if (BPQDirectory[0] == 0) { strcpy(FN, "AIS_NavAids.txt"); } else { strcpy(FN, BPQDirectory); strcat(FN, "/"); strcat(FN, "AIS_NavAids.txt"); } if ((file = fopen(FN,"w")) == NULL) return; fprintf(file, "%d\n", NavAidCount); for (i = 0; i < NavAidCount; i++) { navptr=NavRecords[i]; fprintf(file, "%d|%s|%f|%f|%d|%d\n", navptr->ID, navptr->name, navptr->lat, navptr->Long, navptr->TimeAdded, navptr->TimeLastUpdated); } fclose(file); } void initAIS() { LoadVesselDataBase(); LoadNavAidDataBase(); } void SaveAIS() { SaveVesselDataBase(); SaveNavAidDataBase(); } void initADSB() { _beginthread(ADSBConnect, 0, 0); } int ProcessAISMessage(char * msg, int len) { char * ptr1; char * ptr2; char X1[10], X2[10], X3[10], X4[10]; char Data[256]; float alt=0.0; NOW = time(NULL); ptr1 = &msg[7]; //' Looks like first is no of fragments in message, 2nd fragment seq no, 3rd msg seq, 4th port //'!AIVDM,2,1,7,2,502He5h000030000000AD8hTr10u9B1IA<0000000000040000<000000000,0*54 //'!AIVDM,2,2,7,2,00000000008,2*58 if (memcmp(&msg[1],"AIVDM",5) == 0) { if (memchr(msg,'*',len) == 0) return 0; // No Checksum len-=7; ptr2=(char *)memchr(ptr1,',',8); if (ptr2 == 0) return 0; // Duff *(ptr2++)=0; strcpy(X1,ptr1); ptr1=ptr2; ptr2=(char *)memchr(ptr1,',',8); if (ptr2 == 0) return 0; // Duff *(ptr2++)=0; strcpy(X2,ptr1); ptr1=ptr2; ptr2=(char *)memchr(ptr1,',',8); if (ptr2 == 0) return 0; // Duff *(ptr2++)=0; strcpy(X3,ptr1); ptr1=ptr2; if (*ptr1 == 'A') ACount++; else BCount++; ptr2=(char *)memchr(ptr1,',',8); if (ptr2 == 0) return 0; // Duff *(ptr2++)=0; strcpy(X4,ptr1); ptr1=ptr2; ptr2=(char *)memchr(ptr1,',',78); if (ptr2 == 0) return 0; // Duff *(ptr2++)=0; strcpy(Data,ptr1); if (X1[0] == '1') { // Single fragment DecodeVDM (Data); return 0; } // Multipart. if (X2[0] == '1') { // First of Multipart strcpy(Stack,Data); return 0; // ' this needs generalising } // Subsequent part of Multipart strcat(Stack, Data); if (X1[0] == X2[0]) { // Last Part DecodeVDM (Stack); return 0; } } return 0; } int mm1=0, nasa1=0, mm4=0, nasa4=0; int DecodeVDM(char *msg) { int Conv, i, bits; UCHAR val[500]; // Start value at offet 1 for compatiblilty woth Basic Code ConvertAsciito6Bit(msg, (char *)&val[1], strlen(msg)); // Convert ascii chars to 6-bit values Msg_Type = val[1]; UserID = ((val[2] & 15) << 26) + (val[3] << 20)+ (val[4] << 14)+ (val[5] << 8)+ (val[6] << 2) + (val[7] >>4); ; // if (UserID == 0 ) UserID = 99998888; bits=strlen(msg)*6; if (Msg_Type == 1 || Msg_Type == 2 || Msg_Type == 3) { if (bits == 168) nasa1++; else mm1++; NavStatus = val[7] & 15; //'Rate of turn 8 50 Char 8 Char 9 bits 0-1±127 (–128 (80 hex) indicates not available, which should be the V_ROT = (val[8] << 2) + (val[9] >> 4); /* ' 0...+ 126 = turning right at up to 708 degrees per minute or higher; ' 0...- 126 = turning left at up to 708 degrees per minute or higher ' Values between 0 and 708 degrees/min coded by ' ROTAIS=4.733 SQRT(ROTsensor) degrees/min ' where ROTsensor is the Rate of Turn as input by an external Rate of Turn ' Indicator. ROTAIS is rounded to the nearest integer value. ' + 127 = turning right at more than 5 deg/30s (No TI available) ' -127 = turning Left at more than 5 deg/30s (No TI available) ' –128 (80 hex) indicates no turn information available (default). */ if (V_ROT > 128) V_ROT = -(256 - V_ROT); if (V_ROT == 128) V_ROT = 0; else if (V_ROT == 127) V_ROT = 3; // 5 deg / 30 secs or more else if (V_ROT == -127) V_ROT = -3; else { V_ROT = V_ROT / 4.733; V_ROT = V_ROT * fabs(V_ROT); } //'SOG 10 60 Char 9 bits 2-5 char 10 Speed over ground in 1/10 knot steps (0-102.2 knots) V_SOG = (((val[9] & 15) << 6) + val[10]); if (V_SOG == 1023.0) // Unknown V_SOG = 0.0; V_SOG = V_SOG/10; //'Accuracy 1 61 Char 11 bit 0 Accuracy = val[11] >> 5; //'Long 28 89 char 11 bits 1-5 char 12 13 14 char 15 bita 0-4 Conv = ((val[11] & 31) << 23) + (val[12] << 17) + (val[13] << 11) + (val[14] << 5) + (val[15] >>1); if ((val[11] & 16) == 16) Conv = -(0x10000000 - Conv); V_Lon = Conv; V_Lon/= 600000; if (V_Lon > 18 || V_Lon < -18) return 0; //Lat 27 116 Char 15 bit 5 16 17 18 19 20 bits 0-1 Conv = ((val[15] & 1) << 26) + (val[16]<< 20) + (val[17]<< 14) + (val[18]<< 8) + (val[19]<< 2) + (val[20] >>4); if ((val[15] & 1) == 1) Conv = -(0x8000000 - Conv); V_Lat = Conv; V_Lat/= 600000; //COG 12 128 char 20 bits 2-5 21 22 bit 0-1 V_COG = ((val[20] & 15) << 8) + (val[21]<< 2) + (val[22] >>4); V_COG /= 10; //True Heading 9 137 char 22 bits 2-5 char 23 bits 0-4 V_Heading = ((val[22] & 15) << 5) + (val[23] >>1); /* 'Message ID 6 Char 1 Identifier for this message 1, 2 or 3 'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been ' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. 'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number 'Navigational status 4 42 Char 7 bits 2-5 0 = under way using engine, 1 = at anchor, 2 = not under command, 'Rate of turn 8 50 Char 8 Char 9 bits 0-1±127 (–128 (80 hex) indicates not available, which should be the 'SOG 10 60 Char 9 bits 2-5 char 10 Speed over ground in 1/10 knot steps (0-102.2 knots) 'Accuracy 1 61 Char 11 bit 0 'Long 28 89 char 11 bits 1-5 char 12 13 14 char 15 bita 0-4 'Lat 27 116 Char 15 bit 5 16 17 18 19 20 bits 0-1 'COG 12 128 char 20 bits 2-5 21 22 bit 0-1 'True Heading 9 137 char 22 bits 2-5 char 23 bits 0-4 'Time Stamp 6 143 char 23 bits 5 24 bits 0-4 'Reserved 4 147 char 24 bits 5 char 25 bits 0-2 'Spare 1 148 char 25 bit 3 'RAIM 1 149 char 25 bit 4 'Comms State 19 168 char 25 bit 5 26 27 28 See below. 'Total number of bits 168 28 chars */ ProcessAISVesselMessage(); return 0; } // if (Msg_Type == 4 || Msg_Type == 11) // 11 are probably ships responding to Tamestamp requests if (Msg_Type == 4) { if (bits == 168) nasa4++; else mm4++; // Base Station //'Message ID 6 Char 1 Identifier for this message 1, 2 or 3 //'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been //' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. //'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number //'UTC year 14 52 Char 7 bits 2-5, char 8, char 9 bits 0-3 1 - 9999; 0 = UTC year not available = default. UTCyear = ((val[7] & 15) << 10) + (val[8] << 4) + (val[9] >> 2); //'UTC month 4 56 Char 9 bits 4-5, char 10 bits 0-1 1 - 12; 0 = UTC month not available = default; 13 - 15 not used UTCMonth = ((val[9] & 3) << 2) + (val[10] >> 4); //'UTC day 5 61 Char 10 bits 2-5, chr 11 bit 0 1 - 31; 0 = UTC day not available = default. UTCDay = ((val[10] & 15) << 1) + (val[11] >> 5); //'UTC hour 5 66 Char 11 bits 2-5 0 - 23; 24 = UTC hour not available = default; UTCHour = val[11] & 31; //'UTC minute 6 72 Char 12 0 - 59; 60 = UTC minute not available = default; UTCMinute = val[12]; //'UTC second 6 78 Char 13 0 - 59; 60 = UTC second not available = default; UTCSecond = val[13]; //'Position accuracy 1 79 Char 14 bit 0 1 = high (< 10 m; Differential Mode of e.g. DGNSS receiver) 0 = low //'(> 10 m; Autonomous Mode of e.g. GNSS receiver, or of other Electronic //'Position Fixing Device), default = 0 Accuracy = val[14] >> 5; //'Long 28 107 char 14 bits 1-5 char 15 16 17 char 18 bitS 0-4 //'Longitude 28 Longitude in 1/10 000 minute (±180 degrees, East = positive (as per 2΄s //'complement), West = negative(as per 2΄s complement); //'181 degrees (6791AC0 hex) = not available = default) Conv = ((val[14] & 31) << 23) + (val[15] << 17) + (val[16] << 11) + (val[17] << 5) + (val[18] >>1); if ((val[14] & 16) == 16) Conv = -(0x10000000 - Conv); V_Lon = Conv; V_Lon/= 600000; //'Latitude 27 Latitude in 1/10 000 minute (±90 degrees, North = positive (as per 2΄s //'complement), South = negative (as per 2΄s complement); //'91 degrees (3412140 hex) = not available = default) //'Lat 27 134 Char 18 bit 5 19 20 21 22 23 bits 0-1 Conv = ((val[18] & 1) << 26) + (val[19]<< 20) + (val[20]<< 14) + (val[21]<< 8) + (val[22]<< 2) + (val[23] >>4); if ((val[18] & 1) == 1) Conv = -(0x8000000 - Conv); V_Lat = Conv; V_Lat/= 600000; /* 'Type of Electronic 'Position Fixing 'Device ' 4 138 Char 23 bits 2-5 Use of differential corrections is defined by field "position accuracy" 'above; '0 = Undefined (default), '1 = GPS, '2 = GLONASS, '3 = Combined GPS/GLONASS, '4 = Loran-C, '5 = Chayka, '6 = Integrated Navigation System, '7 = surveyed, '8 - 15 = not used. */ FixType = val[23] & 15; /* 'Spare 10 148 char 24, char 25 bits 0-3 Not used. Should be set to zero. 'RAIM-Flag 1 149 char 25 bit 4 RAIM (Receiver Autonomous Integrity Monitoring) flag of Electronic 'Position Fixing Device; 0 = RAIM not in use = default; 1 = RAIM in use) 'Communication State 19 168 char 25 bit 5, 26 27 28 SOTDMA Communication State as described in § 3.3.7.2.2. 'Sync state 2 0 UTC Direct (refer to § 3.1.1.1). ' 1 UTC Indirect (refer to § 3.1.1.2). ' 2 Station is synchronized to a Base station(Base direct) (refer to § 3.1.1.3). ' 3 Station is synchronized to another station based on the highest ' number of received stations or to another mobile station, which is ' directly synchronized to a base station (refer to § 3.1.1.3 and § 3.1.1.4). 'Slot Time-Out 3 Specifies frames remaining until a new slot is selected. '0 means that this was the last transmission in this slot. '1-7 means that 1 to 7 frames respectively are left until slot change. 'Sub message 14 The sub message depends on the current value in slot time-out as */ SyncState = ((val[25] & 1) << 1) + (val[26] >> 5); //' Debug.Print "Sync State "; SyncState; SlotTO = (val[26] >>2) & 7; //' Debug.Print "Slot TO "; SlotTO; SubMsg = ((val[26] & 3) << 12) + (val[27] << 6) + val[28]; //'Total number of bits 168 ProcessBaseStationMessage(); return 0; } if (Msg_Type == 5) { //'IMO No 30 70 char 7 4-5, 8 9 10 11, char 12 0-3 V_IMO = ((val[7] & 3) << 28) + (val[8]<< 22) + (val[9]<< 16) + (val[10]<< 10) + (val[11]<< 4) + (val[12] >>2); //'Callsign 42 112 char 12 4-5, 13 14 15 16 17 18 19 bits 0-3 for (i = 0; i <= 6; i++) { Conv = ((val[12 + i] & 3) << 4) + (val[13 + i] >> 2); if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_Callsign[i] = Conv; } //Name 120 232 char 19 bits 4-5 20-38 39 bits 0-3 for (i = 0; i <= 19; i++) { Conv = ((val[19 + i] & 3) << 4) + (val[20 + i] >> 2); if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_Name[i] = Conv; } //'Type of Ship 8 240 char 39 bits 4-5 40 V_Type=((val[39] & 3) << 6) + val[40]; //'Dimensions 30 270 char 41 42 43 44 45 //'A Bit 21 – Bit 29 0 – 511 9 //'B Bit 12 – Bit 20 0 – 511 9 //'C Bit 6 – Bit 11 0 - 63 ; 6 //'D Bit 0 – Bit 5 0 - 63 ; 6 V_LenA = (val[41] << 3) + (val[42] >> 3); V_LenB = ((val[42] & 7) << 6) + val[43]; V_WidthC = val[44]; V_WidthD = val[45]; // sprintf(TraceMsg,"%s %d %d %d %d %d LenA %d LenB %d\n", V_Name, val[41], val[42], val[43], val[44], val[45], V_LenA, V_LenB); // OutputDebugString(TraceMsg); //' Type of Fix 4 274 char 46 bits 0-3 FixType = val[46] >>2; //'ETA 20 294 char 46 bits 4-5, 47,48,49 ETA =((val[46] & 3) << 18) + (val[47]<< 12) + (val[48]<< 6) + val[49]; //'Draught 8 302 char 50, char 51 bits 0-1 V_Draught = (val[50] << 2) + (val[51] >>4); //'Destination 120 422 char 51 bits 2-5, 52-70 71 bits 0-1 for (i = 0; i <= 19; i++) { Conv = ((val[51 + i] & 15) << 2) + (val[52 + i] >> 4); if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_Dest[i] = Conv; } //'DTE 1 423 char 71 bit 2 //'Spare 1 424 char 71 bit 3 ProcessAISVesselDetails('A'); return 0; } /* ' Type 5 'Message ID 6 Char 1 Identifier for this message 1, 2 or 3 'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been ' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. 'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number 'AIS Version 2 40 char 7 2-3 'IMO No 30 70 char 7 4-5, 8 9 10 11, char 12 0-3 'Callsign 42 112 char 12 4-5, 13 14 15 16 17 18 19 bits 0-3 'Name 120 232 char 19 bits 4-5 20-38 39 bits 0-3 'Type of Ship 8 240 char 39 bits 4-5 40 'Dimensions 30 270 char 41 42 43 44 45 'A Bit 21 – Bit 29 0 – 511 9 'B Bit 12 – Bit 20 0 – 511 9 'C Bit 6 – Bit 11 0 - 63 ; 6 'D Bit 0 – Bit 5 0 - 63 ; 6 'Type of Fix 4 274 char 46 bits 0-3 'ETA 20 294 'Draught 8 302 'Destination 120 422 'DTE 1 423 'Spare 1 424 ' Total 424 bits = 53 bytes = 70 chars + 4 bits 'A Bit 21 – Bit 29 0 – 511 'B Bit 12 – Bit 20 0 – 511 'C Bit 6 – Bit 11 0 - 63 ; 'D Bit 0 – Bit 5 0 - 63 ; */ if (Msg_Type == 18) // Class B Position { /* Message ID 6 Identifier for Message 18; always 18 char 1 Repeat indicator 2 Used by the repeater to indicate how many times a message has been repeated. 0-3; shall be 0 for Class B “CS” transmissions char 2 bits 0-1 User ID 30 MMSI number Char 2 2-5, 3, 4, 5, 6, 7 bits 0-1 Reserved for regional or local applications 8 Reserved for definition by a competent regional or local authority. Shall be set to zero, if not used for any regional or local application. Regional applications should not use zero Char 7 bits 2-5, Char 8 bits 1-3 SOG 10 Speed over ground in 1/10 knot steps (0-102.2 knots) 1 023 = not available, 1 022 = 102.2 knots or higher Char 8 bits 4,5, Char 9, Char 10 bits 0-1 */ V_SOG = (((val[8] & 3) <<8) + (val[9] << 2) + (val[10] >>4)); V_SOG = V_SOG/10; // Position accuracy 1 1 = high (<10 m) 0 = low (>10 m) // Char 10 bit 2 Accuracy = ((val[10] & 8) >> 3); //Longitude 28 Longitude in 1/10 000 min (±180°, East = positive (as per 2's complement), West = negative (as per 2's complement), 181° (6791AC0 hex) = not available = default) // Char 10 bits 3-5, 11, 12, 13, 14, 15 bit 0 Conv = ((val[10] & 7) << 25) + (val[11] << 19) + (val[12] << 13) + (val[13] << 7) + (val[14] << 1) + (val[15] >>5); if ((val[10] & 4) == 4) Conv = -(0x10000000 - Conv); V_Lon = Conv; V_Lon/= 600000; //Latitude 27 Latitude in 1/10 000 min (±90°, North = positive (as per 2's complement), South = negative (as per 2's complement), 91° (3412140 hex) = not available = default) // Char 15 bits 1-5 16 17 18 19 bits 0-3 Conv = ((val[15] & 31) << 22) + (val[16]<< 16) + (val[17]<< 10) + (val[18]<< 4) + (val[19] >>2); if ((val[15] & 16) == 16) Conv = -(0x8000000 - Conv); V_Lat = Conv; V_Lat/= 600000; //COG 12 Course over ground in 1/10° (0-3 599). 3 600 (E10h) = not available = default; 3 601-4 095 shall not be used // Char 19 bits 4-5 20 21 bit 0-3 V_COG = ((val[19] & 3) << 10) + (val[20]<< 4) + (val[21] >>2); V_COG /= 10; //True heading 9 Degrees (0-359) (511 indicates not available = default) // char 21 bits 4-5 char 22 char 23 bit 0 V_Heading = ((val[21] & 3) << 7) + (val[22]<< 1) + (val[23] >>5); //Time stamp 6 UTC second when the report was generated by the EPFS (0-59); 60 if time stamp is not available, which shall also be the default value 61, 62 and 63 are not used by the Class B “CS” AIS // Char 23 bits 1-5, 24 bit 0 //Reserved for regional applications 2 Reserved for definition by a competent regional authority. Shall be set to zero, if not used for any regional application. Regional applications should not use zero // 24 bit 1-2 //Class B unit flag 1 0 = Class B SOTDMA unit 1 = Class B “CS” unit // 24 bit 3 //Class B display flag 1 0 = No display available; not capable of displaying Message 12 and 14 1 = Equipped with integrated display displaying Message 12 and 14 // 24 bit 4 //Class B DSC flag 1 0 = Not equipped with DSC function 1 = Equipped with DSC function (dedicated or time-shared) // 24 bit 5 //Class B band flag 1 0 = Capable of operating over the upper 525 kHz band of the marine band 1 = Capable of operating over the whole marine band (irrelevant if “Class B Msg22 flag” is 0) // 25 bit 0 //Class B Message 22 flag 1 0 = No frequency management via Message 22 , operating on AIS1, AIS2 only 1 = Frequency management via Message 22 // 25 bit 1 //Mode flag 1 0 = Station operating in autonomous mode = default 1 = Station operating in assigned mode // 25 bit 2 //RAIM-flag 1 RAIM flag of electronic position fixing device, optional; 0 = RAIM not in use = default; 1 = RAIM in use (valid data for expected position error) // 25 bit 3 //Communication state selector flag 1 1 = ITDMA communication state follows // 25 bit 4 //Communication state 19 ITDMA communication state; refer to § 4.3.3.5 // 25 bit 5, 26, 27. 28 //Total number of bits 168 Occupies one-time period (28 chars) ProcessAISVesselMessage(); return 0; } if (Msg_Type == 19) // Class B Extended Position { /* Message ID 6 Identifier for Message 18; always 18 char 1 Repeat indicator 2 Used by the repeater to indicate how many times a message has been repeated. 0-3; shall be 0 for Class B “CS” transmissions char 2 bits 0-1 User ID 30 MMSI number Char 2 2-5, 3, 4, 5, 6, 7 bits 0-1 Reserved for regional or local applications 8 Reserved for definition by a competent regional or local authority. Shall be set to zero, if not used for any regional or local application. Regional applications should not use zero Char 7 bits 2-5, Char 8 bits 1-3 SOG 10 Speed over ground in 1/10 knot steps (0-102.2 knots) 1 023 = not available, 1 022 = 102.2 knots or higher Char 8 bits 4,5, Char 9, Char 10 bits 0-1 */ V_SOG = (((val[8] & 3) <<8) + (val[9] << 2) + (val[10] >>4)); V_SOG = V_SOG/10; // Position accuracy 1 1 = high (<10 m) 0 = low (>10 m) // Char 10 bit 2 Accuracy = ((val[10] & 8) >> 3); //Longitude 28 Longitude in 1/10 000 min (±180°, East = positive (as per 2's complement), West = negative (as per 2's complement), 181° (6791AC0 hex) = not available = default) // Char 10 bits 3-5, 11, 12, 13, 14, 15 bit 0 Conv = ((val[10] & 7) << 25) + (val[11] << 19) + (val[12] << 13) + (val[13] << 7) + (val[14] << 1) + (val[15] >>5); if ((val[10] & 4) == 4) Conv = -(0x10000000 - Conv); V_Lon = Conv; V_Lon/= 600000; //Latitude 27 Latitude in 1/10 000 min (±90°, North = positive (as per 2's complement), South = negative (as per 2's complement), 91° (3412140 hex) = not available = default) // Char 15 bits 1-5 16 17 18 19 bits 0-3 Conv = ((val[15] & 31) << 22) + (val[16]<< 16) + (val[17]<< 10) + (val[18]<< 4) + (val[19] >>2); if ((val[15] & 16) == 16) Conv = -(0x8000000 - Conv); V_Lat = Conv; V_Lat/= 600000; //COG 12 Course over ground in 1/10° (0-3 599). 3 600 (E10h) = not available = default; 3 601-4 095 shall not be used // Char 19 bits 4-5 20 21 bit 0-3 V_COG = ((val[19] & 3) << 10) + (val[20]<< 4) + (val[21] >>2); V_COG /= 10; //True heading 9 Degrees (0-359) (511 indicates not available = default) // char 21 bits 4-5 char 22 char 23 bit 0 V_Heading = ((val[21] & 3) << 7) + (val[22]<< 1) + (val[23] >>5); //Time stamp 6 UTC second when the report was generated by the EPFS (0-59); 60 if time stamp is not available, which shall also be the default value 61, 62 and 63 are not used by the Class B “CS” AIS // Char 23 bits 1-5, 24 bit 0 //Reserved for regional applications 4 Reserved for definition by a competent regional authority. Shall be set to zero, if not used for any regional application. Regional applications should not use zero // 24 bit 1-4 //Name 120 Maximum 20 characters 6-bit ASCII, @@@@@@@@@@@@@@@@@@@@ = not available = default // 24 bit 5 - 44 bits 0-4 for (i = 0; i <= 19; i++) { Conv = (((val[24 + i]) & 1) << 5) + (val[25 + i] >> 1); if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_Name[i] = Conv; } //Type of ship and cargo type 8 0 = not available or no ship = default // 1-99 = as defined in § 3.3.8.2.3.2 // 100-199 = preserved, for regional use // 200-255 = preserved, for future use // 44 bit 5 45 46 bit 0 V_Type=((val[44] & 1) << 5) + (val[45] << 1) + (val[46] >> 5); //Dimension of ship/reference for position 30 Dimensions of ship in metres and reference point for reported position (see Fig. 17 and § 3.3.8.2.3.3) // 46 bit 1-5 47 48 49 50 51 bit 0 // 46 bit 1-5, 47 bits 0-3 - 47 bits 4,5 48, 49 bit bit 0 - 49 bits 1-5, 50 bit 0 - 50 1-5, 51 bit 0 V_LenA = ((val[46] & 31) << 5) + (val[47] >> 2); V_LenB = ((val[47] & 3) << 7) + (val[48] << 1) + (val[49] >> 5); V_WidthC = ((val[49] & 31) << 1) + (val[50] >> 5); V_WidthD = ((val[50] & 31) << 1) + (val[51] >> 5); //Type of electronic position fixing device 4 0 = Undefined (default); 1 = GPS, 2 = GLONASS, 3 = combined GPS/GLONASS, 4 = Loran-C, 5 = Chayka, 6 = integrated navigation system, 7 = surveyed; 8-15 = not used // 51 bits 1-4 //RAIM-flag 1 RAIM flag of electronic position fixing device; 0 = RAIM not in use = default; 1 = RAIM in use // 51 bit 5 //DTE 1 Data terminal ready (0 = available 1 = not available = default) (see § 3.3.8.2.3.1) // 52 bit 0 //Spare 5 Not used. Should be set to zero // 52 bits 1-5 // Total number of bits 312 (52 chars) Occupies two slots ProcessAISVesselMessage(); ProcessAISVesselDetails('B'); return 0; } // Navaid if (Msg_Type == 21) { //'Message ID 6 Char 1 Identifier for this message 21 //'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been //' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. //'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number //'Type of Aids-to- Navigation 5 43 Char 7 bits 2-5 Char 8 bit 0 0 = not available = default; 1 – 15 = Fixed Aid-to-Navigation; 16 - 31 = //' Floating Aid-to-Navigation; refer to appropriate definition set up by IALA; //' refer to Table 34bis. NavAidType = ((val[7] & 15) << 1) + (val[8] >>5); //'Name of Aid s-to-Navigation //' 120 Maximum 20 characters 6 bit ASCII, //' "@@@@@@@@@@@@@@@@@@@@" = not available = default. //' Navigation "@@@@@@@@@@@@@@@@@@@@" = not available = default. //'The name of the Aid-to-Navigation may be extended by the parameter //'"Name of Aid-to-Navigation Extension" below. //'Name 120 163 char 8 bits 1-5 9-27 28 bits 0 for (i = 0; i <= 19; i++) { Conv = ((val[8 + i] & 31) << 1) + (val[9 + i] >> 5); if (Conv < 32) Conv = Conv + 64; V_Name[i] = Conv; } V_Name[20]=0; //'Position accuracy 1 164 28 bit 1 1 = high (< 10 m; Differential Mode of e.g. DGNSS receiver) 0 = low //'(> 10 m; Autonomous Mode of e.g. GNSS receiver or of other Electronic //'Position Fixing Device) ; Default = 0 Accuracy = ((val[28] & 16) >> 4); //'Long 28 192 char 28 bits 2-5 char 29 30 31 32 //'Longitude 28 Longitude in 1/10 000 minute (±180 degrees, East = positive (as per 2΄s //'complement), West = negative(as per 2΄s complement); //'181 degrees (6791AC0 hex) = not available = default) Conv = ((val[28] & 15) << 24) + (val[29] << 18) + (val[30] << 12) + (val[31] << 6) + val[32]; if ((val[28] & 8) == 8) Conv = -(0x10000000 - Conv); V_Lon = Conv; V_Lon/= 600000; //'Latitude 27 Latitude in 1/10 000 minute (±90 degrees, North = positive (as per 2΄s //'complement), South = negative (as per 2΄s complement); //'91 degrees (3412140 hex) = not available = default) //'Lat 27 219 Char 33 34 35 36 37 bits 0-2 Conv = (val[33] << 21) + (val[34]<< 15) + (val[35]<< 9) + (val[36]<< 3) + (val[37] >> 3); if ((val[33] & 32) == 32) Conv = -(0x8000000 - Conv); V_Lat = Conv; V_Lat/= 600000; //'Dimension/Reference for Position 30 249 37 bits 3-5 38 39 40 41 42 bits 0-2 Reference point for reported position; also indicates the dimension of an //'Aid-to-Navigation in metres (see Fig. 18 and § 3.3.8.2.3.3), if relevant. (1) //'Type of Electronic //'A Bit 21 – Bit 29 0 – 511 9 //'B Bit 12 – Bit 20 0 – 511 9 //'C Bit 6 – Bit 11 0 - 63 ; 6 //'D Bit 0 – Bit 5 0 - 63 ; 6 V_LenA = ((val[37] & 7) << 3) + val[38]; V_LenB = (val[39] << 6) + (val[40] >> 3); V_WidthC = ((val[40] & 7) << 3) + (val[41] >> 3); V_WidthD = ((val[41] & 7) << 3) + (val[42] >> 3); /* 'Position Fixing Device 4 253 42 bits 3-5 43 bit 0 '0 = Undefined (default); '1 = GPS, '2 = GLONASS, '3 = Combined GPS/GLONASS, '4 = Loran-C, '5 = Chayka, '6 = Integrated Navigation System, '7 = surveyed.For fixed AtoNs and virtual AtoNs, the surveyed position 'should be used. The accurate position enhances its function as a radar 'reference target. '8 – 15 = not used. */ FixType = ((val[42] & 7) << 1) + (val[43] >> 5); /* 'Time Stamp 6 259 43 bit 1-5 44 bit 0 UTC second when the report was generated by the EPFS (0 –59, 'or 60 if time stamp is not available, which should also be the default value, 'or 61 if positioning system is in manual input mode, 'or 62 if Ele ctronic Position Fixing System operates in estimated (dead reckoning) mode, 'or 63 if the positioning system is inoperative) 'Off-Position Indicator 1 260 44 bit 1 For floating Aids-to-Navigation, only: 0 = on position; 1 = off position; 'NOTE – This flag should only be considered valid by receiving station, if 'the Aid -to-Navigation is a floating aid, and if Time Stamp is equal to or 'below 59. For floating AtoN the guard zone parameters should be set on installation. 'Reserved for regional or local application ' 8 268 44 bits 2-5 45 bit 0-3 'Reserved for definition by a competent regional or local authority. Should 'be set to zero, if not used for any regional or local application. Regional 'applications should not use zero. 'RAIM-Flag 1 269 45 bit 4 RAIM (Receiver Autonomous Integrity Monitoring) flag of Electronic 'Position Fixing Device; 0 = RAIM not in use = default; 1 = RAIM in use) 'Virtual AtoN Flag 1 270 45 bit 5 0 = default = real A to N at indicated position; 1 = virtual AtoN, does not 'physically exist, may only be transmitted from an AIS station nearby under 'the direction of a competent authority. (2) 'Assigned Mode Flag 1 271 46 bit 0 0 = Station operating in autonomous and continuous mode =default '1 = Station operating in assigned mode 'Spare 1 272 46 bit 1 Spare. Not used. Should be set to zero. 'Name of Aid-to-Navigation Extension '0, 6, 12, '18, 24, '30, 36, 'This parameter of up to 14 additional 6-bit-ASCII characters for a 2-slot 'message may be combined with the parameter "Name of Aid-to- 'Navigation " at the end of that parameter, when more than 20 characters are" '30, 36, '... 84 'Navigation " at the end of that parameter, when more than 20 characters are" 'needed for the Name of the Aid-to-Navigation. This parameter should be 'omitted when no more than 20 characters for the name of the A-to-N are 'needed in total. Only the required number of characters should be 'transmitted, i. e. no @-character should be used. 'Spare 0, 2, 4, 'or 6 'Spare. Used only when parameter "Name of Aid-to-Navigation Extension" 'is used. Should be set to zero. The number of spare bits should be adjusted 'in order to observe byte boundaries. 'Number of bits 272 – 360 'Occupies two slots. */ ProcessAISNavAidMessage(); return 0; } if (Msg_Type == 24) // Class B Details { //Message ID 6 Identifier for Message 24; always 24 //Repeat indicator 2 Used by the repeater to indicate how many times a message has been repeated. 0 = default; 3 = do not repeat any more //User ID 30 MMSI number // char 2 bits 2-5, 3 4, 5, 6, 7 bits 0,1 //Part number 2 Identifier for the message part number; always 0 for Part A // char 7 bits 2,3 B_PartNo=(val[7] & 15) >> 2; if (B_PartNo == 0) { //Name 120 Name of the MMSI-registered vessel. Maximum 20 characters 6-bit ASCII, @@@@@@@@@@@@@@@@@@@@ = not available = default // char 7 bits 4,5 - char 26 bits 0-3 for (i = 0; i <= 19; i++) { Conv = (((val[7 + i]) & 3) << 4) + (val[8 + i] >> 2); if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_Name[i] = Conv; } //Total number of bits 160 = 26 chars 4 bits Occupies one-time period ProcessAISVesselDetails('0'); return 0; } if (B_PartNo == 1) { //Type of ship and cargo type 8 0 = not available or no ship = default // 1-99 = as defined in § 3.3.8.2.3.2 of Annex 2 // 100-199 = preserved, for regional use // 200-255 = preserved, for future use // char 7 bits 4-5, char 8 V_Type=((val[7] & 3) << 2) + val[8]; //Vendor ID 42 Unique identification of the Unit by a number as defined by the manufacturer (option; “@@@@@@@“ = not available = default)// char 9-15 for (i = 0; i <= 6; i++) { Conv = val[i+9]; if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_VendorID[i] = Conv; } //Call sign 42 Call sign of the MMSI-registered vessel. 7 X 6-bit ASCII characters, “@@@@@@@“ = not available = default // char 16-22 for (i = 0; i <= 6; i++) { Conv = val[i+16]; if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; V_Callsign[i] = Conv; } //Dimension of ship/reference for position. Or, for unregistered daughter vessels, use the MMSI of the mother ship // 30 Dimensions of ship in meters and reference point for reported position (see Annex 2 Fig. 17 and § 3.3.8.2.3.3). Or, for an unregistered daughter vessel, use the MMSI of the associated mother ship in this data field // char 23-27 V_LenA = (val[23] << 3) + (val[24] >> 3); V_LenB = ((val[24] & 7) << 3) + val[25]; V_WidthC = val[26]; V_WidthD = val[27]; // Spare 6 // Total number of bits 168 (28 chars) Occupies one-time period ProcessAISVesselDetails('1'); return 0; } } if (Msg_Type == 9) { // Msg_Type = val[1]; //Message ID 6 Identifier for message 9; always 9 //Repeat Indicator 2 Used by the repeater to indicate how many times a message has beenrepeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. // char 2 bits 2-5, 3 4, 5, 6, 7 bits 0,1 // UserID = ((val[2] & 15) << 26) + (val[3] << 20)+ (val[4] << 14)+ (val[5] << 8)+ (val[6] << 2) + (val[7] >>4); ; //Part number 2 Identifier for the message part number; always 0 for Part A // char 7 bits 2,3 //Altitude (GNSS) 12 Altitude 7 bits 2-5 8 9 bits 0, 1 Alt = ((val[7] & 15) << 8) + (val[8] << 2) + (val[9] >> 4); //'SOG 10 60 Char 9 bits 2-5 char 10 Speed over ground in knot steps (0-1022 knots) V_SOG = (((val[9] & 15) <<6) + val[10]); //Position accuracy 1 Char 11 bit 0 1 = high (< 10 m; Differential Mode of e.g. DGNSS receiver) 0 = low(> 10 m; Autonomous Mode of e.g. GNSS receiver or of other ElectronicPosition Fixing Device) ; default = 0 //Longitude 28 Longitude in 1/10 000 min (±180°, East = positive (as per 2's complement), West = negative (as per 2's complement), 181° (6791AC0 hex) = not available = default) // Char 11 bits 1-5, 12, 13, 14, 15 0-4 Conv = ((val[11] & 31) << 23) + (val[12] << 17) + (val[13] << 11) + (val[14] << 5) + (val[15] >> 1); if ((val[11] & 16) == 16) Conv = -(0x10000000 - Conv); V_Lon = Conv; V_Lon/= 600000; //Latitude 27 Latitude in 1/10 000 min (±90°, North = positive (as per 2's complement), South = negative (as per 2's complement), 91° (3412140 hex) = not available = default) // Char 15 bit 5 16 17 18 19 20 bits 0-1 Conv = ((val[15] & 1) << 26) + (val[16]<< 20) + (val[17]<< 14) + (val[18]<< 8) + (val[19]<< 2) + (val[20] >> 4); if ((val[15] & 1) == 1) Conv = -(0x8000000 - Conv); V_Lat = Conv; V_Lat/= 600000; //COG 12 128 char 20 bits 2-5 21 22 bit 0-1 V_COG = ((val[20] & 15) << 8) + (val[21]<< 2) + (val[22] >>4); V_COG /= 10; //Time stamp 6 UTC second when the report was generated by the EPFS (0-59,or 60 if time stamp is not available, which should also be the defaultvalue,or 62 if Electronic Position Fixing System operates in estimated (deadreckoning) mode,or 61 if positioning system is in manual input modeor 63 if the positioning system is inoperative) //Reserved forregionalapplications 8 Reserved for definition by a competent regional authority. Should be setto zero, if not used for any regional application. Regional applicationsshould not use zero. //DTE 1 Data terminal ready (0 = available 1 = not available = default) (refer to§ 3.3.8.2.3.1). //Spare 5 3 Not used. Should be set to zero //Assigned ModeFlag1 0 = Station operating in autonomous and continous mode =default1 = Station operating in assigned mode //RAIM-Flag 1 RAIM (Receiver Autonomous Integrity Monitoring) flag of ElectronicPosition Fixing Device; 0 = RAIM not in use = default; 1 = RAIM in use)Communication //State SelectorFlag1 0 = SOTDMA Communication State follows;1 = ITDMA Communication State follows. //Communication State 19 SOTDMA (refer to § 3.3.7.2.2). //Total number of bits168 ProcessSARMessage(); } return Msg_Type; /* '@ 0 0x00 00 0000 64 0x40 0100 0000 ! 33 0x21 10 0001 33 0x21 0010 0001 'A 1 0x01 00 0001 65 0x41 0100 0001 " 34 0x22 10 0010 34 0x22 0010 0010 'B 2 0x02 00 0010 66 0x42 0100 0010 # 35 0x23 10 0011 35 0x23 0010 0011 'C 3 0x03 00 0011 67 0x43 0100 0011 $ 36 0x24 10 0100 36 0x24 0010 0100 'D 4 0x04 00 0100 68 0x44 0100 0100 % 37 0x25 10 0101 37 0x25 0010 0101 'E 5 0x05 00 0101 69 0x45 0100 0101 & 38 0x26 10 0110 38 0x26 0010 0110 'F 6 0x06 00 0110 70 0x46 0100 0110 ` 39 0x27 10 0111 39 0x27 0010 0111 'G 7 0x07 00 0111 71 0x47 0100 0111 ( 40 0x28 10 1000 40 0x28 0010 1000 'H 8 0x08 00 1000 72 0x48 0100 1000 ) 41 0x29 10 1001 41 0x29 0010 1001 'I 9 0x09 00 1001 73 0x49 0100 1001 * 42 0x2A 10 1010 42 0x2A 0010 1010 'J 10 0x0A 00 1010 74 0x4A 0100 1010 + 43 0x2B 10 1011 43 0x2B 0010 1011 'K 11 0x0B 00 1011 75 0x4B 0100 1011 ΄ 44 0x2C 10 1100 44 0x2C 0010 1100 'L 12 0x0C 00 1100 76 0x4C 0100 1100 - 45 0x2D 10 1101 45 0x2D 0010 1101 'M 13 0x0D 00 1101 77 0x4D 0100 1101 , 46 0x2E 10 1110 46 0x2E 0010 1110 'N 14 0x0E 00 1110 78 0x4E 0100 1110 / 47 0x2F 10 1111 47 0x2F 0010 1111 'O 15 0x0F 00 1111 79 0x4F 0100 1111 0 48 0x30 11 0000 48 0x30 0011 0000 'P 16 0x10 01 0000 80 0x50 0101 0000 1 49 0x31 11 0001 49 0x31 0011 0001 'Q 17 0x11 01 0001 81 0x51 0101 0001 2 50 0x32 11 0010 50 0x32 0011 0010 'R 18 0x12 01 0010 82 0x52 0101 0010 3 51 0x33 11 0011 51 0x33 0011 0011 'S 19 0x13 01 0011 83 0x53 0101 0011 4 52 0x34 11 0100 52 0x34 0011 0100 'T 20 0x14 01 0100 84 0x54 0101 0100 5 53 0x35 11 0101 53 0x35 0011 0101 'U 21 0x15 01 0101 85 0x55 0101 0101 6 54 0x36 11 0110 54 0x36 0011 0110 'V 22 0x16 01 0110 86 0x56 0101 0110 7 55 0x37 11 0111 55 0x37 0011 0111 'W 23 0x17 01 0111 87 0x57 0101 0111 8 56 0x38 11 1000 56 0x38 0011 1000 'X 24 0x18 01 1000 88 0x58 0101 1000 9 57 0x39 11 1001 57 0x39 0011 1001 'Y 25 0x19 01 1001 89 0x59 0101 1001 : 58 0x3A 11 1010 58 0x3A 0011 1010 'Z 26 0x1A 01 1010 90 0x5A 0101 1010 ; 59 0x3B 11 1011 59 0x3B 0011 1011 '[ 27 0x1B 01 1011 91 0x5B 0101 1011 < 60 0x3C 11 1100 60 0x3C 0011 1100 '\ 28 0x1C 01 1100 92 0x5C 0101 1100 = 61 0x3D 11 1101 61 0x3D 0011 1101 '] 29 0x1D 01 1101 93 0x5D 0101 1101 > 62 0x3E 11 1110 62 0x3E 0011 1110 '^ 30 0x1E 01 1110 94 0x5E 0101 1110 ? 63 0x3F 11 1111 63 0x3F 0011 1111 '_ 31 0x1F 01 1111 95 0x5F 0101 1111 '[SP] 32 0x20 10 0000 32 0x20 0010 0000 */ return Msg_Type; } int ConvertAsciito6Bit(char * msg, char * buffer, int Len) { int i; for (i = 0; i< Len; i++) { buffer[i]=conv[msg[i]]; } return 0; } void ProcessSARMessage() { int i; struct SARRECORD * ptr; for (i = 0; i < SARCount; i++) { ptr=SARRecords[i]; if (ptr->ID == UserID) { if (V_Lat < 50 || V_Lat > 70) return; ptr->lat = V_Lat; ptr->Long = V_Lon; ptr->course = V_COG; ptr->speed = V_SOG; ptr->Alt = Alt; ptr->TimeLastUpdated = NOW; SaveSARTrackPoint(ptr); return; } } if (SARCount == 0) SARRecords = (struct SARRECORD **)malloc(sizeof(void *)); else SARRecords = (struct SARRECORD **)realloc(SARRecords,(SARCount+1)*sizeof(void *)); ptr = (struct SARRECORD *)malloc(sizeof(struct SARRECORD)); if (ptr == NULL) return; memset(ptr, 0, sizeof(struct SARRECORD)); SARRecords[SARCount] = ptr; ptr->ID = UserID; ptr->TimeAdded = NOW; SARCount++; LookupVessel(UserID); if (ShipDataRec != NULL) { memcpy(ptr->name, ShipDataRec->name, 21); } ProcessSARMessage(); return; } void ProcessAISVesselMessage() { int i; struct TARGETRECORD * ptr; int Length; int Dimensions[10]; if (V_Lat > 90.0) return; for (i = 0; i < TargetCount; i++) { ptr=TargetRecords[i]; if (ptr->ID == UserID) { ptr->lat = V_Lat; ptr->Long = V_Lon; ptr->course = V_COG; if (V_COG == 360) ptr->course = V_Heading; //' 360 means not available ptr->speed = V_SOG; ptr->NavStatus = NavStatus; ptr->ROT = V_ROT; ptr->Heading = V_Heading; ptr->TimeLastUpdated = NOW; SaveTrackPoint(ptr); return; } } // Not found - add on end if (TargetCount == 0) TargetRecords=(struct TARGETRECORD **)malloc(sizeof(void *)); else TargetRecords=(struct TARGETRECORD **)realloc(TargetRecords,(TargetCount+1)*sizeof(void *)); ptr=(struct TARGETRECORD *)malloc(sizeof(struct TARGETRECORD)); memset(ptr, 0, sizeof(struct TARGETRECORD)); if (ptr == NULL) return; TargetRecords[TargetCount]=ptr; ptr->ID = UserID; ptr->TimeAdded = NOW; TargetCount++; // See if vessel is in database - if not, add LookupVessel(UserID); // Find Vessel - if not present, add it if (ShipDataRec != NULL) // Malloc failed!! { memcpy(ptr->name,ShipDataRec->name, 21); memcpy(ptr->Callsign, ShipDataRec->Callsign, 8); ptr->Type = ShipDataRec->Type; ptr->LengthA = ShipDataRec->LengthA; ptr->LengthB = ShipDataRec->LengthB; ptr->WidthC = ShipDataRec->WidthC; ptr->WidthD = ShipDataRec->WidthD; V_LenA = ShipDataRec->LengthA; V_LenB = ShipDataRec->LengthB; V_WidthC = ShipDataRec->WidthC; V_WidthD = ShipDataRec->WidthD; Length = V_LenA + V_LenB; if(Length > 0) { Dimensions[0] = -V_WidthC; Dimensions[1] = -V_LenB; Dimensions[2] = V_WidthD; Dimensions[3] = -V_LenB; Dimensions[4] = V_WidthD; Dimensions[5] = V_LenA - Length / 5; Dimensions[6] = ((V_WidthC + V_WidthD) / 2) - V_WidthC; Dimensions[7] = V_LenA; Dimensions[8] = -V_WidthC; Dimensions[9] = V_LenA - Length / 5; } } ProcessAISVesselMessage(); return; } void ProcessBaseStationMessage() { int i; struct BASESTATIONRECORD * ptr; for (i = 0; i < BaseStationCount; i++) { ptr=BaseStationRecords[i]; if (ptr->ID == UserID) { ptr->lat = V_Lat; ptr->Long = V_Lon; ptr->FixType = FixType; if (SlotTO == 3 || SlotTO == 5 || SlotTO == 7) ptr->NumberofStations = SubMsg; ptr->TimeLastUpdated = NOW; return; } } if (BaseStationCount == 0) BaseStationRecords=(struct BASESTATIONRECORD **)malloc(sizeof(void *)); else BaseStationRecords=(struct BASESTATIONRECORD **)realloc(BaseStationRecords,(BaseStationCount+1)*sizeof(void *)); ptr=(struct BASESTATIONRECORD *)malloc(sizeof(struct BASESTATIONRECORD)); if (ptr == NULL) return; memset(ptr, 0, sizeof(struct BASESTATIONRECORD)); BaseStationRecords[BaseStationCount]=ptr; ptr->ID = UserID; ptr->TimeAdded = NOW; BaseStationCount++; ProcessBaseStationMessage(); return; } void ProcessAISVesselDetails(char Type) { int i; struct TARGETRECORD * ptr; int Dimensions[10]; int Length; for (i = 0; i < TargetCount; i++) { ptr=TargetRecords[i]; if (ptr->ID == UserID) { if (Type == 'A') { memcpy(ptr->name, V_Name, 21); ptr->IMO = V_IMO; memcpy(ptr->Callsign,V_Callsign,8); memcpy(ptr->Dest, V_Dest, 21); ptr->Type = V_Type; ptr->LengthA = V_LenA; ptr->LengthB = V_LenB; ptr->WidthC = V_WidthC; ptr->WidthD = V_WidthD; ptr->Class='A'; } if (Type == 'B') // Type 19 Class B Details { memcpy(ptr->name, V_Name, 21); ptr->Type = V_Type; ptr->LengthA = V_LenA; ptr->LengthB = V_LenB; ptr->WidthC = V_WidthC; ptr->WidthD = V_WidthD; } if (Type == '0') // Class B record 24A { memcpy(ptr->name, V_Name, 21); } if (Type == '1') // Class B record 24B { memcpy(ptr->Callsign,V_Callsign,8); ptr->Type = V_Type; ptr->LengthA = V_LenA; ptr->LengthB = V_LenB; ptr->WidthC = V_WidthC; ptr->WidthD = V_WidthD; } ptr->TimeLastUpdated = NOW; Length = V_LenA + V_LenB; if(Length > 0) { Dimensions[0] = -V_WidthC; Dimensions[1] = -V_LenB; Dimensions[2] = V_WidthD; Dimensions[3] = -V_LenB; Dimensions[4] = V_WidthD; Dimensions[5] = V_LenA - Length / 5; Dimensions[6] = ((V_WidthC + V_WidthD) / 2) - V_WidthC; Dimensions[7] = V_LenA; Dimensions[8] = -V_WidthC; Dimensions[9] = V_LenA - Length / 5; } MaintainVesselDatabase(UserID, Type); return; } } // Not found if (TargetCount == 0) TargetRecords=(struct TARGETRECORD **)malloc(sizeof(void *)); else TargetRecords=(struct TARGETRECORD **)realloc(TargetRecords,(TargetCount+1)*sizeof(void *)); ptr=(struct TARGETRECORD *)malloc(sizeof(struct TARGETRECORD)); memset(ptr, 0, sizeof(struct TARGETRECORD)); if (ptr == NULL) return; TargetRecords[TargetCount]=ptr; ptr->ID = UserID; if (Type == 'A') ptr->Class='A'; else ptr->Class='B'; ptr->TimeAdded = NOW; TargetCount++; sprintf(ptr->name,"%d",UserID); memcpy(ptr->name, V_Name,21); ProcessAISVesselDetails(Type); return; } void ProcessAISNavAidMessage() { int i; struct NAVAIDRECORD * navptr; for (i = 0; i < NavAidCount; i++) { navptr=NavRecords[i]; if (navptr->ID == UserID) { memcpy(navptr->name,V_Name,21); navptr->lat = V_Lat; navptr->Long = V_Lon; navptr->LengthA = V_LenA; navptr->LengthB = V_LenB; navptr->WidthC = V_WidthC; navptr->WidthD = V_WidthD; navptr->NavAidType = NavAidType; navptr->FixType = FixType; navptr->TimeLastUpdated = NOW; NavAidDBChanged = 1; return; } } // Not found, so add if (NavAidCount == 0) NavRecords=(struct NAVAIDRECORD **)malloc(sizeof(void *)); else NavRecords=(struct NAVAIDRECORD **)realloc(NavRecords,(NavAidCount+1)*sizeof(void *)); navptr=(struct NAVAIDRECORD *)malloc(sizeof(struct NAVAIDRECORD)); if (navptr == NULL) return; NavRecords[NavAidCount]=navptr; navptr->ID = UserID; navptr->TimeAdded = NOW; NavAidCount++; ProcessAISNavAidMessage(); return; } static double Distance(double lah, double loh, double laa, double loa) { //' Our Lat, Long other Lat,Long /* '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); return 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))); } static double Bearing(double lat1, double lon1, double lat2, double lon2) { //' Our Lat, Long other Lat,Long 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; } void AISTimer() { // Entered every minute NOW = time(NULL); CheckAgeofTargets(MaxAge); if (VessselDBChanged) { VessselDBChanged = 0; SaveVesselDataBase(); } if (NavAidDBChanged) { NavAidDBChanged = 0; SaveNavAidDataBase(); } } void ADSBTimer() { // Entered every minute NOW = time(NULL); CheckAgeofPlanes(300); ProcessADSBJSON(ADSBFN); // WriteMiniDump(); if (ADSBHost[0]) { if (ADSBConnected == 0) // Try to connect again _beginthread(ADSBConnect, 0, 0); } } void CheckAgeofTargets(UINT MaxAge) { int i, j=0; struct TARGETRECORD * ptr; for (i = 0; i < TargetCount; i++) { ptr=TargetRecords[i]; if (NOW - ptr->TimeLastUpdated > MaxAge) { free(ptr); // Release Memory Block // Delete Current, and move all others down TargetCount--; while (i < TargetCount) { TargetRecords[i] = TargetRecords[i+1]; i++; } return; // Test rest on next pass } } } void CheckAgeofPlanes(UINT MaxAge) { int i, j=0; struct ADSBRECORD * ptr; for (i = 0; i < ADSBCount; i++) { ptr = ADSBRecords[i]; if (ptr->hex[0] && NOW - ptr->seen_pos > MaxAge) memset(ptr, 0, sizeof(struct ADSBRECORD)); // Zap the record // Release Memory Block } } int GetAISPageInfo(char * Buffer, int ais, int adsb) { int i; int Len = 0; struct NAVAIDRECORD * navptr; struct TARGETRECORD * ptr; char Line[280]; int Age; char * pType; char * Name; char nameString[32]; struct tm * TM; char TimeAdded[50]; char TimeHeard[50]; char * p; char DestBuffer[128] = ""; NOW = time(NULL); if (adsb) ProcessADSBJSON(ADSBFN); if (Lat != 0.0 && Lon != 0) Len += sprintf(&Buffer[Len],"H,%f,%f,\r\n|", Lat, Lon); if (ais) { for (i = 0; i < NavAidCount; i++) { navptr=NavRecords[i]; if (strchr(navptr->name, '>') || strchr(navptr->name, '<')) continue; TM = gmtime(&navptr->TimeAdded); sprintf(TimeAdded, "
Created %02d:%02d %02d/%02d/%04d", TM->tm_hour, TM->tm_min, TM->tm_mday, TM->tm_mon + 1, TM->tm_year + 1900); TM = gmtime(&navptr->TimeLastUpdated); sprintf(TimeHeard, "
Last Heard %02d:%02d %02d/%02d/%04d", TM->tm_hour, TM->tm_min, TM->tm_mday, TM->tm_mon + 1, TM->tm_year + 1900); Len += sprintf(&Buffer[Len],"N,%f,%f,%s%s%s,\r\n|", navptr->lat, navptr->Long, navptr->name, TimeAdded, TimeHeard); } // Vessels for (i = 0; i < TargetCount; i++) { ptr = TargetRecords[i]; Age = NOW - ptr->TimeLastUpdated; if (Age > 3600) continue; if (ptr->Type >= 30 && ptr->Type < 40) pType=VesselType3[ptr->Type-30]; else if (ptr->Type >= 50 && ptr->Type < 60) pType=VesselType5[ptr->Type-50]; else pType=VesselType[ptr->Type/10]; if (ptr->name[0]) Name = ptr->name; else { sprintf(nameString, "%d", ptr->ID); Name = nameString; } // Dest may contain comma strcpy(DestBuffer, ptr->Dest); while (p = strchr(DestBuffer, ',')) { memmove(p + 4, p, strlen(p)); memcpy(p, ",", 5); } sprintf(Line,"%s
Dest %s
MMSI %d
Callsign %s
Type %d
Heading %2.0f° ROT %2.1f
Dimensions %d %d %d %d
%2.1f kts %2.0f°
Age %d", Name, DestBuffer, ptr->ID, ptr->Callsign, ptr->Type , ptr->Heading, ptr->ROT, ptr->LengthA, ptr->LengthB, ptr->WidthC, ptr->WidthD, ptr->speed, ptr->course, Age); Len += sprintf(&Buffer[Len],"V,%.4f,%.4f,%s,%.0f,%.1f,%d\r\n|", ptr->lat, ptr->Long, Line, ptr->course, ptr->speed, Age); if (ptr->TrackTime[0]) // Have trackpoints { int n = ptr->Trackptr; int i; // 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]); } n++; if (n == TRACKPOINTS) n = 0; } Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->lat, ptr->Long); //Add current position to end of track } } // SAR Vessels for (i = 0; i < SARCount; i++) { struct SARRECORD * ptr = SARRecords[i]; Age = NOW - ptr->TimeLastUpdated; if (Age > 3600) continue; if (ptr->name[0]) Name = ptr->name; else { sprintf(nameString, "%d", ptr->ID); Name = nameString; } sprintf(Line,"%s
Dest %s
MMSI %d
%2.1f kts %2.0f°
Age %d", Name, ptr->Dest, ptr->ID, ptr->speed, ptr->course, Age); Len += sprintf(&Buffer[Len],"S,%.4f,%.4f,%s,%.0f,%.0f,%d\r\n|", ptr->lat, ptr->Long, Line, ptr->course, ptr->speed, Age); if (ptr->TrackTime[0]) // Have trackpoints { int n = ptr->Trackptr; int i; // 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]); } n++; if (n == TRACKPOINTS) n = 0; } Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->lat, ptr->Long); //Add current position to end of track } } } if (adsb) { // Aircraft for (i = 0; i < ADSBCount; i++) { struct ADSBRECORD * ptr = ADSBRecords[i]; Age = NOW - ptr->seen_pos; if (Age > 600) continue; if (ptr->lat == 0.0 && ptr->lon == 0.0) continue; sprintf(Line,"%s
Flight %s
Heading %d°
Speed %d kts
Altitude %d feet
rssi %d
Age %d", ptr->hex, ptr->flight, ptr->track, ptr->speed, ptr->altitude, ptr->rssi, Age); Len += sprintf(&Buffer[Len],"P,%.4f,%.4f,%s,%s,%d,%d,%d\r\n|", ptr->lat, ptr->lon, Line, ptr->flight, ptr->track, ptr->speed, Age); if (ptr->TrackTime[0]) // Have trackpoints { int n = ptr->Trackptr; int i; // 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]); } n++; if (n == TRACKPOINTS) n = 0; } Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->lat, ptr->lon); //Add current position to end of track } } } return Len; } /* { "now" : 1634055090.3, "messages" : 28814, "aircraft" : [ {"hex":"4b8467","squawk":"1146","flight":"RUN410 ","lat":58.554749,"lon":-7.728350,"nucp":7,"seen_pos":0.0,"altitude":37000,"vert_rate":0,"track":111,"speed":537,"messages":197,"seen":0.0,"rssi":-32.7}, {"hex":"abed10","squawk":"1170","flight":"FDX4 ","lat":57.969498,"lon":-7.888449,"nucp":7,"seen_pos":7.2,"altitude":37000,"vert_rate":0,"track":137,"speed":553,"messages":588,"seen":0.1,"rssi":-34.6}, {"hex":"4007dd","squawk":"2000","altitude":1825,"messages":180,"seen":37.8,"rssi":-33.4} ] } */ void ProcessADSBMessage(struct ADSBRECORD * plane); void ProcessADSBLine(char * Msg, int Len); void ProcessADSBJSON(char * FN) { FILE *file; char buf[2048]; char *token; struct ADSBRECORD plane; time_t ReportTime; // Testing, read dump1090 file if (ADSBHost[0]) return; // Using TCP if ((file = fopen(FN,"r")) == NULL) return; fgets(buf, 2048, file); // now ReportTime = atoi(&buf[9]); fgets(buf, 2048, file); // messages fgets(buf, 2048, file); // aircraft while (1) { if (fgets(buf, 2048, file) == 0) { fclose(file); return; } if (strchr(buf, ']')) { fclose(file); return; } memset(&plane, 0, sizeof(struct ADSBRECORD)); if (token = strstr(buf, "hex")) { memcpy(plane.hex, &token[6], 7); strlop(plane.hex, '"'); } if (token = strstr(buf, "squawk")) { memcpy(plane.squawk, &token[9], 4); } if (token = strstr(buf, "flight")) { memcpy(plane.flight, &token[9], 8); } if (token = strstr(buf, "lat")) { plane.lat = atof(&token[5]); } if (token = strstr(buf, "lon")) { plane.lon = atof(&token[5]); } if (token = strstr(buf, "seen_pos")) { plane.seen_pos = ReportTime - atoi(&token[10]); } if (token = strstr(buf, "altitude")) { plane.altitude = atoi(&token[10]); } if (token = strstr(buf, "vert_rate")) { plane.vert_rate = atoi(&token[11]); } if (token = strstr(buf, "track")) { plane.track = atoi(&token[7]); } if (token = strstr(buf, "speed")) { plane.speed = atoi(&token[7]); } if (token = strstr(buf, "messages")) { plane.messages = atoi(&token[10]); } if (token = strstr(buf, "seen")) { plane.seen = ReportTime - atoi(&token[6]); } if (token = strstr(buf, "rssi")) { plane.rssi = atoi(&token[6]); } ProcessADSBMessage(&plane); } } void ProcessADSBMessage(struct ADSBRECORD * plane) { int i; struct ADSBRECORD * ptr; struct ADSBRECORD * spare = 0; // Pointer to a cleared record for (i = 0; i < ADSBCount; i++) { ptr = ADSBRecords[i]; if (strcmp(ptr->hex, plane->hex) == 0) { if (plane->squawk[0]) strcpy(ptr->squawk,plane->squawk); if (plane->flight[0]) strcpy(ptr->flight, plane->flight); if (plane->lat) ptr->lat = plane->lat; if (plane->lon) ptr->lon = plane->lon; if (plane->altitude) ptr->altitude = plane->altitude; if (plane->seen_pos) ptr->seen_pos = plane->seen_pos; if (plane->vert_rate) ptr->vert_rate = plane->vert_rate; if (plane->track) ptr->track = plane->track; if (plane->speed) ptr->speed = plane->speed; if (plane->messages) ptr->messages = plane->messages; if (plane->seen) ptr->seen = plane->seen; if (plane->rssi) ptr->rssi = plane->rssi; ptr->TimeLastUpdated = ptr->seen; if (ptr->lat == 0.0) return; // Save Track Point if ((NOW - ptr->LastTime) < 15) // Not More that once per 15 secs return; ptr->LastCourse = ptr->track; ptr->LastSpeed = ptr->speed; ptr->LastTime = NOW; ptr->Lastlat = ptr->lat; ptr->LastLong = ptr->lon; ptr->LatTrack[ptr->Trackptr] = ptr->lat; ptr->LonTrack[ptr->Trackptr] = ptr->lon; ptr->TrackTime[ptr->Trackptr] = NOW; ptr->Trackptr++; if (ptr->Trackptr == TRACKPOINTS) ptr->Trackptr = 0; return; } if (ptr->hex[0] == 0 && spare == 0) //Zapped record spare = ptr; } // Not found, if got a freed entry use it, elsee add new one if (spare) ptr = spare; else { if (ADSBCount == 0) ADSBRecords = (struct ADSBRECORD **)malloc(sizeof(void *)); else ADSBRecords = (struct ADSBRECORD **)realloc(ADSBRecords,(ADSBCount+1)*sizeof(void *)); ptr = (struct ADSBRECORD *)zalloc(sizeof(struct ADSBRECORD)); if (ptr == NULL) return; ADSBRecords[ADSBCount] = ptr; ADSBCount++; } strcpy(ptr->hex, plane->hex); ptr->TimeAdded = NOW; ProcessADSBMessage(plane); // Reenter to save details return; } // Thread to process ADS-B messages from dump1090 void ProcessADSBLine(char * Msg, int Len) { int i; struct ADSBRECORD * rec; struct ADSBRECORD * spare = 0; // Pointer to a cleared record char * p[22] = {0}; char * ptr; char Type; char hex[10]; //MSG,3,111,11111,394A05,111111,2021/10/13,07:56:46.075,2021/10/13,07:56:46.128,,37000,,,59.11278,-6.53052,,,,,,0 if (memcmp(Msg, "MSG,", 4) != 0) return; // Simplest way to process comma separated with null fields is strlop; ptr = &Msg[4]; for (i = 0; i < 21; i++) { p[i] = ptr; ptr = strlop(ptr, ','); } if (p[20] == 0) return; // Should have 21 params strcpy(hex, p[3]); // identifier for (i = 0; i < ADSBCount; i++) { rec = ADSBRecords[i]; if (strcmp(rec->hex, hex) == 0) goto Found; if (rec->hex[0] == 0 && spare == 0) //Zapped record spare = rec; } if (spare) rec = spare; else { // Not found, so add if (ADSBCount == 0) ADSBRecords = (struct ADSBRECORD **)malloc(sizeof(void *)); else ADSBRecords = (struct ADSBRECORD **)realloc(ADSBRecords,(ADSBCount+1) * sizeof(void *)); rec = (struct ADSBRECORD *)zalloc(sizeof(struct ADSBRECORD)); if (rec == NULL) return; ADSBRecords[ADSBCount] = rec; ADSBCount++; } strcpy(rec->hex, hex); rec->TimeAdded = NOW; Found: rec->seen = NOW; rec->messages++; Type = atoi(p[0]); switch (Type) { case 1: strcpy(rec->flight, p[9]); return; case 2: rec->altitude = atoi(p[10]); rec->speed = atoi(p[11]); rec->track = atoi(p[12]); rec->lat = atof(p[13]); rec->lon = atof(p[14]); rec->seen_pos = NOW; break; case 3: // Position rec->altitude = atoi(p[10]); rec->lat = atof(p[13]); rec->lon = atof(p[14]); rec->seen_pos = NOW; break; case 4: rec->speed = atoi(p[11]); rec->track = atoi(p[12]); rec->vert_rate = atoi(p[15]); return; case 5: return; case 6: strcpy(rec->squawk, p[16]); return; case 7: case 8: return; } // Recs with position get here if (rec->lat == 0.0) return; // Save Track Point // if ((NOW - rec->LastTime) < 15) // Not More that once per 15 secs // return; rec->LastCourse = rec->track; rec->LastSpeed = rec->speed; rec->LastTime = NOW; rec->Lastlat = rec->lat; rec->LastLong = rec->lon; rec->LatTrack[rec->Trackptr] = rec->lat; rec->LonTrack[rec->Trackptr] = rec->lon; rec->TrackTime[rec->Trackptr] = NOW; rec->Trackptr++; if (rec->Trackptr == TRACKPOINTS) rec->Trackptr = 0; } static VOID ProcessADSBData(SOCKET TCPSock) { char Buffer[66000]; char Msg[1024]; int len; char * ptr; char * Lastptr; // Although it is possible that a packet will be split by a partial read. // it is so unlikely with a 65536 buffer that I'll just accept the loss len = recv(TCPSock, Buffer, 65536, 0); if (len <= 0) { closesocket(TCPSock); ADSBConnected = FALSE; return; } if (len > 5000) Debugprintf("ADSB Len %d", len); ptr = Lastptr = Buffer; Buffer[len] = 0; while (len > 0) { ptr = strchr(Lastptr, 10); if (ptr) { size_t Len = ptr - Lastptr - 1; if (Len > 1020) return; memcpy(Msg, Lastptr, Len); Msg[Len++] = 13; Msg[Len++] = 10; Msg[Len] = 0; ProcessADSBLine(Msg, Len); Lastptr = ptr + 1; len -= (int)Len; } else return; } } #define SD_RECEIVE 0x00 #define SD_SEND 0x01 #define SD_BOTH 0x02 static VOID ADSBConnect(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; int addrlen=sizeof(sinx); if (ADSBHost[0] == 0) return; destaddr.sin_addr.s_addr = inet_addr(ADSBHost); destaddr.sin_family = AF_INET; destaddr.sin_port = htons(ADSBPort); TCPSock = socket(AF_INET,SOCK_STREAM,0); if (TCPSock == INVALID_SOCKET) { return; } setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); ADSBConnected = TRUE; // So we don't try to reconnect while waiting if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) { // // Connected successful // ioctl(TCPSock, FIONBIO, ¶m); } else { err=WSAGetLastError(); #ifdef LINBPQ printf("Connect Failed for ADSB socket - error code = %d\n", err); #else Debugprintf("Connect Failed for ADSB socket - error code = %d", err); #endif closesocket(TCPSock); ADSBConnected = FALSE; return; } ADSBConnected = TRUE; while (TRUE) { FD_ZERO(&readfs); FD_ZERO(&errorfs); FD_SET(TCPSock,&readfs); FD_SET(TCPSock,&errorfs); timeout.tv_sec = 60; 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)) { ProcessADSBData(TCPSock); } if (FD_ISSET(TCPSock, &errorfs)) { Lost: #ifdef LINBPQ printf("ADSB Connection lost\n"); #endif closesocket(TCPSock); ADSBConnected = FALSE; return; } } else { // 60 secs without data. Shouldn't happen shutdown(TCPSock, SD_BOTH); Sleep(100); closesocket(TCPSock); ADSBConnected = FALSE; return; } } } //http://fr24.com/+selected.flight //http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?flightNumber='+selected.flight