41#include <wx/datetime.h>
45#include <wx/textfile.h>
47#include <wx/tokenzr.h>
48#include <wx/filename.h>
52#include "rapidjson/document.h"
53#include "rapidjson/writer.h"
54#include "rapidjson/stringbuffer.h"
56#include "model/ais_decoder.h"
58#include "model/meteo_points.h"
59#include "model/ais_target_data.h"
61#include "model/config_vars.h"
62#include "model/geodesic.h"
63#include "model/georef.h"
64#include "model/idents.h"
65#include "model/multiplexer.h"
66#include "model/navutil_base.h"
67#include "model/own_ship.h"
68#include "model/route_point.h"
69#include "model/select.h"
70#include "SoundFactory.h"
71#include "model/track.h"
75static const long long lNaN = 0xfff8000000000000;
76#define NAN (*(double *)&lNaN)
79wxEvtHandler *g_pais_alert_dialog_active;
83bool g_bUseOnlyConfirmedAISName;
84wxString GetShipNameFromFile(
int);
85wxString AISTargetNameFileName;
86bool isBuoyMmsi(
const int);
108EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
109EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
112static const
double ms_to_knot_factor = 1.9438444924406;
118static
bool b_firstrx;
119static
int first_rx_ticks;
121static
double arpa_ref_hdg = NAN;
123static inline
double GeodesicRadToDeg(
double rads) {
124 return rads * 180.0 / M_PI;
127static inline double MS2KNOTS(
double ms) {
return ms * 1.9438444924406; }
129int AisMeteoNewMmsi(
int,
int,
int,
int,
int);
133 AIS_Target_Name_Hash *AISTargetNamesC,
134 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
137 : m_signalk_selfid(
""), m_callbacks(callbacks) {
139 AISTargetNamesC =
new AIS_Target_Name_Hash;
140 AISTargetNamesNC =
new AIS_Target_Name_Hash;
142 if (g_benableAISNameCache) {
143 if (wxFileName::FileExists(AISTargetNameFileName)) {
145 if (infile.Open(AISTargetNameFileName)) {
146 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
147 wxString line = infile.GetFirstLine();
148 while (!infile.Eof()) {
149 if (line.IsSameAs(wxT(
"+++==Confirmed Entry's==+++")))
150 HashFile = AISTargetNamesC;
152 if (line.IsSameAs(wxT(
"+++==Non Confirmed Entry's==+++")))
153 HashFile = AISTargetNamesNC;
155 wxStringTokenizer tokenizer(line, _T(
","));
156 int mmsi = wxAtoi(tokenizer.GetNextToken());
157 wxString name = tokenizer.GetNextToken().Trim();
158 (*HashFile)[mmsi] = name;
161 line = infile.GetNextLine();
168 BuildERIShipTypeHash();
170 g_pais_alert_dialog_active =
nullptr;
171 m_bAIS_Audio_Alert_On =
false;
175 m_bAIS_AlertPlaying =
false;
177 TimerAIS.SetOwner(
this, TIMER_AIS1);
178 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
180 m_ptentative_dsctarget = NULL;
181 m_dsc_timer.SetOwner(
this, TIMER_DSC);
191AisDecoder::~AisDecoder(
void) {
200 if (outfile.Open(AISTargetNameFileName)) {
202 content = wxT(
"+++==Confirmed Entry's==+++");
203 AIS_Target_Name_Hash::iterator it;
204 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
205 content.append(_T(
"\r\n"));
206 content.append(wxString::Format(wxT(
"%i"), it->first));
207 content.append(_T(
",")).append(it->second);
209 content.append(_T(
"\r\n"));
210 content.append(_T(
"+++==Non Confirmed Entry's==+++"));
211 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
212 content.append(_T(
"\r\n"));
213 content.append(wxString::Format(wxT(
"%i"), it->first));
214 content.append(_T(
",")).append(it->second);
216 outfile.Write(content);
220 AISTargetNamesC->clear();
221 delete AISTargetNamesC;
222 AISTargetNamesNC->clear();
223 delete AISTargetNamesNC;
228 m_AIS_Audio_Alert_Timer.Stop();
233 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
235 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
239bool IsTargetOnTheIgnoreList(
const int &mmsi) {
241 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
242 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
244 if (props->m_bignore) {
252void AisDecoder::InitCommListeners(
void) {
255 auto &msgbus = NavMsgBus::GetInstance();
260 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
262 auto ptr = ev.GetSharedPtr();
263 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
264 HandleN0183_AIS(n0183_msg);
269 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
272 auto ptr = ev.GetSharedPtr();
273 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
274 HandleN0183_AIS(n0183_msg);
279 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
281 auto ptr = ev.GetSharedPtr();
282 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
283 HandleN0183_AIS(n0183_msg);
288 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
290 auto ptr = ev.GetSharedPtr();
291 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
292 HandleN0183_AIS(n0183_msg);
297 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
300 auto ptr = ev.GetSharedPtr();
301 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
302 HandleN0183_AIS(n0183_msg);
307 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
309 auto ptr = ev.GetSharedPtr();
310 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
311 HandleN0183_AIS(n0183_msg);
316 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
318 auto ptr = ev.GetSharedPtr();
319 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
320 HandleN0183_AIS(n0183_msg);
325 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
327 auto ptr = ev.GetSharedPtr();
328 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
329 HandleN0183_AIS(n0183_msg);
334 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
336 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
341 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
342 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
344 HandleN2K_129038(UnpackEvtPointer<Nmea2000Msg>(ev));
349 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
350 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
352 HandleN2K_129039(UnpackEvtPointer<Nmea2000Msg>(ev));
357 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
358 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
360 HandleN2K_129041(UnpackEvtPointer<Nmea2000Msg>(ev));
365 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
366 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
368 HandleN2K_129794(UnpackEvtPointer<Nmea2000Msg>(ev));
373 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
374 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
376 HandleN2K_129809(UnpackEvtPointer<Nmea2000Msg>(ev));
381 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
382 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
384 HandleN2K_129810(UnpackEvtPointer<Nmea2000Msg>(ev));
389 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
390 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
392 HandleN2K_129793(UnpackEvtPointer<Nmea2000Msg>(ev));
396bool AisDecoder::HandleN0183_AIS(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
397 std::string str = n0183_msg->payload;
398 wxString sentence(str.c_str());
399 DecodeN0183(sentence);
404bool AisDecoder::HandleN2K_129038(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
405 std::vector<unsigned char> v = n2k_msg->payload;
408 tN2kAISRepeat Repeat;
419 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
420 tN2kAISTransceiverInformation AISTransceiverInformation;
422 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
423 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
424 NavStat, AISTransceiverInformation)) {
427 if (IsTargetOnTheIgnoreList(mmsi))
return false;
431 long mmsi_long = mmsi;
432 std::shared_ptr<AisTargetData> pTargetData = 0;
433 bool bnewtarget =
false;
435 auto it = AISTargetList.find(mmsi);
436 if (it == AISTargetList.end())
438 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
442 pTargetData = it->second;
445 wxDateTime now = wxDateTime::Now();
449 pTargetData->MMSI = mmsi;
450 pTargetData->MID = MessageID;
451 pTargetData->MMSI = mmsi;
452 pTargetData->Class = AIS_CLASS_A;
454 if (97 == pTargetData->MMSI / 10000000) {
455 pTargetData->Class = AIS_SART;
457 pTargetData->StaticReportTicks = now.GetTicks();
459 pTargetData->NavStatus = (ais_nav_status)NavStat;
460 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
461 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
462 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
463 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
464 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
466 pTargetData->ROTAIS = ROT;
468 double rot_dir = 1.0;
480 pTargetData->b_OwnShip =
481 AISTransceiverInformation ==
482 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
483 pTargetData->b_active =
true;
484 pTargetData->b_lost =
false;
485 pTargetData->b_positionOnceValid =
true;
486 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
487 pTargetData->PositionReportTicks = now.GetTicks();
489 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
490 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
499bool AisDecoder::HandleN2K_129039(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
500 std::vector<unsigned char> v = n2k_msg->payload;
515 tN2kAISRepeat Repeat;
525 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
526 tN2kAISTransceiverInformation AISTransceiverInformation;
528 bool DSC, Band, Msg22, State, Display;
531 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
532 Accuracy, RAIM, Seconds, COG, SOG,
533 AISTransceiverInformation, Heading, Unit, Display, DSC,
534 Band, Msg22, Mode, State)) {
537 if (IsTargetOnTheIgnoreList(mmsi))
return false;
541 long mmsi_long = mmsi;
542 std::shared_ptr<AisTargetData> pTargetData = 0;
543 bool bnewtarget =
false;
545 auto it = AISTargetList.find(mmsi);
546 if (it == AISTargetList.end())
548 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
552 pTargetData = it->second;
555 wxDateTime now = wxDateTime::Now();
559 pTargetData->MMSI = mmsi;
560 pTargetData->MID = MessageID;
561 if (!isBuoyMmsi(mmsi))
562 pTargetData->Class = AIS_CLASS_B;
564 pTargetData->Class = AIS_BUOY;
566 pTargetData->NavStatus = (ais_nav_status)NavStat;
567 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
568 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
569 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
570 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
571 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
573 pTargetData->b_positionOnceValid =
true;
574 pTargetData->b_active =
true;
575 pTargetData->b_lost =
false;
576 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
577 pTargetData->PositionReportTicks = now.GetTicks();
578 pTargetData->b_OwnShip =
579 AISTransceiverInformation ==
580 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
582 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
583 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
593bool AisDecoder::HandleN2K_129041(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
594 std::vector<unsigned char> v = n2k_msg->payload;
596 tN2kAISAtoNReportData data;
599 struct tN2kAISAtoNReportData {
601 tN2kAISRepeat Repeat;
610 double PositionReferenceStarboard ;
611 double PositionReferenceTrueNorth;
612 tN2kAISAtoNType AtoNType;
613 bool OffPositionIndicator;
614 bool VirtualAtoNFlag;
615 bool AssignedModeFlag;
616 tN2kGNSStype GNSSType;
618 tN2kAISTransceiverInformation AISTransceiverInformation;
619 char AtoNName[34 + 1];
622 if (ParseN2kPGN129041(v, data)) {
623 int mmsi = data.UserID;
625 if (IsTargetOnTheIgnoreList(mmsi))
return false;
629 long mmsi_long = mmsi;
630 std::shared_ptr<AisTargetData> pTargetData = 0;
631 bool bnewtarget =
false;
633 auto it = AISTargetList.find(mmsi);
634 if (it == AISTargetList.end())
636 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
640 pTargetData = it->second;
644 pTargetData->MMSI = mmsi;
646 wxDateTime now = wxDateTime::Now();
649 int offpos = data.OffPositionIndicator;
650 int virt = data.VirtualAtoNFlag;
653 pTargetData->NavStatus = ATON_VIRTUAL;
655 pTargetData->NavStatus = ATON_REAL;
657 pTargetData->m_utc_sec = data.Seconds;
659 if (pTargetData->m_utc_sec <= 59) {
660 pTargetData->NavStatus += 1;
661 if (offpos) pTargetData->NavStatus += 1;
664 data.AtoNName[34] = 0;
665 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
666 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
667 pTargetData->b_nameValid =
true;
668 pTargetData->MID = 124;
670 pTargetData->ShipType = data.AtoNType;
671 pTargetData->Class = AIS_ATON;
673 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
674 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
675 pTargetData->b_positionDoubtful =
false;
676 pTargetData->b_positionOnceValid =
true;
677 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
678 pTargetData->PositionReportTicks = now.GetTicks();
682 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
683 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
685 touch_state.Notify();
692bool AisDecoder::HandleN2K_129794(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
693 std::vector<unsigned char> v = n2k_msg->payload;
696 tN2kAISRepeat Repeat;
700 char Name[SHIP_NAME_LEN];
709 char Destination[DESTINATION_LEN];
710 tN2kAISVersion AISversion;
711 tN2kGNSStype GNSStype;
713 tN2kAISTranceiverInfo AISinfo;
715 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
716 VesselType, Length, Beam, PosRefStbd, PosRefBow,
717 ETAdate, ETAtime, Draught, Destination, AISversion,
718 GNSStype, DTE, AISinfo)) {
721 if (IsTargetOnTheIgnoreList(mmsi))
return false;
725 long mmsi_long = mmsi;
726 std::shared_ptr<AisTargetData> pTargetData = 0;
727 bool bnewtarget =
false;
729 auto it = AISTargetList.find(mmsi);
730 if (it == AISTargetList.end())
732 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
736 pTargetData = it->second;
740 pTargetData->MMSI = mmsi;
741 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
742 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
743 Name[
sizeof(Name) - 1] = 0;
744 pTargetData->b_nameValid =
true;
745 pTargetData->MID = 124;
747 pTargetData->b_OwnShip =
749 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
751 pTargetData->DimA = PosRefBow;
752 pTargetData->DimB = Length - PosRefBow;
753 pTargetData->DimC = Beam - PosRefStbd;
754 pTargetData->DimD = PosRefStbd;
755 pTargetData->Draft = Draught;
756 pTargetData->IMO = IMOnumber;
757 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
758 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
759 pTargetData->ShipType = (
unsigned char)VesselType;
760 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
761 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
762 Destination[
sizeof(Destination) - 1] = 0;
764 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
765 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
766 wxDateTime t((time_t)secs);
768 wxDateTime tz = t.ToUTC();
769 pTargetData->ETA_Mo = tz.GetMonth() + 1;
770 pTargetData->ETA_Day = tz.GetDay();
771 pTargetData->ETA_Hr = tz.GetHour();
772 pTargetData->ETA_Min = tz.GetMinute();
776 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
777 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
785bool AisDecoder::HandleN2K_129809(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
786 std::vector<unsigned char> v = n2k_msg->payload;
789 tN2kAISRepeat Repeat;
793 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
796 if (IsTargetOnTheIgnoreList(mmsi))
return false;
800 long mmsi_long = mmsi;
801 std::shared_ptr<AisTargetData> pTargetData = 0;
802 bool bnewtarget =
false;
804 auto it = AISTargetList.find(mmsi);
805 if (it == AISTargetList.end())
807 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
811 pTargetData = it->second;
815 pTargetData->MMSI = mmsi;
816 Name[
sizeof(Name) - 1] = 0;
817 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
818 pTargetData->b_nameValid =
true;
819 pTargetData->MID = 124;
821 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
822 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
832bool AisDecoder::HandleN2K_129810(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
833 std::vector<unsigned char> v = n2k_msg->payload;
836 tN2kAISRepeat Repeat;
845 uint32_t MothershipID;
847 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
848 Callsign, Length, Beam, PosRefStbd, PosRefBow,
852 if (IsTargetOnTheIgnoreList(mmsi))
return false;
856 long mmsi_long = mmsi;
857 std::shared_ptr<AisTargetData> pTargetData = 0;
858 bool bnewtarget =
false;
860 auto it = AISTargetList.find(mmsi);
861 if (it == AISTargetList.end())
863 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
867 pTargetData = it->second;
871 pTargetData->MMSI = mmsi;
872 pTargetData->DimA = PosRefBow;
873 pTargetData->DimB = Length - PosRefBow;
874 pTargetData->DimC = Beam - PosRefStbd;
875 pTargetData->DimD = PosRefStbd;
876 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
877 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
878 pTargetData->ShipType = (
unsigned char)VesselType;
880 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
881 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
890bool AisDecoder::HandleN2K_129793(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
891 std::vector<unsigned char> v = n2k_msg->payload;
894 tN2kAISRepeat Repeat;
898 unsigned int SecondsSinceMidnight;
899 unsigned int DaysSinceEpoch;
901 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
902 SecondsSinceMidnight, DaysSinceEpoch)) {
903 wxDateTime now = wxDateTime::Now();
909 long mmsi_long = mmsi;
910 std::shared_ptr<AisTargetData> pTargetData = 0;
911 bool bnewtarget =
false;
913 auto it = AISTargetList.find(mmsi);
914 if (it == AISTargetList.end())
916 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
920 pTargetData = it->second;
924 pTargetData->MMSI = mmsi;
925 pTargetData->Class = AIS_BASE;
927 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
928 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
929 pTargetData->b_positionDoubtful =
false;
930 pTargetData->b_positionOnceValid =
true;
931 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
932 pTargetData->PositionReportTicks = now.GetTicks();
936 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
937 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
953void AisDecoder::BuildERIShipTypeHash(
void) {
954 make_hash_ERI(8000, _(
"Vessel, type unknown"));
955 make_hash_ERI(8150, _(
"Freightbarge"));
956 make_hash_ERI(8160, _(
"Tankbarge"));
957 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
958 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
959 make_hash_ERI(8430, _(
"Pushboat, single"));
960 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
961 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
962 make_hash_ERI(8490, _(
"Bunkership"));
963 make_hash_ERI(8010, _(
"Motor freighter"));
964 make_hash_ERI(8020, _(
"Motor tanker"));
965 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
966 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
967 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
968 make_hash_ERI(8030, _(
"Container vessel"));
969 make_hash_ERI(8040, _(
"Gas tanker"));
970 make_hash_ERI(8050, _(
"Motor freighter, tug"));
971 make_hash_ERI(8060, _(
"Motor tanker, tug"));
972 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
973 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
974 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
975 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
976 make_hash_ERI(8110, _(
"Tug, freighter"));
977 make_hash_ERI(8120, _(
"Tug, tanker"));
978 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
979 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
980 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
981 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
982 make_hash_ERI(8170, _(
"Freightbarge with containers"));
983 make_hash_ERI(8180, _(
"Tankbarge, gas"));
984 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
985 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
986 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
987 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
988 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
989 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
990 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
991 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
992 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
993 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
995 _(
"Pushtow, two barges at least one tanker or gas barge"));
997 _(
"Pushtow, three barges at least one tanker or gas barge"));
999 _(
"Pushtow, four barges at least one tanker or gas barge"));
1001 _(
"Pushtow, five barges at least one tanker or gas barge"));
1003 _(
"Pushtow, six barges at least one tanker or gas barge"));
1005 _(
"Pushtow, seven barges at least one tanker or gas barge"));
1007 _(
"Pushtow, eight barges at least one tanker or gas barge"));
1009 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
1010 make_hash_ERI(8400, _(
"Tug, single"));
1011 make_hash_ERI(8410, _(
"Tug, one or more tows"));
1012 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
1013 make_hash_ERI(8430, _(
"Pushboat, single"));
1014 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
1015 make_hash_ERI(8441, _(
"Ferry"));
1016 make_hash_ERI(8442, _(
"Red cross ship"));
1017 make_hash_ERI(8443, _(
"Cruise ship"));
1018 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
1019 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
1020 "cable-ship, buoy-ship, dredge"));
1021 make_hash_ERI(8480, _(
"Fishing boat"));
1022 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
1023 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
1024 make_hash_ERI(1510, _(
"Unit carrier maritime"));
1025 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
1026 make_hash_ERI(1530, _(
"Tanker"));
1027 make_hash_ERI(1540, _(
"Liquified gas tanker"));
1028 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
1029 make_hash_ERI(1900, _(
"Fast ship"));
1030 make_hash_ERI(1910, _(
"Hydrofoil"));
1036void AisDecoder::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg) {
1037 rapidjson::Document root;
1039 root.Parse(sK_msg->raw_message);
1041 if (root.HasParseError())
return;
1043 if (root.HasMember(
"self")) {
1049 if (m_signalk_selfid.IsEmpty()) {
1054 int meteo_SiteID = 0;
1055 if (root.HasMember(
"context") && root[
"context"].IsString()) {
1056 wxString context = root[
"context"].GetString();
1057 if (context == m_signalk_selfid) {
1059 wxLogMessage(_T(
"** Ignore context own ship.."));
1063 wxString mmsi_string;
1064 if (context.StartsWith(_T(
"vessels.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1065 context.StartsWith(_T(
"atons.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1066 context.StartsWith(_T(
"aircraft.urn:mrn:imo:mmsi:"), &mmsi_string)) {
1069 if (mmsi_string.ToLong(&mmsi)) {
1074 }
else if (context.StartsWith(_T(
"meteo.urn:mrn:imo:mmsi:"),
1077 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
1078 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
1081 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
1092 if (g_pMUX && g_pMUX->IsLogActive()) {
1094 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
1095 std::string source = sK_msg->source->to_string();
1100 if (IsTargetOnTheIgnoreList(mmsi))
return;
1105 writer.
Write(root, dbg);
1107 wxString msg( _T(
"AisDecoder::OnEvtSignalK: ") );
1111 std::shared_ptr<AisTargetData> pTargetData = 0;
1112 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1113 bool bnewtarget =
false;
1114 int last_report_ticks;
1116 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
1119 pTargetData->MMSI = mmsi;
1120 getMmsiProperties(pTargetData);
1121 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
1122 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
1123 itr != root[
"updates"].End(); ++itr) {
1124 handleUpdate(pTargetData, bnewtarget, *itr);
1129 if (97 == mmsi / 10000000) {
1130 pTargetData->Class = AIS_SART;
1131 }
else if (1994 == mmsi / 100000) {
1133 pTargetData->Class = AIS_METEO;
1134 pTargetData->met_data.original_mmsi = origin_mmsi;
1135 pTargetData->met_data.stationID = meteo_SiteID;
1138 wxString met_name = pTargetData->ShipName;
1139 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
1142 s_id << meteo_SiteID;
1143 id1 = wxAtoi(s_id.Mid(1, 3));
1144 id2 = wxAtoi(s_id.Mid(4, 3));
1145 met_name =
"METEO ";
1146 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
1147 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
1149 pTargetData->b_nameValid =
true;
1150 pTargetData->MID = 123;
1151 pTargetData->COG = -1.;
1152 pTargetData->HDG = 511;
1153 pTargetData->SOG = -1.;
1154 pTargetData->b_NoTrack =
true;
1155 pTargetData->b_show_track =
false;
1157 pTargetData->b_OwnShip =
false;
1158 AISTargetList[pTargetData->MMSI] = pTargetData;
1162void AisDecoder::handleUpdate(std::shared_ptr<AisTargetData> pTargetData,
1163 bool bnewtarget,
const rapidjson::Value &update) {
1164 wxString sfixtime =
"";
1166 if (update.HasMember(
"timestamp")) {
1167 sfixtime = update[
"timestamp"].GetString();
1169 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
1170 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
1171 itr != update[
"values"].End(); ++itr) {
1172 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
1175 wxDateTime now = wxDateTime::Now();
1176 pTargetData->m_utc_hour = now.ToUTC().GetHour();
1177 pTargetData->m_utc_min = now.ToUTC().GetMinute();
1178 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
1180 pTargetData->b_active =
true;
1181 pTargetData->b_lost =
false;
1183 if (pTargetData->b_positionOnceValid) {
1184 long mmsi_long = pTargetData->MMSI;
1186 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
1187 (
void *)mmsi_long, SELTYPE_AISTARGET);
1188 pSel->SetUserData(pTargetData->MMSI);
1190 UpdateOneCPA(pTargetData.get());
1191 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
1194void AisDecoder::updateItem(std::shared_ptr<AisTargetData> pTargetData,
1195 bool bnewtarget,
const rapidjson::Value &item,
1196 wxString &sfixtime)
const {
1197 if (item.HasMember(
"path") && item.HasMember(
"value")) {
1198 const wxString &update_path = item[
"path"].GetString();
1199 if (update_path == _T(
"navigation.position")) {
1200 if (item[
"value"].HasMember(
"latitude") &&
1201 item[
"value"].HasMember(
"longitude")) {
1202 wxDateTime now = wxDateTime::Now();
1204 double lat = item[
"value"][
"latitude"].GetDouble();
1205 double lon = item[
"value"][
"longitude"].GetDouble();
1206 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1207 pTargetData->PositionReportTicks = now.GetTicks();
1208 pTargetData->StaticReportTicks = now.GetTicks();
1209 pTargetData->Lat = lat;
1210 pTargetData->Lon = lon;
1211 pTargetData->b_positionOnceValid =
true;
1212 pTargetData->b_positionDoubtful =
false;
1219 }
else if (update_path == _T(
"navigation.speedOverGround") &&
1220 item[
"value"].IsNumber()) {
1221 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
1222 }
else if (update_path == _T(
"navigation.courseOverGroundTrue") &&
1223 item[
"value"].IsNumber()) {
1224 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1225 }
else if (update_path == _T(
"navigation.headingTrue") &&
1226 item[
"value"].IsNumber()) {
1227 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1228 }
else if (update_path == _T(
"navigation.rateOfTurn") &&
1229 item[
"value"].IsNumber()) {
1230 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
1231 }
else if (update_path == _T(
"design.aisShipType")) {
1232 if (item[
"value"].HasMember(
"id")) {
1233 if (!pTargetData->b_isDSCtarget) {
1234 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1237 }
else if (update_path == _T(
"atonType")) {
1238 if (item[
"value"].HasMember(
"id")) {
1239 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1241 }
else if (update_path == _T(
"virtual")) {
1242 if (item[
"value"].GetBool()) {
1243 pTargetData->NavStatus = ATON_VIRTUAL;
1245 pTargetData->NavStatus = ATON_REAL;
1247 }
else if (update_path == _T(
"offPosition")) {
1248 if (item[
"value"].GetBool()) {
1249 if (ATON_REAL == pTargetData->NavStatus) {
1250 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
1251 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
1252 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
1255 }
else if (update_path == _T(
"design.draft")) {
1256 if (item[
"value"].HasMember(
"maximum") &&
1257 item[
"value"][
"maximum"].IsNumber()) {
1258 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
1259 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
1261 if (item[
"value"].HasMember(
"current") &&
1262 item[
"value"][
"current"].IsNumber()) {
1263 double draft = item[
"value"][
"current"].GetDouble();
1265 pTargetData->Draft = draft;
1266 pTargetData->Euro_Draft = draft;
1269 }
else if (update_path == _T(
"design.length")) {
1270 if (pTargetData->DimB == 0) {
1271 if (item[
"value"].HasMember(
"overall")) {
1272 if (item[
"value"][
"overall"].IsNumber()) {
1273 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
1274 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
1276 pTargetData->DimB = 0;
1279 }
else if (update_path == _T(
"sensors.ais.class")) {
1280 wxString aisclass = item[
"value"].GetString();
1281 if (aisclass == _T(
"A")) {
1282 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
1283 }
else if (aisclass == _T(
"B")) {
1284 if (!pTargetData->b_isDSCtarget) {
1285 if (!isBuoyMmsi(pTargetData->MMSI))
1286 pTargetData->Class = AIS_CLASS_B;
1288 pTargetData->Class = AIS_BUOY;
1291 pTargetData->NavStatus = UNDEFINED;
1293 }
else if (aisclass == _T(
"BASE")) {
1294 pTargetData->Class = AIS_BASE;
1295 }
else if (aisclass == _T(
"ATON")) {
1296 pTargetData->Class = AIS_ATON;
1298 }
else if (update_path == _T(
"sensors.ais.fromBow")) {
1299 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
1300 int length = pTargetData->DimA;
1301 if (item[
"value"].IsNumber()) {
1302 pTargetData->DimA = item[
"value"].GetDouble();
1303 pTargetData->DimB = length - item[
"value"].GetDouble();
1306 }
else if (update_path == _T(
"design.beam")) {
1307 if (pTargetData->DimD == 0) {
1308 if (item[
"value"].IsNumber()) {
1309 pTargetData->Euro_Beam = item[
"value"].GetDouble();
1310 pTargetData->DimC = item[
"value"].GetDouble();
1312 pTargetData->DimD = 0;
1314 }
else if (update_path == _T(
"sensors.ais.fromCenter")) {
1315 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
1316 int beam = pTargetData->DimC;
1317 int center = beam / 2;
1318 if (item[
"value"].IsNumber()) {
1321 pTargetData->DimC = center + item[
"value"].GetDouble();
1322 pTargetData->DimD = beam - pTargetData->DimC;
1325 }
else if (update_path == _T(
"navigation.state")) {
1326 wxString state = item[
"value"].GetString();
1327 if (state == _T(
"motoring")) {
1328 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
1329 }
else if (state == _T(
"anchored")) {
1330 pTargetData->NavStatus = AT_ANCHOR;
1331 }
else if (state == _T(
"not under command")) {
1332 pTargetData->NavStatus = NOT_UNDER_COMMAND;
1333 }
else if (state == _T(
"restricted manouverability")) {
1334 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
1335 }
else if (state == _T(
"constrained by draft")) {
1336 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
1337 }
else if (state == _T(
"moored")) {
1338 pTargetData->NavStatus = MOORED;
1339 }
else if (state == _T(
"aground")) {
1340 pTargetData->NavStatus = AGROUND;
1341 }
else if (state == _T(
"fishing")) {
1342 pTargetData->NavStatus = FISHING;
1343 }
else if (state == _T(
"sailing")) {
1344 pTargetData->NavStatus = UNDERWAY_SAILING;
1345 }
else if (state == _T(
"hazardous material high speed")) {
1346 pTargetData->NavStatus = HSC;
1347 }
else if (state == _T(
"hazardous material wing in ground")) {
1348 pTargetData->NavStatus = WIG;
1349 }
else if (state == _T(
"ais-sart")) {
1350 pTargetData->NavStatus = RESERVED_14;
1352 pTargetData->NavStatus = UNDEFINED;
1354 }
else if (update_path == _T(
"navigation.destination.commonName")) {
1355 const wxString &destination = item[
"value"].GetString();
1356 pTargetData->Destination[0] =
'\0';
1357 strncpy(pTargetData->Destination, destination.c_str(),
1358 DESTINATION_LEN - 1);
1359 }
else if (update_path == _T(
"navigation.specialManeuver")) {
1360 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
1361 pTargetData->IMO < 1) {
1362 const wxString &bluesign = item[
"value"].GetString();
1363 if (_T(
"not engaged") == bluesign) {
1364 pTargetData->blue_paddle = 1;
1366 if (_T(
"engaged") == bluesign) {
1367 pTargetData->blue_paddle = 2;
1369 pTargetData->b_blue_paddle =
1370 pTargetData->blue_paddle == 2 ? true :
false;
1372 }
else if (update_path == _T(
"sensors.ais.designatedAreaCode")) {
1373 if (item[
"value"].GetInt() == 200) {
1374 pTargetData->b_hasInlandDac =
true;
1376 }
else if (update_path == _T(
"sensors.ais.functionalId")) {
1377 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
1379 pTargetData->b_isEuroInland =
true;
1383 }
else if (update_path ==
"environment.date") {
1384 wxString issued = item[
"value"].GetString();
1388 ParseGPXDateTime(tz, issued);
1389 pTargetData->met_data.day = tz.GetDay();
1390 pTargetData->met_data.hour = tz.GetHour();
1391 pTargetData->met_data.minute = tz.GetMinute();
1393 }
else if (update_path ==
"environment.wind.averageSpeed" &&
1394 item[
"value"].IsNumber()) {
1395 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
1396 }
else if (update_path ==
"environment.wind.gust" &&
1397 item[
"value"].IsNumber()) {
1398 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
1399 }
else if (update_path ==
"environment.wind.directionTrue" &&
1400 item[
"value"].IsNumber()) {
1401 pTargetData->met_data.wind_dir =
1402 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1403 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
1404 item[
"value"].IsNumber()) {
1405 pTargetData->met_data.wind_gust_dir =
1406 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1407 }
else if (update_path ==
"environment.outside.temperature" &&
1408 item[
"value"].IsNumber()) {
1409 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
1410 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
1411 item[
"value"].IsNumber()) {
1412 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
1413 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
1414 item[
"value"].IsNumber()) {
1415 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
1416 }
else if (update_path ==
"environment.outside.pressure" &&
1417 item[
"value"].IsNumber()) {
1418 pTargetData->met_data.airpress =
1419 static_cast<int>(item[
"value"].GetDouble() / 100);
1420 }
else if (update_path ==
"environment.water.level" &&
1421 item[
"value"].IsNumber()) {
1422 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
1423 }
else if (update_path ==
"environment.water.current.drift" &&
1424 item[
"value"].IsNumber()) {
1425 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
1426 }
else if (update_path ==
"environment.water.current.set" &&
1427 item[
"value"].IsNumber()) {
1428 pTargetData->met_data.curr_dir =
1429 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1430 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
1431 item[
"value"].IsNumber()) {
1432 pTargetData->met_data.water_lev_trend =
1433 static_cast<int>(item[
"value"].GetDouble());
1434 }
else if (update_path ==
"environment.water.levelTendency") {
1436 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
1437 item[
"value"].IsNumber()) {
1438 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
1439 }
else if (update_path ==
"environment.water.waves.period" &&
1440 item[
"value"].IsNumber()) {
1441 pTargetData->met_data.wave_period =
1442 static_cast<int>(item[
"value"].GetDouble());
1443 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
1444 item[
"value"].IsNumber()) {
1445 pTargetData->met_data.wave_dir =
1446 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1447 }
else if (update_path ==
"environment.water.swell.height" &&
1448 item[
"value"].IsNumber()) {
1449 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
1450 }
else if (update_path ==
"environment.water.swell.period" &&
1451 item[
"value"].IsNumber()) {
1452 pTargetData->met_data.swell_per =
1453 static_cast<int>(item[
"value"].GetDouble());
1454 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
1455 item[
"value"].IsNumber()) {
1456 pTargetData->met_data.swell_dir =
1457 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1458 }
else if (update_path ==
"environment.water.temperature" &&
1459 item[
"value"].IsNumber()) {
1460 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
1461 }
else if (update_path ==
"environment.water.salinity" &&
1462 item[
"value"].IsNumber()) {
1463 pTargetData->met_data.salinity = item[
"value"].GetDouble();
1464 }
else if (update_path ==
"environment.water.ice") {
1466 }
else if (update_path ==
"environment.water.iceValue" &&
1467 item[
"value"].IsNumber()) {
1468 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
1469 }
else if (update_path ==
"environment.water.seaStateValue" &&
1470 item[
"value"].IsNumber()) {
1471 pTargetData->met_data.seastate =
1472 static_cast<int>(item[
"value"].GetDouble());
1473 }
else if (update_path ==
"environment.water.seaState") {
1475 }
else if (update_path ==
"environment.outside.precipitation") {
1477 }
else if (update_path ==
"environment.outside.precipitationValue" &&
1478 item[
"value"].IsNumber()) {
1479 pTargetData->met_data.precipitation =
1480 static_cast<int>(item[
"value"].GetDouble());
1481 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
1482 item[
"value"].IsNumber()) {
1483 pTargetData->met_data.airpress_tend =
1484 static_cast<int>(item[
"value"].GetDouble());
1485 }
else if (update_path ==
"environment.outside.pressureTendency") {
1487 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
1488 item[
"value"].IsNumber()) {
1489 pTargetData->met_data.hor_vis =
1490 GEODESIC_METERS2NM(item[
"value"].GetDouble());
1491 }
else if (update_path ==
1492 "environment.outside.horizontalVisibility.overRange") {
1493 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
1494 }
else if (update_path == _T(
"")) {
1495 if (item[
"value"].HasMember(
"name")) {
1496 const wxString &name = item[
"value"][
"name"].GetString();
1497 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
1498 pTargetData->b_nameValid =
true;
1499 pTargetData->MID = 123;
1500 }
else if (item[
"value"].HasMember(
"registrations")) {
1501 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
1502 pTargetData->IMO = wxAtoi(imo.Right(7));
1503 }
else if (item[
"value"].HasMember(
"communication")) {
1504 const wxString &callsign =
1505 item[
"value"][
"communication"][
"callsignVhf"].GetString();
1506 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
1508 if (item[
"value"].HasMember(
"mmsi") &&
1509 1994 != (pTargetData->MMSI) / 100000) {
1511 wxString tmp = item[
"value"][
"mmsi"].GetString();
1512 if (tmp.ToLong(&mmsi)) {
1513 pTargetData->MMSI = mmsi;
1515 if (97 == mmsi / 10000000) {
1516 pTargetData->Class = AIS_SART;
1518 if (111 == mmsi / 1000000) {
1519 pTargetData->b_SarAircraftPosnReport =
true;
1522 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
1527 wxLogMessage(wxString::Format(
1528 _T(
"** AisDecoder::updateItem: unhandled path %s"), update_path));
1530 rapidjson::StringBuffer buffer;
1531 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1532 item.Accept(writer);
1533 wxString msg(_T(
"update: "));
1534 msg.append(buffer.GetString());
1544AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
1545 wxString *accumulator) {
1547 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
1549 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
1551 if (!pos)
return AIS_GENERIC_ERROR;
1553 if (!accumulator)
return AIS_GENERIC_ERROR;
1556 if (!str.Mid(1, 5).IsSameAs(_T(
"AIVDO")))
return AIS_GENERIC_ERROR;
1559 wxStringTokenizer tkz(str, _T(
","));
1562 token = tkz.GetNextToken();
1564 token = tkz.GetNextToken();
1565 int nsentences = atoi(token.mb_str());
1567 token = tkz.GetNextToken();
1568 int isentence = atoi(token.mb_str());
1570 token = tkz.GetNextToken();
1571 token = tkz.GetNextToken();
1573 wxString string_to_parse;
1574 string_to_parse.Clear();
1587 if ((1 == nsentences) && (1 == isentence)) {
1588 string_to_parse = tkz.GetNextToken();
1591 else if (nsentences > 1) {
1592 if (1 == isentence) {
1593 *accumulator = tkz.GetNextToken();
1597 accumulator->Append(tkz.GetNextToken());
1600 if (isentence == nsentences) {
1601 string_to_parse = *accumulator;
1605 if (string_to_parse.IsEmpty() &&
1607 return AIS_INCOMPLETE_MULTIPART;
1616 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1618 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
1620 if (bdecode_result) {
1621 switch (TargetData->MID) {
1626 if (!TargetData->b_positionDoubtful) {
1627 pos->kLat = TargetData->Lat;
1628 pos->kLon = TargetData->Lon;
1634 if (TargetData->COG == 360.0)
1637 pos->kCog = TargetData->COG;
1639 if (TargetData->SOG > 102.2)
1642 pos->kSog = TargetData->SOG;
1644 if ((
int)TargetData->HDG == 511)
1647 pos->kHdt = TargetData->HDG;
1655 return AIS_GENERIC_ERROR;
1660 return AIS_GENERIC_ERROR;
1668AisError AisDecoder::DecodeN0183(
const wxString &str) {
1669 AisError ret = AIS_GENERIC_ERROR;
1670 wxString string_to_parse;
1672 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
1673 double gpsg_cog, gpsg_sog, gpsg_utc_time;
1674 int gpsg_utc_hour = 0;
1675 int gpsg_utc_min = 0;
1676 int gpsg_utc_sec = 0;
1677 char gpsg_name_str[21];
1680 bool bdecode_result =
false;
1687 long arpa_tgt_num = 0;
1688 double arpa_sog = 0.;
1689 double arpa_cog = 0.;
1690 double arpa_lat = 0.;
1691 double arpa_lon = 0.;
1692 double arpa_dist = 0.;
1693 double arpa_brg = 0.;
1694 wxString arpa_brgunit;
1695 wxString arpa_status;
1696 wxString arpa_distunit;
1697 wxString arpa_cogunit;
1698 wxString arpa_reftarget;
1699 double arpa_mins, arpa_degs;
1700 double arpa_utc_time;
1701 int arpa_utc_hour = 0;
1702 int arpa_utc_min = 0;
1703 int arpa_utc_sec = 0;
1704 char arpa_name_str[21];
1705 bool arpa_lost =
true;
1706 bool arpa_nottracked =
false;
1708 double aprs_lat = 0.;
1709 double aprs_lon = 0.;
1710 char aprs_name_str[21];
1711 double aprs_mins, aprs_degs;
1713 std::shared_ptr<AisTargetData> pTargetData = 0;
1714 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1715 bool bnewtarget =
false;
1716 int last_report_ticks;
1720 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
1722 if (!NMEACheckSumOK(str)) {
1723 return AIS_NMEAVDX_CHECKSUM_BAD;
1725 if (str.Mid(1, 2).IsSameAs(_T(
"CD"))) {
1728 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
1732 wxString string(str);
1733 wxStringTokenizer tkz(
string, _T(
",*"));
1736 token = tkz.GetNextToken();
1737 token = tkz.GetNextToken();
1738 token.ToLong(&arpa_tgt_num);
1739 token = tkz.GetNextToken();
1740 token.ToDouble(&arpa_dist);
1741 token = tkz.GetNextToken();
1742 token.ToDouble(&arpa_brg);
1743 arpa_brgunit = tkz.GetNextToken();
1744 if (arpa_brgunit == _T(
"R")) {
1745 if (std::isnan(arpa_ref_hdg)) {
1746 if (!std::isnan(gHdt))
1751 arpa_brg += arpa_ref_hdg;
1752 if (arpa_brg >= 360.) arpa_brg -= 360.;
1754 token = tkz.GetNextToken();
1755 token.ToDouble(&arpa_sog);
1756 token = tkz.GetNextToken();
1757 token.ToDouble(&arpa_cog);
1758 arpa_cogunit = tkz.GetNextToken();
1759 if (arpa_cogunit == _T(
"R")) {
1760 if (std::isnan(arpa_ref_hdg)) {
1761 if (!std::isnan(gHdt))
1766 arpa_cog += arpa_ref_hdg;
1767 if (arpa_cog >= 360.) arpa_cog -= 360.;
1769 token = tkz.GetNextToken();
1770 token = tkz.GetNextToken();
1772 arpa_distunit = tkz.GetNextToken();
1773 token = tkz.GetNextToken();
1774 if (token == wxEmptyString)
1775 token = wxString::Format(_T(
"ARPA %ld"), arpa_tgt_num);
1776 int len = wxMin(token.Length(), 20);
1777 strncpy(arpa_name_str, token.mb_str(), len);
1778 arpa_name_str[len] = 0;
1779 arpa_status = tkz.GetNextToken();
1780 if (arpa_status != _T(
"L" )) {
1782 }
else if (arpa_status != wxEmptyString)
1783 arpa_nottracked =
true;
1784 arpa_reftarget = tkz.GetNextToken();
1785 if (tkz.HasMoreTokens()) {
1786 token = tkz.GetNextToken();
1787 token.ToDouble(&arpa_utc_time);
1788 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1789 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1791 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1793 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
1794 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
1795 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
1798 if (arpa_distunit == _T(
"K")) {
1799 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_KM, g_iDistanceFormat);
1800 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_KMH, g_iSpeedFormat);
1801 }
else if (arpa_distunit == _T(
"S")) {
1802 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_MI, g_iDistanceFormat);
1803 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_MPH, g_iSpeedFormat);
1806 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
1810 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
1813 wxString aprs_tll_str;
1814 wxString string(str);
1815 wxStringTokenizer tkz(
string, _T(
",*"));
1818 aprs_tll_str = tkz.GetNextToken();
1819 token = tkz.GetNextToken();
1820 token.ToLong(&arpa_tgt_num);
1821 token = tkz.GetNextToken();
1822 token.ToDouble(&arpa_lat);
1823 arpa_degs = (int)(arpa_lat / 100.0);
1824 arpa_mins = arpa_lat - arpa_degs * 100.0;
1825 arpa_lat = arpa_degs + arpa_mins / 60.0;
1826 token = tkz.GetNextToken();
1827 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1828 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1829 arpa_lat = 0. - arpa_lat;
1830 token = tkz.GetNextToken();
1831 token.ToDouble(&arpa_lon);
1832 arpa_degs = (int)(arpa_lon / 100.0);
1833 arpa_mins = arpa_lon - arpa_degs * 100.0;
1834 arpa_lon = arpa_degs + arpa_mins / 60.0;
1835 token = tkz.GetNextToken();
1836 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1837 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1838 arpa_lon = 0. - arpa_lon;
1839 token = tkz.GetNextToken();
1840 if (token == wxEmptyString)
1841 token = wxString::Format(_T(
"ARPA %d"), arpa_tgt_num);
1842 int len = wxMin(token.Length(), 20);
1843 strncpy(arpa_name_str, token.mb_str(), len);
1844 arpa_name_str[len] = 0;
1845 token = tkz.GetNextToken();
1846 token.ToDouble(&arpa_utc_time);
1847 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1848 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1850 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1855 if (arpa_status != _T(
"L"))
1857 else if (arpa_status != wxEmptyString)
1858 arpa_nottracked =
true;
1859 arpa_reftarget = tkz.GetNextToken();
1866 }
else if (str.Mid(3, 3).IsSameAs(_T(
"OSD"))) {
1868 wxString string(str);
1869 wxStringTokenizer tkz(
string, _T(
",*"));
1872 token = tkz.GetNextToken();
1873 token = tkz.GetNextToken();
1874 token.ToDouble(&arpa_ref_hdg);
1884 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(_T(
"WPL"))) {
1886 wxString string(str);
1887 wxStringTokenizer tkz(
string, _T(
",*"));
1890 token = tkz.GetNextToken();
1891 token = tkz.GetNextToken();
1892 token.ToDouble(&aprs_lat);
1893 aprs_degs = (int)(aprs_lat / 100.0);
1894 aprs_mins = aprs_lat - aprs_degs * 100.0;
1895 aprs_lat = aprs_degs + aprs_mins / 60.0;
1896 token = tkz.GetNextToken();
1897 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1898 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1899 aprs_lat = 0. - aprs_lat;
1900 token = tkz.GetNextToken();
1901 token.ToDouble(&aprs_lon);
1902 aprs_degs = (int)(aprs_lon / 100.0);
1903 aprs_mins = aprs_lon - aprs_degs * 100.0;
1904 aprs_lon = aprs_degs + aprs_mins / 60.0;
1905 token = tkz.GetNextToken();
1906 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1907 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1908 aprs_lon = 0. - aprs_lon;
1909 token = tkz.GetNextToken();
1910 int len = wxMin(token.Length(), 20);
1911 strncpy(aprs_name_str, token.mb_str(), len + 1);
1912 if (0 == g_WplAction) {
1914 aprs_name_str[len] = 0;
1915 for (i = 0; i < len; i++) {
1917 hash += (int)(aprs_name_str[i]);
1918 while (hash >= 100000) hash = hash / 100000;
1920 mmsi = aprs_mmsi = 199300000 + hash;
1924 }
else if (1 == g_WplAction) {
1926 aprs_name_str, wxEmptyString,
false);
1927 pWP->m_bIsolatedMark =
true;
1928 InsertWpt(pWP,
true);
1930 }
else if (str.Mid(1, 5).IsSameAs(_T(
"FRPOS"))) {
1934 wxString string(str);
1935 wxStringTokenizer tkz(
string, _T(
",*"));
1938 token = tkz.GetNextToken();
1940 token = tkz.GetNextToken();
1941 token.ToDouble(&gpsg_lat);
1942 gpsg_degs = (int)(gpsg_lat / 100.0);
1943 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
1944 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
1946 token = tkz.GetNextToken();
1947 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1948 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1949 gpsg_lat = 0. - gpsg_lat;
1951 token = tkz.GetNextToken();
1952 token.ToDouble(&gpsg_lon);
1953 gpsg_degs = (int)(gpsg_lon / 100.0);
1954 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
1955 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
1957 token = tkz.GetNextToken();
1958 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1959 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1960 gpsg_lon = 0. - gpsg_lon;
1962 token = tkz.GetNextToken();
1965 token = tkz.GetNextToken();
1966 token.ToDouble(&gpsg_sog);
1968 token = tkz.GetNextToken();
1969 token.ToDouble(&gpsg_cog);
1971 token = tkz.GetNextToken();
1974 token = tkz.GetNextToken();
1975 token.ToDouble(&gpsg_utc_time);
1976 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
1977 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
1979 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
1983 token = tkz.GetNextToken();
1984 int i, len, hash = 0;
1985 len = wxMin(wxStrlen(token), 20);
1986 strncpy(gpsg_name_str, token.mb_str(), len);
1987 gpsg_name_str[len] = 0;
1988 for (i = 0; i < len; i++) {
1990 hash += (int)(token[i]);
1991 while (hash >= 100000) hash = hash / 100000;
1994 gpsg_mmsi = 199000000 + hash;
1996 }
else if (!str.Mid(3, 2).IsSameAs(_T(
"VD"))) {
1997 return AIS_NMEAVDX_BAD;
2003 wxString string(str);
2004 wxStringTokenizer tkz(
string, _T(
","));
2007 token = tkz.GetNextToken();
2009 token = tkz.GetNextToken();
2010 nsentences = atoi(token.mb_str());
2012 token = tkz.GetNextToken();
2013 isentence = atoi(token.mb_str());
2015 token = tkz.GetNextToken();
2016 long lsequence_id = 0;
2017 token.ToLong(&lsequence_id);
2019 token = tkz.GetNextToken();
2021 token.ToLong(&lchannel);
2024 string_to_parse.Clear();
2028 if ((1 == nsentences) && (1 == isentence)) {
2029 string_to_parse = tkz.GetNextToken();
2032 else if (nsentences > 1) {
2033 if (1 == isentence) {
2034 sentence_accumulator = tkz.GetNextToken();
2038 sentence_accumulator += tkz.GetNextToken();
2041 if (isentence == nsentences) {
2042 string_to_parse = sentence_accumulator;
2046 if (mmsi || (!string_to_parse.IsEmpty() &&
2047 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
2049 wxCharBuffer abuf = string_to_parse.ToUTF8();
2051 return AIS_GENERIC_ERROR;
2056 if (!mmsi) mmsi = strbit.GetInt(9, 30);
2057 long mmsi_long = mmsi;
2060 int origin_mmsi = 0;
2061 int messID = strbit.GetInt(1, 6);
2062 int dac = strbit.GetInt(41, 10);
2063 int fi = strbit.GetInt(51, 6);
2065 int met_lon, met_lat;
2066 if (dac == 001 && fi == 31) {
2068 met_lon = strbit.GetInt(57, 25);
2069 met_lat = strbit.GetInt(82, 24);
2070 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
2073 }
else if (dac == 367 && fi == 33) {
2075 const int size = strbit.GetBitCount();
2076 if (size < 168)
return AIS_GENERIC_ERROR;
2077 const int startb = 56;
2078 const int slot_size = 112;
2079 const int extra_bits = (size - startb) % slot_size;
2080 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
2082 int mes_type = strbit.GetInt(57, 4);
2083 int site_ID = strbit.GetInt(77, 7);
2084 if (mes_type == 0) {
2086 met_lon = strbit.GetInt(90, 28);
2087 met_lat = strbit.GetInt(118, 27);
2088 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
2093 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
2099 return AIS_GENERIC_ERROR;
2105 auto it = AISTargetList.find(mmsi);
2106 if (it == AISTargetList.end())
2108 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2113 pTargetData->MMSI = mmsi;
2114 pTargetData->met_data.original_mmsi = origin_mmsi;
2117 pTargetData = it->second;
2120 pStaleTarget = pTargetData;
2122 pTargetData->MMSI = mmsi;
2123 pTargetData->met_data.original_mmsi = origin_mmsi;
2126 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2128 if (mmsi == props->MMSI) {
2130 if (TRACKTYPE_NEVER == props->TrackType) {
2131 pTargetData->b_show_track =
false;
2132 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
2133 pTargetData->b_show_track =
true;
2138 if (props->m_bignore)
return AIS_NoError;
2141 else if (props->m_bVDM) {
2143 if (str.Mid(3, 9).IsSameAs(wxT(
"VDM,1,1,,"))) {
2144 int message_ID = strbit.GetInt(1, 6);
2147 if ((message_ID <= 3) || (message_ID == 18)) {
2149 pTargetData->b_OwnShip =
true;
2151 wxString aivdostr = str;
2152 aivdostr.replace(1, 5,
"AIVDO");
2153 unsigned char calculated_checksum = 0;
2154 wxString::iterator i;
2155 for (i = aivdostr.begin() + 1; i != aivdostr.end() && *i !=
'*';
2157 calculated_checksum ^=
static_cast<unsigned char>(*i);
2160 if (i <= aivdostr.end() - 3)
2163 wxString::Format(_(
"%02X"), calculated_checksum));
2165 gps_watchdog_timeout_ticks =
2168 std::string full_sentence = aivdostr.ToStdString();
2169 std::string identifier(
"AIVDO");
2173 auto address = std::make_shared<NavAddr0183>(
"virtual");
2174 auto msg = std::make_shared<const Nmea0183Msg>(
2175 identifier, full_sentence, address);
2176 auto msg_all = std::make_shared<const Nmea0183Msg>(*msg,
"ALL");
2178 auto &msgbus = NavMsgBus::GetInstance();
2180 msgbus.Notify(std::move(msg));
2181 msgbus.Notify(std::move(msg_all));
2191 wxDateTime now = wxDateTime::Now();
2195 last_report_ticks = pStaleTarget->PositionReportTicks;
2197 last_report_ticks = now.GetTicks();
2201 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2205 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2206 pTargetData->PositionReportTicks = now.GetTicks();
2207 pTargetData->StaticReportTicks = now.GetTicks();
2208 pTargetData->m_utc_hour = gpsg_utc_hour;
2209 pTargetData->m_utc_min = gpsg_utc_min;
2210 pTargetData->m_utc_sec = gpsg_utc_sec;
2211 pTargetData->m_date_string = gpsg_date;
2212 pTargetData->MMSI = gpsg_mmsi;
2213 pTargetData->NavStatus = 0;
2214 pTargetData->Lat = gpsg_lat;
2215 pTargetData->Lon = gpsg_lon;
2216 pTargetData->b_positionOnceValid =
true;
2217 pTargetData->COG = gpsg_cog;
2218 pTargetData->SOG = gpsg_sog;
2219 pTargetData->ShipType = 52;
2220 pTargetData->Class = AIS_GPSG_BUDDY;
2221 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
2222 pTargetData->b_nameValid =
true;
2223 pTargetData->b_active =
true;
2224 pTargetData->b_lost =
false;
2226 bdecode_result =
true;
2227 }
else if (arpa_mmsi) {
2228 pTargetData->m_utc_hour = arpa_utc_hour;
2229 pTargetData->m_utc_min = arpa_utc_min;
2230 pTargetData->m_utc_sec = arpa_utc_sec;
2231 pTargetData->MMSI = arpa_mmsi;
2232 pTargetData->NavStatus = 15;
2233 if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
2236 (now.GetTicks() - pTargetData->PositionReportTicks);
2237 if (age_of_last > 0) {
2238 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
2239 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
2240 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2243 pTargetData->Lat = arpa_lat;
2244 pTargetData->Lon = arpa_lon;
2245 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
2246 if (arpa_dist != 0.)
2247 ll_gc_ll(gLat, gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
2251 pTargetData->COG = arpa_cog;
2252 pTargetData->SOG = arpa_sog;
2254 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2255 pTargetData->PositionReportTicks = now.GetTicks();
2256 pTargetData->StaticReportTicks = now.GetTicks();
2257 pTargetData->b_positionOnceValid =
true;
2258 pTargetData->ShipType = 55;
2259 pTargetData->Class = AIS_ARPA;
2261 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
2262 if (arpa_status != _T(
"Q"))
2263 pTargetData->b_nameValid =
true;
2265 pTargetData->b_nameValid =
false;
2266 pTargetData->b_active = !arpa_lost;
2267 pTargetData->b_lost = arpa_nottracked;
2269 bdecode_result =
true;
2270 }
else if (aprs_mmsi) {
2271 pTargetData->m_utc_hour = now.GetHour();
2272 pTargetData->m_utc_min = now.GetMinute();
2273 pTargetData->m_utc_sec = now.GetSecond();
2274 pTargetData->MMSI = aprs_mmsi;
2275 pTargetData->NavStatus = 15;
2277 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
2278 if (age_of_last > 0) {
2279 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
2280 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
2281 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2284 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2285 pTargetData->PositionReportTicks = now.GetTicks();
2286 pTargetData->StaticReportTicks = now.GetTicks();
2287 pTargetData->Lat = aprs_lat;
2288 pTargetData->Lon = aprs_lon;
2289 pTargetData->b_positionOnceValid =
true;
2290 pTargetData->ShipType = 56;
2291 pTargetData->Class = AIS_APRS;
2292 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
2293 pTargetData->b_nameValid =
true;
2294 pTargetData->b_active =
true;
2295 pTargetData->b_lost =
false;
2297 bdecode_result =
true;
2301 Parse_VDXBitstring(&strbit, pTargetData);
2305 getMmsiProperties(pTargetData);
2308 pTargetData->RecentPeriod =
2309 pTargetData->PositionReportTicks - last_report_ticks;
2319 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
2324 if ((n_msgs % 10000) == 0)
2325 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
2326 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
2332void AisDecoder::CommitAISTarget(std::shared_ptr<AisTargetData> pTargetData,
2333 const wxString &str,
bool message_valid,
2335 m_pLatestTargetData = pTargetData;
2337 if (!str.IsEmpty()) {
2338 if (str.Mid(3, 3).IsSameAs(_T(
"VDO")))
2339 pTargetData->b_OwnShip =
true;
2341 pTargetData->b_OwnShip =
false;
2344 if (!pTargetData->b_OwnShip) {
2346 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
2348 pTargetData->b_PersistTrack =
false;
2350 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2351 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2353 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2360 pTargetData->b_NoTrack =
false;
2365 if (message_valid) {
2367 if (pTargetData->MMSI) {
2368 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2371 AISTargetList[pTargetData->MMSI] =
2374 if (!pTargetData->area_notices.empty()) {
2375 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
2376 if (it == AIS_AreaNotice_Sources.end())
2377 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
2382 if (!pTargetData->b_OwnShip) {
2383 if (pTargetData->b_positionOnceValid) {
2384 long mmsi_long = pTargetData->MMSI;
2385 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2386 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2388 pSel->SetUserData(pTargetData->MMSI);
2392 UpdateOneCPA(pTargetData.get());
2395 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2408 if (!pTargetData->b_OwnShip) {
2409 if (pTargetData->b_positionOnceValid) {
2410 long mmsi_long = pTargetData->MMSI;
2411 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2412 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2414 pSel->SetUserData(pTargetData->MMSI);
2421void AisDecoder::getAISTarget(
long mmsi,
2422 std::shared_ptr<AisTargetData> &pTargetData,
2423 std::shared_ptr<AisTargetData> &pStaleTarget,
2424 bool &bnewtarget,
int &last_report_ticks,
2426 now = wxDateTime::Now();
2427 auto it = AISTargetList.find(mmsi);
2428 if (it == AISTargetList.end())
2430 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2434 pTargetData = it->second;
2435 pStaleTarget = pTargetData;
2442 last_report_ticks = pStaleTarget->PositionReportTicks;
2444 last_report_ticks = now.GetTicks();
2448 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
2451void AisDecoder::getMmsiProperties(
2452 std::shared_ptr<AisTargetData> &pTargetData) {
2453 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2454 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2456 pTargetData->b_isFollower = props->m_bFollower;
2457 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2458 if (TRACKTYPE_NEVER == props->TrackType) {
2459 pTargetData->b_show_track =
false;
2460 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
2461 pTargetData->b_show_track =
true;
2468std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
2470 double dsc_lat = 0.;
2471 double dsc_lon = 0.;
2472 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
2474 double dse_lat = 0.;
2475 double dse_lon = 0.;
2476 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
2479 int dsc_tx_mmsi = 0;
2481 double dse_cog = 0.;
2482 double dse_sog = 0.;
2483 wxString dse_shipName = wxEmptyString;
2488 std::shared_ptr<AisTargetData> pTargetData = NULL;
2492 wxString string(str);
2493 wxStringTokenizer tkz(
string, _T(
",*"));
2496 token = tkz.GetNextToken();
2498 if (str.Mid(3, 3).IsSameAs(_T(
"DSC"))) {
2499 m_dsc_last_string = str;
2501 token = tkz.GetNextToken();
2505 token = tkz.GetNextToken();
2507 if (dsc_fmt == 12 || dsc_fmt == 16) {
2508 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2510 token.ToDouble(&dsc_addr);
2511 dsc_mmsi = 0 - (int)(dsc_addr / 10);
2514 token = tkz.GetNextToken();
2515 token.ToLong(&dsc_cat);
2517 token = tkz.GetNextToken();
2518 if (!token.IsSameAs(wxEmptyString)) {
2519 token.ToLong(&dsc_nature);
2523 token = tkz.GetNextToken();
2525 token = tkz.GetNextToken();
2526 token.ToDouble(&dsc_tmp);
2528 token = tkz.GetNextToken();
2529 token = tkz.GetNextToken();
2530 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(wxEmptyString)) {
2532 dsc_tx_mmsi = dsc_mmsi;
2533 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2535 token = tkz.GetNextToken();
2536 if (dsc_fmt == 16 && dsc_cat == 12) {
2537 if (!token.IsSameAs(wxEmptyString)) {
2538 token.ToLong(&dsc_nature);
2542 token = tkz.GetNextToken();
2543 token = tkz.GetNextToken();
2545 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
2547 if (dsc_quadrant > 3)
2550 dsc_lat = (int)(dsc_tmp / 100000.0);
2551 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
2552 dsc_lat = dsc_lat - dsc_quadrant * 10000;
2553 dsc_degs = (int)(dsc_lat / 100.0);
2554 dsc_mins = dsc_lat - dsc_degs * 100.0;
2555 dsc_lat = dsc_degs + dsc_mins / 60.0;
2557 dsc_degs = (int)(dsc_lon / 100.0);
2558 dsc_mins = dsc_lon - dsc_degs * 100.0;
2559 dsc_lon = dsc_degs + dsc_mins / 60.0;
2560 switch (dsc_quadrant) {
2576 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
2578 }
else if (str.Mid(3, 3).IsSameAs(_T(
"DSE"))) {
2579 token = tkz.GetNextToken();
2580 token = tkz.GetNextToken();
2581 token = tkz.GetNextToken();
2582 token = tkz.GetNextToken();
2583 dse_mmsi = wxAtoi(token.Mid(
2589 token = tkz.GetNextToken();
2592 token.ToDouble(&dse_tmp);
2593 dse_lat = (int)(dse_tmp / 10000.0);
2594 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2595 dse_lat = dse_lat / 600000.0;
2596 dse_lon = dse_lon / 600000.0;
2599 while (tkz.HasMoreTokens()) {
2600 dseSymbol = tkz.GetNextToken();
2601 token = tkz.GetNextToken();
2602 if (dseSymbol.IsSameAs(_T(
"00"))) {
2603 token.ToDouble(&dse_tmp);
2604 dse_lat = (int)(dse_tmp / 10000.0);
2605 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2606 dse_lat = dse_lat / 600000.0;
2607 dse_lon = dse_lon / 600000.0;
2608 }
else if (dseSymbol.IsSameAs(_T(
"01"))) {
2609 }
else if (dseSymbol.IsSameAs(_T(
"02"))) {
2610 token.ToDouble(&dse_tmp);
2611 dse_sog = dse_tmp / 10.0;
2612 }
else if (dseSymbol.IsSameAs(_T(
"03"))) {
2613 token.ToDouble(&dse_tmp);
2614 dse_cog = dse_tmp / 10.0;
2615 }
else if (dseSymbol.IsSameAs(_T(
"04"))) {
2616 dse_shipName = DecodeDSEExpansionCharacters(token);
2617 }
else if (dseSymbol.IsSameAs(_T(
"05"))) {
2618 }
else if (dseSymbol.IsSameAs(_T(
"06"))) {
2621 mmsi = abs((
int)dse_mmsi);
2625 wxDateTime now = wxDateTime::Now();
2627 int last_report_ticks = now.GetTicks();
2630 auto it = AISTargetList.find(mmsi);
2631 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
2632 if (it == AISTargetList.end()) {
2634 pStaleTarget = it->second;
2635 last_report_ticks = pStaleTarget->PositionReportTicks;
2641 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
2643 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
2644 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
2646 m_ptentative_dsctarget->MMSI = mmsi;
2647 m_ptentative_dsctarget->NavStatus =
2649 m_ptentative_dsctarget->Lat = dsc_lat;
2650 m_ptentative_dsctarget->Lon = dsc_lon;
2651 m_ptentative_dsctarget->b_positionOnceValid =
true;
2652 m_ptentative_dsctarget->COG = 0;
2653 m_ptentative_dsctarget->SOG = 0;
2654 m_ptentative_dsctarget->ShipType = dsc_fmt;
2655 m_ptentative_dsctarget->Class = AIS_DSC;
2656 m_ptentative_dsctarget->b_isDSCtarget =
true;
2657 m_ptentative_dsctarget->b_nameValid =
true;
2658 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
2659 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %d",
2661 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
2662 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
2664 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %d",
2668 m_ptentative_dsctarget->b_active =
true;
2669 m_ptentative_dsctarget->b_lost =
false;
2670 m_ptentative_dsctarget->RecentPeriod =
2671 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2674 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
2679 if (dse_mmsi || b_take_dsc) {
2680 if (m_ptentative_dsctarget) {
2686 m_ptentative_dsctarget->Lat =
2687 m_ptentative_dsctarget->Lat +
2688 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
2689 m_ptentative_dsctarget->Lon =
2690 m_ptentative_dsctarget->Lon +
2691 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
2692 if (dse_shipName.length() > 0) {
2693 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
2694 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
2695 "%s", dse_shipName.ToAscii().data());
2697 m_ptentative_dsctarget->COG = dse_cog;
2698 m_ptentative_dsctarget->SOG = dse_sog;
2702 m_ptentative_dsctarget->RecentPeriod =
2703 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2708 auto it = AISTargetList.find(mmsi);
2709 if (it == AISTargetList.end()) {
2710 pTargetData = m_ptentative_dsctarget;
2712 pTargetData = it->second;
2713 std::vector<AISTargetTrackPoint> ptrack =
2714 std::move(pTargetData->m_ptrack);
2715 pTargetData->CloneFrom(
2716 m_ptentative_dsctarget
2719 pTargetData->m_ptrack =
2726 m_ptentative_dsctarget = NULL;
2728 m_pLatestTargetData = pTargetData;
2730 AISTargetList[pTargetData->MMSI] =
2733 long mmsi_long = pTargetData->MMSI;
2737 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2740 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2741 (
void *)mmsi_long, SELTYPE_AISTARGET);
2742 pSel->SetUserData(pTargetData->MMSI);
2745 UpdateOneCPA(pTargetData.get());
2748 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2756wxString AisDecoder::DecodeDSEExpansionCharacters(wxString dseData) {
2758 char lookupTable[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
' ',
2759 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
2760 'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
2761 'W',
'X',
'Y',
'Z',
'.',
',',
'-',
'/',
' '};
2763 for (
size_t i = 0; i < dseData.length(); i += 2) {
2764 result.append(1, lookupTable[strtol(dseData.Mid(i, 2).data(), NULL, 10)]);
2773 std::shared_ptr<AisTargetData> ptd) {
2774 bool parse_result =
false;
2775 bool b_posn_report =
false;
2777 wxDateTime now = wxDateTime::Now();
2779 int message_ID = bstr->
GetInt(1, 6);
2780 ptd->MID = message_ID;
2783 int met_mmsi = ptd->MMSI;
2786 ptd->MMSI = bstr->
GetInt(9, 30);
2788 switch (message_ID) {
2794 ptd->NavStatus = bstr->
GetInt(39, 4);
2795 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
2797 int lon = bstr->
GetInt(62, 28);
2798 if (lon & 0x08000000)
2800 double lon_tentative = lon / 600000.;
2802 int lat = bstr->
GetInt(90, 27);
2803 if (lat & 0x04000000)
2805 double lat_tentative = lat / 600000.;
2807 if ((lon_tentative <= 180.) &&
2811 ptd->Lon = lon_tentative;
2812 ptd->Lat = lat_tentative;
2813 ptd->b_positionDoubtful =
false;
2814 ptd->b_positionOnceValid =
true;
2815 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2816 ptd->PositionReportTicks = now.GetTicks();
2818 ptd->b_positionDoubtful =
true;
2821 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
2822 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
2824 ptd->ROTAIS = bstr->
GetInt(43, 8);
2825 double rot_dir = 1.0;
2827 if (ptd->ROTAIS == 128)
2829 else if ((ptd->ROTAIS & 0x80) == 0x80) {
2830 ptd->ROTAIS = ptd->ROTAIS - 256;
2834 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
2837 ptd->m_utc_sec = bstr->
GetInt(138, 6);
2839 if ((1 == message_ID) ||
2842 ptd->SyncState = bstr->
GetInt(151, 2);
2843 ptd->SlotTO = bstr->
GetInt(153, 2);
2844 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
2846 ptd->m_utc_hour = bstr->
GetInt(155, 5);
2848 ptd->m_utc_min = bstr->
GetInt(160, 7);
2850 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
2851 (ptd->m_utc_sec < 60)) {
2852 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
2853 rx_ticks = rx_time.GetTicks();
2855 first_rx_ticks = rx_ticks;
2863 ptd->blue_paddle = bstr->
GetInt(144, 2);
2864 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
2866 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
2869 int mmsi_start = ptd->MMSI / 10000000;
2871 if (mmsi_start == 97) {
2872 ptd->Class = AIS_SART;
2873 ptd->StaticReportTicks =
2887 parse_result =
true;
2888 b_posn_report =
true;
2897 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2899 int lon = bstr->
GetInt(58, 28);
2900 if (lon & 0x08000000)
2902 double lon_tentative = lon / 600000.;
2904 int lat = bstr->
GetInt(86, 27);
2905 if (lat & 0x04000000)
2907 double lat_tentative = lat / 600000.;
2909 if ((lon_tentative <= 180.) &&
2913 ptd->Lon = lon_tentative;
2914 ptd->Lat = lat_tentative;
2915 ptd->b_positionDoubtful =
false;
2916 ptd->b_positionOnceValid =
true;
2917 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2918 ptd->PositionReportTicks = now.GetTicks();
2920 ptd->b_positionDoubtful =
true;
2922 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2923 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2925 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2927 if (!ptd->b_isDSCtarget) {
2928 if (!isBuoyMmsi(ptd->MMSI))
2929 ptd->Class = AIS_CLASS_B;
2931 ptd->Class = AIS_BUOY;
2933 parse_result =
true;
2934 b_posn_report =
true;
2942 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2943 int lon = bstr->
GetInt(58, 28);
2944 if (lon & 0x08000000)
2946 double lon_tentative = lon / 600000.;
2948 int lat = bstr->
GetInt(86, 27);
2949 if (lat & 0x04000000)
2951 double lat_tentative = lat / 600000.;
2953 if ((lon_tentative <= 180.) &&
2957 ptd->Lon = lon_tentative;
2958 ptd->Lat = lat_tentative;
2959 ptd->b_positionDoubtful =
false;
2960 ptd->b_positionOnceValid =
true;
2961 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2962 ptd->PositionReportTicks = now.GetTicks();
2964 ptd->b_positionDoubtful =
true;
2966 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2967 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2968 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2970 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
2971 ptd->b_nameValid =
true;
2972 if (!ptd->b_isDSCtarget) {
2973 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
2975 ptd->DimA = bstr->
GetInt(272, 9);
2976 ptd->DimB = bstr->
GetInt(281, 9);
2977 ptd->DimC = bstr->
GetInt(290, 6);
2978 ptd->DimD = bstr->
GetInt(296, 6);
2980 if (!ptd->b_isDSCtarget) {
2982 if (!isBuoyMmsi(ptd->MMSI))
2983 ptd->Class = AIS_CLASS_B;
2985 ptd->Class = AIS_BUOY;
2987 parse_result =
true;
2988 b_posn_report =
true;
3001 int bitCorrection = 10;
3002 int resolution = 10;
3005 double lon_tentative = 181.;
3006 double lat_tentative = 91.;
3009 printf(
"AIS Message 27 - received:\r\n");
3010 printf(
"MMSI : %i\r\n", ptd->MMSI);
3016 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
3018 ptd->NavStatus = bstr->
GetInt(39, 4);
3020 int lon = bstr->
GetInt(45, 18);
3021 int lat = bstr->
GetInt(63, 17);
3023 lat_tentative = lat;
3024 lon_tentative = lon;
3027 if (lat >= (0x4000000 >> bitCorrection)) {
3028 lat_tentative = (0x8000000 >> bitCorrection) - lat;
3029 lat_tentative *= -1;
3033 if (lon >= (0x8000000 >> bitCorrection)) {
3034 lon_tentative = (0x10000000 >> bitCorrection) - lon;
3035 lon_tentative *= -1;
3039 lat_tentative = lat_tentative / resolution / 60.0;
3040 lon_tentative = lon_tentative / resolution / 60.0;
3043 printf(
"Latitude : %f\r\n", lat_tentative);
3044 printf(
"Longitude : %f\r\n", lon_tentative);
3048 int positionLatency = bstr->
GetInt(95, 1);
3050 if ((lon_tentative <= 180.) &&
3054 ptd->Lon = lon_tentative;
3055 ptd->Lat = lat_tentative;
3056 ptd->b_positionDoubtful =
false;
3057 ptd->b_positionOnceValid =
true;
3058 if (positionLatency == 0) {
3061 printf(
"Low latency position report.\r\n");
3063 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3064 ptd->PositionReportTicks = now.GetTicks();
3067 ptd->b_positionDoubtful =
true;
3069 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
3070 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
3072 b_posn_report =
true;
3073 parse_result =
true;
3079 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
3085 int AIS_version_indicator = bstr->
GetInt(39, 2);
3086 if (AIS_version_indicator < 4) {
3087 ptd->IMO = bstr->
GetInt(41, 30);
3089 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
3090 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3091 ptd->b_nameValid =
true;
3092 if (!ptd->b_isDSCtarget) {
3093 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
3096 ptd->DimA = bstr->
GetInt(241, 9);
3097 ptd->DimB = bstr->
GetInt(250, 9);
3098 ptd->DimC = bstr->
GetInt(259, 6);
3099 ptd->DimD = bstr->
GetInt(265, 6);
3101 ptd->ETA_Mo = bstr->
GetInt(275, 4);
3102 ptd->ETA_Day = bstr->
GetInt(279, 5);
3103 ptd->ETA_Hr = bstr->
GetInt(284, 5);
3104 ptd->ETA_Min = bstr->
GetInt(289, 6);
3106 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
3108 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
3110 ptd->StaticReportTicks = now.GetTicks();
3112 parse_result =
true;
3119 int part_number = bstr->
GetInt(39, 2);
3120 if (0 == part_number) {
3121 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3122 ptd->b_nameValid =
true;
3123 parse_result =
true;
3125 }
else if (1 == part_number) {
3126 if (!ptd->b_isDSCtarget) {
3127 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
3129 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
3131 ptd->DimA = bstr->
GetInt(133, 9);
3132 ptd->DimB = bstr->
GetInt(142, 9);
3133 ptd->DimC = bstr->
GetInt(151, 6);
3134 ptd->DimD = bstr->
GetInt(157, 6);
3135 parse_result =
true;
3141 ptd->Class = AIS_BASE;
3143 ptd->m_utc_hour = bstr->
GetInt(62, 5);
3144 ptd->m_utc_min = bstr->
GetInt(67, 6);
3145 ptd->m_utc_sec = bstr->
GetInt(73, 6);
3147 int lon = bstr->
GetInt(80, 28);
3148 if (lon & 0x08000000)
3150 double lon_tentative = lon / 600000.;
3152 int lat = bstr->
GetInt(108, 27);
3153 if (lat & 0x04000000)
3155 double lat_tentative = lat / 600000.;
3157 if ((lon_tentative <= 180.) &&
3161 ptd->Lon = lon_tentative;
3162 ptd->Lat = lat_tentative;
3163 ptd->b_positionDoubtful =
false;
3164 ptd->b_positionOnceValid =
true;
3165 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3166 ptd->PositionReportTicks = now.GetTicks();
3168 ptd->b_positionDoubtful =
true;
3174 parse_result =
true;
3175 b_posn_report =
true;
3181 ptd->SOG = bstr->
GetInt(51, 10);
3183 int lon = bstr->
GetInt(62, 28);
3184 if (lon & 0x08000000)
3186 double lon_tentative = lon / 600000.;
3188 int lat = bstr->
GetInt(90, 27);
3189 if (lat & 0x04000000)
3191 double lat_tentative = lat / 600000.;
3193 if ((lon_tentative <= 180.) &&
3197 ptd->Lon = lon_tentative;
3198 ptd->Lat = lat_tentative;
3199 ptd->b_positionDoubtful =
false;
3200 ptd->b_positionOnceValid =
true;
3201 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3202 ptd->PositionReportTicks = now.GetTicks();
3204 ptd->b_positionDoubtful =
true;
3207 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
3209 int alt_tent = bstr->
GetInt(39, 12);
3210 ptd->altitude = alt_tent;
3212 ptd->b_SarAircraftPosnReport =
true;
3214 parse_result =
true;
3215 b_posn_report =
true;
3221 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
3227 ptd->DimA = bstr->
GetInt(220, 9);
3228 ptd->DimB = bstr->
GetInt(229, 9);
3229 ptd->DimC = bstr->
GetInt(238, 6);
3230 ptd->DimD = bstr->
GetInt(244, 6);
3233 ptd->m_utc_sec = bstr->
GetInt(254, 6);
3235 int offpos = bstr->
GetInt(260, 1);
3236 int virt = bstr->
GetInt(270, 1);
3239 ptd->NavStatus = ATON_VIRTUAL;
3241 ptd->NavStatus = ATON_REAL;
3242 if (ptd->m_utc_sec <= 59 ) {
3243 ptd->NavStatus += 1;
3244 if (offpos) ptd->NavStatus += 1;
3247 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3250 if (bstr->GetBitCount() > 276) {
3251 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
3252 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
3253 ptd->ShipNameExtension[14] = 0;
3255 ptd->ShipNameExtension[0] = 0;
3258 ptd->b_nameValid =
true;
3260 parse_result =
true;
3262 ptd->Class = AIS_ATON;
3264 int lon = bstr->
GetInt(165, 28);
3266 if (lon & 0x08000000)
3268 double lon_tentative = lon / 600000.;
3270 int lat = bstr->
GetInt(193, 27);
3272 if (lat & 0x04000000)
3274 double lat_tentative = lat / 600000.;
3276 if ((lon_tentative <= 180.) &&
3280 ptd->Lon = lon_tentative;
3281 ptd->Lat = lat_tentative;
3282 ptd->b_positionDoubtful =
false;
3283 ptd->b_positionOnceValid =
true;
3284 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3285 ptd->PositionReportTicks = now.GetTicks();
3287 ptd->b_positionDoubtful =
true;
3289 b_posn_report =
true;
3294 int dac = bstr->
GetInt(41, 10);
3295 int fi = bstr->
GetInt(51, 6);
3301 ptd->b_isEuroInland =
true;
3303 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
3304 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
3305 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
3306 ptd->UN_shiptype = bstr->
GetInt(128, 14);
3307 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
3308 parse_result =
true;
3311 if (dac == 1 || dac == 366)
3315 if (bstr->GetBitCount() >= 111) {
3317 an.link_id = bstr->
GetInt(57, 10);
3318 an.notice_type = bstr->
GetInt(67, 7);
3319 an.month = bstr->
GetInt(74, 4);
3320 an.day = bstr->
GetInt(78, 5);
3321 an.hour = bstr->
GetInt(83, 5);
3322 an.minute = bstr->
GetInt(88, 6);
3323 an.duration_minutes = bstr->
GetInt(94, 18);
3325 wxDateTime now = wxDateTime::Now();
3328 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3329 now.GetYear(), an.hour, an.minute);
3334 if (an.start_time > now + wxTimeSpan::Hours(48))
3335 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3336 now.GetYear() - 1, an.hour, an.minute);
3339 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3344 if (an.expiry_time < now - wxTimeSpan::Hours(24)) {
3345 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3346 now.GetYear() + 1, an.hour, an.minute);
3348 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3353 int subarea_len = 87;
3356 float pos_scale = 60000.0;
3364 pos_scale = 600000.0;
3369 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
3370 for (
int i = 0; i < subarea_count; ++i) {
3371 int base = 111 + i * subarea_len;
3373 sa.shape = bstr->
GetInt(base + 1, 3);
3374 int scale_factor = 1;
3375 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
3378 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
3379 sa.text = wxString(t, wxConvUTF8);
3381 int scale_multipliers[4] = {1, 10, 100, 1000};
3382 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
3384 case AIS8_001_22_SHAPE_SECTOR:
3385 sa.left_bound_deg = bstr->
GetInt(
3386 base + 6 + lon_len + lat_len + prec_size + 12, 9);
3387 sa.right_bound_deg = bstr->
GetInt(
3388 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
3389 case AIS8_001_22_SHAPE_CIRCLE:
3391 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
3394 case AIS8_001_22_SHAPE_RECT:
3396 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
3398 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
3401 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
3406 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
3408 sa.orient_deg = bstr->
GetInt(
3409 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
3411 case AIS8_001_22_SHAPE_POLYLINE:
3412 case AIS8_001_22_SHAPE_POLYGON:
3413 for (
int i = 0; i < 4; ++i) {
3414 sa.angles[i] = bstr->
GetInt(base + 6 + i * 20, 10) * 0.5;
3416 bstr->
GetInt(base + 16 + i * 20, 10) * scale_factor;
3420 an.sub_areas.push_back(sa);
3422 ptd->area_notices[an.link_id] = an;
3423 parse_result =
true;
3429 if (bstr->GetBitCount() >= 360) {
3431 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
3434 double lon_tentative = 181.;
3435 double lat_tentative = 91.;
3437 int lon = bstr->
GetInt(57, 25);
3438 int lat = bstr->
GetInt(82, 24);
3440 if (lon & 0x01000000)
3442 lon_tentative = lon / 60000.;
3444 if (lat & 0x00800000)
3446 lat_tentative = lat / 60000.;
3448 ptd->Lon = lon_tentative;
3449 ptd->Lat = lat_tentative;
3452 wxString x = ptd->ShipName;
3453 if (x.Find(
"METEO") == wxNOT_FOUND) {
3455 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
3456 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
3457 slat.ToDouble(&id1);
3458 slon.ToDouble(&id2);
3459 wxString nameID =
"METEO ";
3460 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
3461 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
3464 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
3465 ptd->met_data.day = bstr->
GetInt(107, 5);
3466 ptd->met_data.hour = bstr->
GetInt(112, 5);
3467 ptd->met_data.minute = bstr->
GetInt(117, 6);
3468 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
3469 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
3470 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
3471 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
3473 int tmp = bstr->
GetInt(155, 11);
3474 if (tmp & 0x00000400)
3476 ptd->met_data.air_temp = tmp / 10.;
3477 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
3478 int dew = bstr->
GetInt(173, 10);
3479 if (dew & 0x00000200)
3481 ptd->met_data.dew_point = dew / 10.;
3486 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
3487 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
3489 int horVis = bstr->
GetInt(194, 8);
3490 if (horVis & 0x80u) {
3492 ptd->met_data.hor_vis_GT =
true;
3494 ptd->met_data.hor_vis_GT =
false;
3496 ptd->met_data.hor_vis = horVis / 10.0;
3498 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
3499 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
3500 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
3501 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
3502 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
3503 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
3504 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
3505 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
3506 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
3507 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
3508 ptd->met_data.seastate = bstr->
GetInt(323, 4);
3510 int wt = bstr->
GetInt(327, 10);
3511 if (wt & 0x00000200)
3513 ptd->met_data.water_temp = wt / 10.;
3515 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
3516 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
3517 ptd->met_data.ice = bstr->
GetInt(349, 2);
3519 ptd->Class = AIS_METEO;
3523 ptd->b_NoTrack =
true;
3524 ptd->b_show_track =
false;
3525 ptd->b_positionDoubtful =
false;
3526 ptd->b_positionOnceValid =
true;
3527 b_posn_report =
true;
3528 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3529 ptd->PositionReportTicks = now.GetTicks();
3530 ptd->b_nameValid =
true;
3532 parse_result =
true;
3538 if (dac == 367 && fi == 33) {
3542 const int size = bstr->GetBitCount();
3545 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
3546 const int startbits = 56;
3547 const int slotsize = 112;
3548 const int slots_count = (size - startbits) / slotsize;
3550 for (
int slot = 0; slot < slots_count; slot++) {
3551 slotbit = slot * slotsize;
3552 int type = bstr->
GetInt(slotbit + 57, 4);
3553 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
3554 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
3555 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
3558 if (!ptd->b_nameValid) {
3559 wxString nameID =
"METEO Site: ";
3561 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
3562 ptd->b_nameValid =
true;
3566 int lon = bstr->
GetInt(slotbit + 90, 28);
3567 if (lon & 0x08000000)
3569 ptd->Lon = lon / 600000.;
3571 int lat = bstr->
GetInt(slotbit + 118, 27);
3572 if (lat & 0x04000000)
3574 ptd->Lat = lat / 600000.;
3575 ptd->b_positionOnceValid =
true;
3577 }
else if (type == 1) {
3578 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
3579 ptd->b_nameValid =
true;
3581 }
else if (type == 2) {
3583 int descr = bstr->
GetInt(slotbit + 116, 3);
3584 if (descr == 1 || descr == 2) {
3585 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
3586 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
3587 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
3588 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
3591 }
else if (type == 3) {
3593 int descr = bstr->
GetInt(slotbit + 108, 3);
3594 if (descr == 1 || descr == 2) {
3595 int wltype = bstr->
GetInt(slotbit + 84, 1);
3596 int wl = bstr->
GetInt(slotbit + 85, 16);
3597 if (wl & 0x00004000)
3601 ptd->met_data.water_level = wl / 100.;
3603 ptd->met_data.water_lev_dev = wl / 100.;
3605 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
3606 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
3608 }
else if (type == 6) {
3609 int readbearing = bstr->
GetInt(slotbit + 84, 9);
3610 int readdistance = bstr->
GetInt(slotbit + 93, 9);
3611 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
3612 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
3613 int readLevel = bstr->
GetInt(slotbit + 119, 9);
3615 }
else if (type == 7) {
3617 bstr->
GetInt(slotbit + 111, 3);
3618 if (swell_descr == 1 || swell_descr == 2) {
3619 ptd->met_data.swell_height =
3620 bstr->
GetInt(slotbit + 84, 8) / 10.0;
3621 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
3622 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
3624 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
3625 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
3626 if (wt_descr == 1 || wt_descr == 2)
3627 ptd->met_data.water_temp =
3628 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
3630 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
3631 if (wawe_descr == 1 || wawe_descr == 2) {
3632 ptd->met_data.wave_height =
3633 bstr->
GetInt(slotbit + 134, 8) / 10.0;
3634 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
3635 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
3637 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
3639 }
else if (type == 8) {
3640 ptd->met_data.water_temp =
3641 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
3642 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
3644 }
else if (type == 9) {
3645 int tmp = bstr->
GetInt(slotbit + 84, 11);
3646 if (tmp & 0x00000400)
3648 ptd->met_data.air_temp = tmp / 10.;
3649 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
3660 ptd->met_data.precipitation = pp;
3661 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
3662 ptd->met_data.dew_point =
3663 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
3664 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
3665 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
3666 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
3668 }
else if (type == 11) {
3670 int descr = bstr->
GetInt(slotbit + 113, 3);
3671 if (descr == 1 || descr == 2) {
3672 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
3673 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
3674 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
3679 if (ptd->b_positionOnceValid) {
3680 ptd->Class = AIS_METEO;
3684 ptd->b_NoTrack =
true;
3685 ptd->b_show_track =
false;
3686 ptd->b_positionDoubtful =
false;
3687 b_posn_report =
true;
3688 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3689 ptd->PositionReportTicks = now.GetTicks();
3690 ptd->b_nameValid =
true;
3692 parse_result =
true;
3702 char msg_14_text[968];
3703 if (bstr->GetBitCount() > 40) {
3704 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
3705 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
3707 nd = wxMin(nd, 967);
3708 msg_14_text[nd] = 0;
3709 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
3711 parse_result =
true;
3729 if (b_posn_report) ptd->b_lost =
false;
3731 if (
true == parse_result) {
3733 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
3734 ptd->b_active =
true;
3737 return parse_result;
3740bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3741 unsigned char checksum_value = 0;
3742 int sentence_hex_sum;
3744 wxCharBuffer buf = str_in.ToUTF8();
3745 if (!buf.data())
return false;
3747 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3748 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3749 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3751 int string_length = strlen(str_ascii);
3753 int payload_length = 0;
3754 while ((payload_length < string_length) &&
3755 (str_ascii[payload_length] !=
'*'))
3758 if (payload_length == string_length)
3763 while (index < payload_length) {
3764 checksum_value ^= str_ascii[index];
3768 if (string_length > 4) {
3770 scanstr[0] = str_ascii[payload_length + 1];
3771 scanstr[1] = str_ascii[payload_length + 2];
3773 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3775 if (sentence_hex_sum == checksum_value)
return true;
3781void AisDecoder::UpdateAllCPA(
void) {
3783 for (
const auto &it : GetTargetList()) {
3784 std::shared_ptr<AisTargetData> td = it.second;
3786 if (NULL != td) UpdateOneCPA(td.get());
3790void AisDecoder::UpdateAllTracks(
void) {
3792 for (
const auto &it : GetTargetList()) {
3793 std::shared_ptr<AisTargetData> td = it.second;
3795 if (NULL != td) UpdateOneTrack(td.get());
3801 if (!ptarget->b_positionOnceValid)
return;
3803 if (ptarget->m_ptrack.size() > 0) {
3805 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3806 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3809 ptarget->m_ptrack.pop_back();
3810 ptarget->b_positionDoubtful =
true;
3817 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3820 ptrackpoint.m_lat = ptarget->Lat;
3821 ptrackpoint.m_lon = ptarget->Lon;
3822 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3824 ptarget->m_ptrack.push_back(ptrackpoint);
3826 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3828 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3830 t->SetName(wxString::Format(
3831 _T(
"AIS %s (%u) %s %s"), ptarget->GetFullName().c_str(),
3832 ptarget->MMSI, wxDateTime::Now().FormatISODate().c_str(),
3833 wxDateTime::Now().FormatISOTime().c_str()));
3834 g_TrackList.push_back(t);
3836 m_persistent_tracks[ptarget->MMSI] = t;
3838 t = m_persistent_tracks[ptarget->MMSI];
3841 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3843 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3846 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3847 tp1->m_lon, tp, tp1, t);
3857 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3859 ptarget->m_ptrack.erase(
3860 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3862 return track.m_time < test_time;
3864 ptarget->m_ptrack.end());
3869void AisDecoder::DeletePersistentTrack(
Track *track) {
3870 for (std::map<int, Track *>::iterator iterator = m_persistent_tracks.begin();
3871 iterator != m_persistent_tracks.end(); iterator++) {
3872 if (iterator->second == track) {
3873 int mmsi = iterator->first;
3874 m_persistent_tracks.erase(iterator);
3876 if (0 == m_persistent_tracks.count(mmsi)) {
3877 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3878 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
3880 if (props->m_bPersistentTrack) {
3884 std::shared_ptr<AisTargetData> td =
3885 Get_Target_Data_From_MMSI(mmsi);
3887 props->m_bPersistentTrack =
false;
3888 td->b_mPropPersistTrack =
false;
3890 if (!m_callbacks.confirm_stop_track()) {
3891 props->m_bPersistentTrack =
true;
3903void AisDecoder::UpdateAllAlarms(
void) {
3904 m_bGeneralAlert =
false;
3907 for (
const auto &it : GetTargetList()) {
3908 std::shared_ptr<AisTargetData> td = it.second;
3912 if (!m_bGeneralAlert) {
3914 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3915 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3916 m_bGeneralAlert =
true;
3919 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3920 m_bGeneralAlert =
false;
3923 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3924 m_bGeneralAlert =
false;
3927 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3930 if (td->Class == AIS_SART && td->NavStatus == 14)
3931 m_bGeneralAlert =
true;
3934 if ((td->Class == AIS_DSC) &&
3935 ((td->ShipType == 12) || (td->ShipType == 16)))
3936 m_bGeneralAlert =
true;
3939 ais_alert_type this_alarm = AIS_NO_ALERT;
3942 if (td->Class == AIS_SART && td->NavStatus == 14)
3943 this_alarm = AIS_ALERT_SET;
3946 if ((td->Class == AIS_DSC) &&
3947 ((td->ShipType == 12) || (td->ShipType == 16)))
3948 this_alarm = AIS_ALERT_SET;
3950 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3951 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3954 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3955 td->n_alert_state = AIS_NO_ALERT;
3961 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3962 (td->SOG <= g_ShowMoored_Kts)) {
3963 td->n_alert_state = AIS_NO_ALERT;
3969 if (td->Range_NM > g_CPAMax_NM) {
3970 td->n_alert_state = AIS_NO_ALERT;
3975 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3976 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3977 (td->Class != AIS_METEO)) {
3979 if (td->TCPA < g_TCPA_Max) {
3980 if (td->b_isFollower)
3981 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3983 this_alarm = AIS_ALERT_SET;
3986 if (td->b_isFollower)
3987 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3989 this_alarm = AIS_ALERT_SET;
3997 if (g_bAIS_ACK_Timeout || (td->Class == AIS_SART) ||
3998 ((td->Class == AIS_DSC) &&
3999 ((td->ShipType == 12) || (td->ShipType == 16)))) {
4000 if (td->b_in_ack_timeout) {
4001 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
4002 if (delta.GetMinutes() > g_AckTimeout_Mins)
4003 td->b_in_ack_timeout =
false;
4009 if (td->b_in_ack_timeout) {
4010 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
4014 td->n_alert_state = this_alarm;
4020 ptarget->Range_NM = -1.;
4029 DistanceBearingMercator(ptarget->Lat, ptarget->Lon, gLat, gLon, &brg, &dist);
4030 ptarget->Range_NM = dist;
4033 if (dist <= 1e-5) ptarget->Brg = -1.0;
4035 if (!ptarget->b_positionOnceValid || !bGPSValid) {
4036 ptarget->bCPA_Valid =
false;
4040 if (ptarget->Class == AIS_METEO) {
4041 ptarget->bCPA_Valid =
false;
4050 if (ptarget->b_OwnShip) {
4052 ptarget->TCPA = -100;
4053 ptarget->bCPA_Valid =
false;
4057 double cpa_calc_ownship_cog = gCog;
4058 double cpa_calc_target_cog = ptarget->COG;
4061 if (std::isnan(gSog) || (gSog > 102.2)) {
4062 ptarget->bCPA_Valid =
false;
4067 if (std::isnan(gCog) || gCog == 360.0) {
4069 cpa_calc_ownship_cog =
4073 ptarget->bCPA_Valid =
false;
4079 if (ptarget->COG == 360.0) {
4080 if (ptarget->SOG > 102.2) {
4081 ptarget->bCPA_Valid =
false;
4083 }
else if (ptarget->SOG < .01)
4084 cpa_calc_target_cog =
4088 ptarget->bCPA_Valid =
false;
4094 double v0 = gSog * 1852.;
4095 double v1 = ptarget->SOG * 1852.;
4097 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4101 ptarget->bCPA_Valid =
false;
4108 double east1 = (ptarget->Lon - gLon) * 60 * 1852;
4109 double north1 = (ptarget->Lat - gLat) * 60 * 1852;
4111 double east = east1 * (cos(gLat * PI / 180.));
4113 double north = north1;
4116 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4117 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4118 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4119 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4122 double fc = (v0 * cosa) - (v1 * cosb);
4123 double fs = (v0 * sina) - (v1 * sinb);
4125 double d = (fc * fc) + (fs * fs);
4133 tcpa = ((fc * east) + (fs * north)) / d;
4136 ptarget->TCPA = tcpa * 60.;
4141 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4143 ll_gc_ll(gLat, gLon, cpa_calc_ownship_cog, gSog * tcpa, &OwnshipLatCPA,
4145 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4146 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4149 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4152 ptarget->bCPA_Valid =
true;
4154 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4158void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4161 if (m_ptentative_dsctarget) {
4162 ProcessDSx(m_dsc_last_string,
true);
4166void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4171 wxDateTime now = wxDateTime::Now();
4174 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4177 auto it = current_targets.begin();
4178 std::vector<int> remove_array;
4180 while (it != current_targets.end()) {
4181 if (it->second == NULL)
4183 current_targets.erase(it);
4188 std::shared_ptr<AisTargetData> xtd = it->second;
4190 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4191 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4203 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4205 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4206 double iECD_LostTimeOut = 0.0;
4210 if (xtd->Class == AIS_CLASS_B) {
4211 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR))
4212 iECD_LostTimeOut = 18 * 60;
4214 iECD_LostTimeOut = 180;
4216 if (xtd->Class == AIS_CLASS_A) {
4217 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4219 iECD_LostTimeOut = 18 * 60;
4221 iECD_LostTimeOut = 60;
4223 iECD_LostTimeOut = 60;
4226 if ((target_posn_age > iECD_LostTimeOut) &&
4227 (xtd->Class != AIS_GPSG_BUDDY))
4228 xtd->b_active =
false;
4230 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4231 }
else if (g_bMarkLost) {
4232 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4233 (xtd->Class != AIS_GPSG_BUDDY))
4234 xtd->b_active =
false;
4237 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4238 removelost_Mins = 18.0;
4242 if (g_bRemoveLost || g_bInlandEcdis) {
4244 (xtd->Class == AIS_ARPA &&
4246 if (((target_posn_age > removelost_Mins * 60) &&
4247 (xtd->Class != AIS_GPSG_BUDDY)) ||
4252 xtd->b_positionOnceValid =
false;
4260 long mmsi_long = xtd->MMSI;
4261 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4266 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4267 xtd->b_removed =
true;
4269 remove_array.push_back(xtd->MMSI);
4276 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
4278 if (xtd->MMSI == props->MMSI) {
4279 if (props->m_bignore) {
4280 remove_array.push_back(xtd->MMSI);
4281 xtd->b_removed =
true;
4292 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4293 auto itd = current_targets.find(remove_array[i]);
4294 if (itd != current_targets.end()) {
4295 std::shared_ptr<AisTargetData> td = itd->second;
4296 current_targets.erase(itd);
4305 m_bSuppressed =
false;
4306 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4307 (g_bShowScaled && g_bAllowShowScaled))
4308 m_bSuppressed =
true;
4310 m_bAIS_Audio_Alert_On =
false;
4319 std::shared_ptr<AisTargetData> palert_target = NULL;
4320 int audioType = AISAUDIO_NONE;
4322 if (!g_pais_alert_dialog_active) {
4323 pAISMOBRoute = NULL;
4324 double tcpa_min = 1e6;
4325 double sart_range = 1e6;
4326 std::shared_ptr<AisTargetData> palert_target_cpa = NULL;
4327 std::shared_ptr<AisTargetData> palert_target_sart = NULL;
4328 std::shared_ptr<AisTargetData> palert_target_dsc = NULL;
4330 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4331 std::shared_ptr<AisTargetData> td = it->second;
4333 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4334 if (g_bAIS_CPA_Alert && td->b_active) {
4335 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4336 if (td->TCPA < tcpa_min) {
4337 tcpa_min = td->TCPA;
4338 palert_target_cpa = td;
4342 }
else if ((td->Class == AIS_DSC) &&
4343 ((td->ShipType == 12) || (td->ShipType == 16))) {
4345 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4346 palert_target_dsc = td;
4349 td->b_isDSCtarget =
false;
4354 else if (td->Class == AIS_SART) {
4356 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4357 if (td->Range_NM < sart_range) {
4358 tcpa_min = sart_range;
4359 palert_target_sart = td;
4369 palert_target = palert_target_cpa;
4370 if (palert_target) audioType = AISAUDIO_CPA;
4372 if (palert_target_sart) {
4373 palert_target = palert_target_sart;
4374 audioType = AISAUDIO_SART;
4377 if (palert_target_dsc) {
4378 palert_target = palert_target_dsc;
4379 audioType = AISAUDIO_DSC;
4383 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4388 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4391std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
int mmsi) {
4392 if (AISTargetList.find(mmsi) == AISTargetList.end())
4395 return AISTargetList[mmsi];
4398ArrayOfMmsiProperties g_MMSI_Props_Array;
4402MmsiProperties::MmsiProperties(wxString &spec) {
4404 wxStringTokenizer tkz(spec, _T(
";"));
4407 s = tkz.GetNextToken();
4412 s = tkz.GetNextToken();
4414 if (s.Upper() == _T(
"ALWAYS"))
4415 TrackType = TRACKTYPE_ALWAYS;
4416 else if (s.Upper() == _T(
"NEVER"))
4417 TrackType = TRACKTYPE_NEVER;
4420 s = tkz.GetNextToken();
4422 if (s.Upper() == _T(
"IGNORE")) m_bignore =
true;
4425 s = tkz.GetNextToken();
4427 if (s.Upper() == _T(
"MOB")) m_bMOB =
true;
4430 s = tkz.GetNextToken();
4432 if (s.Upper() == _T(
"VDM")) m_bVDM =
true;
4435 s = tkz.GetNextToken();
4437 if (s.Upper() == _T(
"FOLLOWER")) m_bFollower =
true;
4440 s = tkz.GetNextToken();
4442 if (s.Upper() == _T(
"PERSIST")) m_bPersistentTrack =
true;
4445 s = tkz.GetNextToken();
4447 m_ShipName = s.Upper();
4451MmsiProperties::~MmsiProperties() {}
4453void MmsiProperties::Init(
void) {
4455 TrackType = TRACKTYPE_DEFAULT;
4459 m_bFollower =
false;
4460 m_bPersistentTrack =
false;
4461 m_ShipName = wxEmptyString;
4464wxString MmsiProperties::Serialize(
void) {
4468 sMMSI.Printf(_T(
"%d"), MMSI);
4472 if (TRACKTYPE_ALWAYS == TrackType)
4473 sMMSI << _T(
"always");
4474 else if (TRACKTYPE_NEVER == TrackType)
4475 sMMSI << _T(
"never");
4480 sMMSI << _T(
"ignore");
4495 sMMSI << _T(
"Follower");
4499 if (m_bPersistentTrack) {
4500 sMMSI << _T(
"PERSIST");
4504 if (m_ShipName == wxEmptyString) {
4505 m_ShipName = GetShipNameFromFile(MMSI);
4507 sMMSI << m_ShipName;
4512 AIS_Target_Name_Hash *AISTargetNamesC,
4513 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4514 if (g_benableAISNameCache) {
4515 wxString ship_name = wxEmptyString;
4518 if (!pTargetData->b_nameValid) {
4519 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4520 if (it != AISTargetNamesC->end()) {
4521 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4522 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4523 ship_name.length() + 1);
4524 pTargetData->b_nameValid =
true;
4525 pTargetData->b_nameFromCache =
true;
4526 }
else if (!g_bUseOnlyConfirmedAISName) {
4527 it = AISTargetNamesNC->find(mmsi);
4528 if (it != AISTargetNamesNC->end()) {
4529 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4530 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4531 ship_name.length() + 1);
4532 pTargetData->b_nameValid =
true;
4533 pTargetData->b_nameFromCache =
true;
4538 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4539 (pTargetData->MID == 19) ||
4540 (pTargetData->MID == 123) ||
4541 (pTargetData->MID == 124)) {
4543 pTargetData->b_nameFromCache =
false;
4544 ship_name = trimAISField(pTargetData->ShipName);
4545 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4546 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4548 AISTargetNamesC->end()) {
4549 if ((*AISTargetNamesC)[mmsi] ==
4551 if (itNC != AISTargetNamesNC->end()) {
4553 AISTargetNamesNC->erase(itNC);
4556 if (itNC != AISTargetNamesNC->end()) {
4558 if ((*AISTargetNamesNC)[mmsi] ==
4561 (*AISTargetNamesC)[mmsi] = ship_name;
4563 AISTargetNamesNC->erase(itNC);
4566 (*AISTargetNamesNC)[mmsi] = ship_name;
4568 if (g_bUseOnlyConfirmedAISName)
4569 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4570 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4572 (*AISTargetNamesNC)[mmsi] = ship_name;
4577 AISTargetNamesNC->end()) {
4578 if ((*AISTargetNamesNC)[mmsi] ==
4581 (*AISTargetNamesC)[mmsi] = ship_name;
4583 AISTargetNamesNC->erase(itNC);
4585 (*AISTargetNamesNC)[mmsi] = ship_name;
4588 (*AISTargetNamesNC)[mmsi] = ship_name;
4590 if (g_bUseOnlyConfirmedAISName) {
4591 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4592 strncpy(pTargetData->ShipName,
"Unknown ",
4600wxString GetShipNameFromFile(
int nmmsi) {
4601 wxString name = wxEmptyString;
4602 if (g_benableAISNameCache) {
4603 std::ifstream infile(AISTargetNameFileName.mb_str());
4606 while (getline(infile, line)) {
4607 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()), _T(
","));
4608 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4609 name = tokenizer.GetNextToken().Trim();
4612 tokenizer.GetNextToken();
4621int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4625 if ((!lon_bits || lon_bits == 999) && siteID) {
4628 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4629 if (points.size()) {
4630 for (
const auto &point : points) {
4632 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4634 new_mmsi = point.mmsi;
4640 if (!found && !lon_bits) {
4645 double lon_tentative = 181.;
4646 double lat_tentative = 91.;
4648 if (lon_bits == 25) {
4649 if (m_lon & 0x01000000)
4650 m_lon |= 0xFE000000;
4651 lon_tentative = m_lon / 60000.;
4653 if (m_lat & 0x00800000)
4654 m_lat |= 0xFF000000;
4655 lat_tentative = m_lat / 60000.;
4657 }
else if (lon_bits == 28) {
4658 if (m_lon & 0x08000000)
4659 m_lon |= 0xf0000000;
4660 lon_tentative = m_lon / 600000.;
4662 if (m_lat & 0x04000000)
4663 m_lat |= 0xf8000000;
4664 lat_tentative = m_lat / 600000.;
4669 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4670 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4678 static int nextMeteommsi = 199400000;
4679 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4681 if (lon_bits != 999 && points.size()) {
4682 wxString t_lat, t_lon;
4683 for (
const auto &point : points) {
4685 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4687 new_mmsi = point.mmsi;
4697 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4698 new_mmsi = nextMeteommsi;
4703bool isBuoyMmsi(
const int msi) {
4708 int mid = msi / 1000000;
4709 if ((mid > 200 && mid < 800) || mid >= 970) {
Global state for AIS decoder.
int GetInt(int sp, int len, bool signed_flag=false)
sp is starting bit, 1-based
EventVar plugin_msg
A JSON message should be sent.
EventVar new_track
Notified on new track creation.
EventVar info_update
Notified when AIS user dialogs should update.
EventVar touch_state
Notified when gFrame->TouchAISActive() should be invoked.
Add a new point to the list of Meteo stations.
const void Notify()
Notify all listeners, no data supplied.
void LogInputMessage(const wxString &msg, const wxString &stream_name, bool b_filter, bool b_error=false, const wxString error_msg=wxEmptyString)
Logs an input message with context information.
A regular Nmea0183 message.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Adds a std::shared<void> element to wxCommandEvent.
A parsed SignalK message over ipv4.
Represents a single point in a track.
Represents a track, which is a series of connected track points.
The JSON document writer.
void Write(const wxJSONValue &value, wxString &str)
Write the JSONvalue object to a JSON text.
Raw messages layer, supports sending and recieving navmsg messages.