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);
109EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
110EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
113static const
double ms_to_knot_factor = 1.9438444924406;
119static
bool b_firstrx;
120static
int first_rx_ticks;
122static
double arpa_ref_hdg = NAN;
124static inline
double GeodesicRadToDeg(
double rads) {
125 return rads * 180.0 / M_PI;
128static inline double MS2KNOTS(
double ms) {
return ms * 1.9438444924406; }
130int AisMeteoNewMmsi(
int,
int,
int,
int,
int);
134 AIS_Target_Name_Hash *AISTargetNamesC,
135 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
138 : m_signalk_selfid(
""), m_callbacks(callbacks) {
140 AISTargetNamesC =
new AIS_Target_Name_Hash;
141 AISTargetNamesNC =
new AIS_Target_Name_Hash;
143 if (g_benableAISNameCache) {
144 if (wxFileName::FileExists(AISTargetNameFileName)) {
146 if (infile.Open(AISTargetNameFileName)) {
147 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
148 wxString line = infile.GetFirstLine();
149 while (!infile.Eof()) {
150 if (line.IsSameAs(wxT(
"+++==Confirmed Entry's==+++")))
151 HashFile = AISTargetNamesC;
153 if (line.IsSameAs(wxT(
"+++==Non Confirmed Entry's==+++")))
154 HashFile = AISTargetNamesNC;
156 wxStringTokenizer tokenizer(line, _T(
","));
157 int mmsi = wxAtoi(tokenizer.GetNextToken());
158 wxString name = tokenizer.GetNextToken().Trim();
159 (*HashFile)[mmsi] = name;
162 line = infile.GetNextLine();
169 BuildERIShipTypeHash();
171 g_pais_alert_dialog_active =
nullptr;
172 m_bAIS_Audio_Alert_On =
false;
176 m_bAIS_AlertPlaying =
false;
178 TimerAIS.SetOwner(
this, TIMER_AIS1);
179 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
181 m_ptentative_dsctarget = NULL;
182 m_dsc_timer.SetOwner(
this, TIMER_DSC);
192AisDecoder::~AisDecoder(
void) {
201 if (outfile.Open(AISTargetNameFileName)) {
203 content = wxT(
"+++==Confirmed Entry's==+++");
204 AIS_Target_Name_Hash::iterator it;
205 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
206 content.append(_T(
"\r\n"));
207 content.append(wxString::Format(wxT(
"%i"), it->first));
208 content.append(_T(
",")).append(it->second);
210 content.append(_T(
"\r\n"));
211 content.append(_T(
"+++==Non Confirmed Entry's==+++"));
212 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
213 content.append(_T(
"\r\n"));
214 content.append(wxString::Format(wxT(
"%i"), it->first));
215 content.append(_T(
",")).append(it->second);
217 outfile.Write(content);
221 AISTargetNamesC->clear();
222 delete AISTargetNamesC;
223 AISTargetNamesNC->clear();
224 delete AISTargetNamesNC;
229 m_AIS_Audio_Alert_Timer.Stop();
234 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
236 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
240bool IsTargetOnTheIgnoreList(
const int &mmsi) {
242 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
243 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
245 if (props->m_bignore) {
253void AisDecoder::InitCommListeners(
void) {
256 auto &msgbus = NavMsgBus::GetInstance();
261 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
264 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
265 HandleN0183_AIS(n0183_msg);
270 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
274 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
275 HandleN0183_AIS(n0183_msg);
280 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
283 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
284 HandleN0183_AIS(n0183_msg);
289 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
292 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
293 HandleN0183_AIS(n0183_msg);
298 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
302 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
303 HandleN0183_AIS(n0183_msg);
308 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
311 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
312 HandleN0183_AIS(n0183_msg);
317 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
320 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
321 HandleN0183_AIS(n0183_msg);
326 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
329 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
330 HandleN0183_AIS(n0183_msg);
335 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
337 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
342 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
343 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
345 HandleN2K_129038(UnpackEvtPointer<Nmea2000Msg>(ev));
350 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
351 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
353 HandleN2K_129039(UnpackEvtPointer<Nmea2000Msg>(ev));
358 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
359 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
361 HandleN2K_129041(UnpackEvtPointer<Nmea2000Msg>(ev));
366 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
367 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
369 HandleN2K_129794(UnpackEvtPointer<Nmea2000Msg>(ev));
374 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
375 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
377 HandleN2K_129809(UnpackEvtPointer<Nmea2000Msg>(ev));
382 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
383 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
385 HandleN2K_129810(UnpackEvtPointer<Nmea2000Msg>(ev));
390 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
391 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
393 HandleN2K_129793(UnpackEvtPointer<Nmea2000Msg>(ev));
397bool AisDecoder::HandleN0183_AIS(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
398 std::string str = n0183_msg->payload;
399 wxString sentence(str.c_str());
400 DecodeN0183(sentence);
405bool AisDecoder::HandleN2K_129038(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
406 std::vector<unsigned char> v = n2k_msg->payload;
409 tN2kAISRepeat Repeat;
420 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
421 tN2kAISTransceiverInformation AISTransceiverInformation;
423 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
424 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
425 NavStat, AISTransceiverInformation)) {
428 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
432 long mmsi_long = mmsi;
433 std::shared_ptr<AisTargetData> pTargetData = 0;
434 bool bnewtarget =
false;
436 auto it = AISTargetList.find(mmsi);
437 if (it == AISTargetList.end())
439 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
443 pTargetData = it->second;
446 wxDateTime now = wxDateTime::Now();
450 pTargetData->MMSI = mmsi;
451 pTargetData->MID = MessageID;
452 pTargetData->MMSI = mmsi;
453 pTargetData->Class = AIS_CLASS_A;
455 if (97 == pTargetData->MMSI / 10000000) {
456 pTargetData->Class = AIS_SART;
458 pTargetData->StaticReportTicks = now.GetTicks();
460 pTargetData->NavStatus = (ais_nav_status)NavStat;
461 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
462 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
463 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
464 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
465 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
467 pTargetData->ROTAIS = ROT;
469 double rot_dir = 1.0;
481 pTargetData->b_OwnShip =
482 AISTransceiverInformation ==
483 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
484 pTargetData->b_active =
true;
485 pTargetData->b_lost =
false;
486 pTargetData->b_positionOnceValid =
true;
487 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
488 pTargetData->PositionReportTicks = now.GetTicks();
490 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
491 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
500bool AisDecoder::HandleN2K_129039(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
501 std::vector<unsigned char> v = n2k_msg->payload;
516 tN2kAISRepeat Repeat;
526 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
527 tN2kAISTransceiverInformation AISTransceiverInformation;
529 bool DSC, Band, Msg22, State, Display;
532 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
533 Accuracy, RAIM, Seconds, COG, SOG,
534 AISTransceiverInformation, Heading, Unit, Display, DSC,
535 Band, Msg22, Mode, State)) {
538 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
542 long mmsi_long = mmsi;
543 std::shared_ptr<AisTargetData> pTargetData = 0;
544 bool bnewtarget =
false;
546 auto it = AISTargetList.find(mmsi);
547 if (it == AISTargetList.end())
549 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
553 pTargetData = it->second;
556 wxDateTime now = wxDateTime::Now();
560 pTargetData->MMSI = mmsi;
561 pTargetData->MID = MessageID;
562 if (!isBuoyMmsi(mmsi))
563 pTargetData->Class = AIS_CLASS_B;
565 pTargetData->Class = AIS_BUOY;
567 pTargetData->NavStatus = UNDEFINED;
568 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
569 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
570 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
571 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
572 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
574 pTargetData->b_positionOnceValid =
true;
575 pTargetData->b_active =
true;
576 pTargetData->b_lost =
false;
577 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
578 pTargetData->PositionReportTicks = now.GetTicks();
579 pTargetData->b_OwnShip =
580 AISTransceiverInformation ==
581 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
583 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
584 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
594bool AisDecoder::HandleN2K_129041(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
595 std::vector<unsigned char> v = n2k_msg->payload;
597 tN2kAISAtoNReportData data;
600 struct tN2kAISAtoNReportData {
602 tN2kAISRepeat Repeat;
611 double PositionReferenceStarboard ;
612 double PositionReferenceTrueNorth;
613 tN2kAISAtoNType AtoNType;
614 bool OffPositionIndicator;
615 bool VirtualAtoNFlag;
616 bool AssignedModeFlag;
617 tN2kGNSStype GNSSType;
619 tN2kAISTransceiverInformation AISTransceiverInformation;
620 char AtoNName[34 + 1];
623 if (ParseN2kPGN129041(v, data)) {
624 int mmsi = data.UserID;
626 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
630 long mmsi_long = mmsi;
631 std::shared_ptr<AisTargetData> pTargetData = 0;
632 bool bnewtarget =
false;
634 auto it = AISTargetList.find(mmsi);
635 if (it == AISTargetList.end())
637 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
641 pTargetData = it->second;
645 pTargetData->MMSI = mmsi;
647 wxDateTime now = wxDateTime::Now();
650 int offpos = data.OffPositionIndicator;
651 int virt = data.VirtualAtoNFlag;
654 pTargetData->NavStatus = ATON_VIRTUAL;
656 pTargetData->NavStatus = ATON_REAL;
658 pTargetData->m_utc_sec = data.Seconds;
660 if (pTargetData->m_utc_sec <= 59) {
661 pTargetData->NavStatus += 1;
662 if (offpos) pTargetData->NavStatus += 1;
665 data.AtoNName[34] = 0;
666 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
667 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
668 pTargetData->b_nameValid =
true;
669 pTargetData->MID = 124;
671 pTargetData->ShipType = data.AtoNType;
672 pTargetData->Class = AIS_ATON;
674 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
675 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
676 pTargetData->b_positionDoubtful =
false;
677 pTargetData->b_positionOnceValid =
true;
678 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
679 pTargetData->PositionReportTicks = now.GetTicks();
683 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
684 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
686 touch_state.Notify();
693bool AisDecoder::HandleN2K_129794(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
694 std::vector<unsigned char> v = n2k_msg->payload;
697 tN2kAISRepeat Repeat;
701 char Name[SHIP_NAME_LEN];
710 char Destination[DESTINATION_LEN];
711 tN2kAISVersion AISversion;
712 tN2kGNSStype GNSStype;
714 tN2kAISTranceiverInfo AISinfo;
716 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
717 VesselType, Length, Beam, PosRefStbd, PosRefBow,
718 ETAdate, ETAtime, Draught, Destination, AISversion,
719 GNSStype, DTE, AISinfo)) {
722 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
726 long mmsi_long = mmsi;
727 std::shared_ptr<AisTargetData> pTargetData = 0;
728 bool bnewtarget =
false;
730 auto it = AISTargetList.find(mmsi);
731 if (it == AISTargetList.end())
733 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
737 pTargetData = it->second;
741 pTargetData->MMSI = mmsi;
742 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
743 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
744 Name[
sizeof(Name) - 1] = 0;
745 pTargetData->b_nameValid =
true;
746 pTargetData->MID = 124;
748 pTargetData->b_OwnShip =
750 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
752 pTargetData->DimA = PosRefBow;
753 pTargetData->DimB = Length - PosRefBow;
754 pTargetData->DimC = Beam - PosRefStbd;
755 pTargetData->DimD = PosRefStbd;
756 pTargetData->Draft = Draught;
757 pTargetData->IMO = IMOnumber;
758 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
759 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
760 pTargetData->ShipType = (
unsigned char)VesselType;
761 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
762 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
763 Destination[
sizeof(Destination) - 1] = 0;
765 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
766 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
767 wxDateTime t((time_t)secs);
769 wxDateTime tz = t.ToUTC();
770 pTargetData->ETA_Mo = tz.GetMonth() + 1;
771 pTargetData->ETA_Day = tz.GetDay();
772 pTargetData->ETA_Hr = tz.GetHour();
773 pTargetData->ETA_Min = tz.GetMinute();
777 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
778 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
786bool AisDecoder::HandleN2K_129809(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
787 std::vector<unsigned char> v = n2k_msg->payload;
790 tN2kAISRepeat Repeat;
794 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
797 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
801 long mmsi_long = mmsi;
802 std::shared_ptr<AisTargetData> pTargetData = 0;
803 bool bnewtarget =
false;
805 auto it = AISTargetList.find(mmsi);
806 if (it == AISTargetList.end())
808 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
812 pTargetData = it->second;
816 pTargetData->MMSI = mmsi;
817 Name[
sizeof(Name) - 1] = 0;
818 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
819 pTargetData->b_nameValid =
true;
820 pTargetData->MID = 124;
822 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
823 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
833bool AisDecoder::HandleN2K_129810(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
834 std::vector<unsigned char> v = n2k_msg->payload;
837 tN2kAISRepeat Repeat;
846 uint32_t MothershipID;
848 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
849 Callsign, Length, Beam, PosRefStbd, PosRefBow,
853 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
857 long mmsi_long = mmsi;
858 std::shared_ptr<AisTargetData> pTargetData = 0;
859 bool bnewtarget =
false;
861 auto it = AISTargetList.find(mmsi);
862 if (it == AISTargetList.end())
864 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
868 pTargetData = it->second;
872 pTargetData->MMSI = mmsi;
873 pTargetData->DimA = PosRefBow;
874 pTargetData->DimB = Length - PosRefBow;
875 pTargetData->DimC = Beam - PosRefStbd;
876 pTargetData->DimD = PosRefStbd;
877 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
878 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
879 pTargetData->ShipType = (
unsigned char)VesselType;
881 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
882 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
891bool AisDecoder::HandleN2K_129793(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
892 std::vector<unsigned char> v = n2k_msg->payload;
895 tN2kAISRepeat Repeat;
899 unsigned int SecondsSinceMidnight;
900 unsigned int DaysSinceEpoch;
902 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
903 SecondsSinceMidnight, DaysSinceEpoch)) {
904 wxDateTime now = wxDateTime::Now();
910 long mmsi_long = mmsi;
911 std::shared_ptr<AisTargetData> pTargetData = 0;
912 bool bnewtarget =
false;
914 auto it = AISTargetList.find(mmsi);
915 if (it == AISTargetList.end())
917 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
921 pTargetData = it->second;
925 pTargetData->MMSI = mmsi;
926 pTargetData->Class = AIS_BASE;
928 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
929 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
930 pTargetData->b_positionDoubtful =
false;
931 pTargetData->b_positionOnceValid =
true;
932 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
933 pTargetData->PositionReportTicks = now.GetTicks();
937 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
938 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
954void AisDecoder::BuildERIShipTypeHash(
void) {
955 make_hash_ERI(8000, _(
"Vessel, type unknown"));
956 make_hash_ERI(8150, _(
"Freightbarge"));
957 make_hash_ERI(8160, _(
"Tankbarge"));
958 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
959 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
960 make_hash_ERI(8430, _(
"Pushboat, single"));
961 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
962 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
963 make_hash_ERI(8490, _(
"Bunkership"));
964 make_hash_ERI(8010, _(
"Motor freighter"));
965 make_hash_ERI(8020, _(
"Motor tanker"));
966 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
967 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
968 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
969 make_hash_ERI(8030, _(
"Container vessel"));
970 make_hash_ERI(8040, _(
"Gas tanker"));
971 make_hash_ERI(8050, _(
"Motor freighter, tug"));
972 make_hash_ERI(8060, _(
"Motor tanker, tug"));
973 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
974 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
975 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
976 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
977 make_hash_ERI(8110, _(
"Tug, freighter"));
978 make_hash_ERI(8120, _(
"Tug, tanker"));
979 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
980 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
981 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
982 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
983 make_hash_ERI(8170, _(
"Freightbarge with containers"));
984 make_hash_ERI(8180, _(
"Tankbarge, gas"));
985 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
986 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
987 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
988 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
989 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
990 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
991 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
992 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
993 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
994 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
996 _(
"Pushtow, two barges at least one tanker or gas barge"));
998 _(
"Pushtow, three barges at least one tanker or gas barge"));
1000 _(
"Pushtow, four barges at least one tanker or gas barge"));
1002 _(
"Pushtow, five barges at least one tanker or gas barge"));
1004 _(
"Pushtow, six barges at least one tanker or gas barge"));
1006 _(
"Pushtow, seven barges at least one tanker or gas barge"));
1008 _(
"Pushtow, eight barges at least one tanker or gas barge"));
1010 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
1011 make_hash_ERI(8400, _(
"Tug, single"));
1012 make_hash_ERI(8410, _(
"Tug, one or more tows"));
1013 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
1014 make_hash_ERI(8430, _(
"Pushboat, single"));
1015 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
1016 make_hash_ERI(8441, _(
"Ferry"));
1017 make_hash_ERI(8442, _(
"Red cross ship"));
1018 make_hash_ERI(8443, _(
"Cruise ship"));
1019 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
1020 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
1021 "cable-ship, buoy-ship, dredge"));
1022 make_hash_ERI(8480, _(
"Fishing boat"));
1023 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
1024 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
1025 make_hash_ERI(1510, _(
"Unit carrier maritime"));
1026 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
1027 make_hash_ERI(1530, _(
"Tanker"));
1028 make_hash_ERI(1540, _(
"Liquified gas tanker"));
1029 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
1030 make_hash_ERI(1900, _(
"Fast ship"));
1031 make_hash_ERI(1910, _(
"Hydrofoil"));
1037void AisDecoder::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg) {
1038 rapidjson::Document root;
1040 root.Parse(sK_msg->raw_message);
1042 if (root.HasParseError())
return;
1044 if (root.HasMember(
"self")) {
1050 if (m_signalk_selfid.IsEmpty()) {
1055 int meteo_SiteID = 0;
1056 if (root.HasMember(
"context") && root[
"context"].IsString()) {
1057 wxString context = root[
"context"].GetString();
1058 if (context == m_signalk_selfid) {
1060 wxLogMessage(_T(
"** Ignore context own ship.."));
1064 wxString mmsi_string;
1065 if (context.StartsWith(_T(
"vessels.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1066 context.StartsWith(_T(
"atons.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1067 context.StartsWith(_T(
"aircraft.urn:mrn:imo:mmsi:"), &mmsi_string)) {
1070 if (mmsi_string.ToLong(&mmsi)) {
1075 }
else if (context.StartsWith(_T(
"meteo.urn:mrn:imo:mmsi:"),
1078 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
1079 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
1082 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
1093 if (g_pMUX && g_pMUX->IsLogActive()) {
1095 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
1100 if (IsTargetOnTheIgnoreList(mmsi))
return;
1103 if (mmsi == g_OwnShipmmsi)
return;
1108 writer.
Write(root, dbg);
1110 wxString msg( _T(
"AisDecoder::OnEvtSignalK: ") );
1114 std::shared_ptr<AisTargetData> pTargetData = 0;
1115 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1116 bool bnewtarget =
false;
1117 int last_report_ticks;
1119 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
1122 pTargetData->MMSI = mmsi;
1123 getMmsiProperties(pTargetData);
1124 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
1125 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
1126 itr != root[
"updates"].End(); ++itr) {
1127 handleUpdate(pTargetData, bnewtarget, *itr);
1132 if (97 == mmsi / 10000000) {
1133 pTargetData->Class = AIS_SART;
1134 }
else if (1994 == mmsi / 100000) {
1136 pTargetData->Class = AIS_METEO;
1137 pTargetData->met_data.original_mmsi = origin_mmsi;
1138 pTargetData->met_data.stationID = meteo_SiteID;
1141 wxString met_name = pTargetData->ShipName;
1142 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
1145 s_id << meteo_SiteID;
1146 id1 = wxAtoi(s_id.Mid(1, 3));
1147 id2 = wxAtoi(s_id.Mid(4, 3));
1148 met_name =
"METEO ";
1149 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
1150 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
1152 pTargetData->b_nameValid =
true;
1153 pTargetData->MID = 123;
1154 pTargetData->COG = -1.;
1155 pTargetData->HDG = 511;
1156 pTargetData->SOG = -1.;
1157 pTargetData->b_NoTrack =
true;
1158 pTargetData->b_show_track =
false;
1160 pTargetData->b_OwnShip =
false;
1161 AISTargetList[pTargetData->MMSI] = pTargetData;
1165void AisDecoder::handleUpdate(std::shared_ptr<AisTargetData> pTargetData,
1166 bool bnewtarget,
const rapidjson::Value &update) {
1167 wxString sfixtime =
"";
1169 if (update.HasMember(
"timestamp")) {
1170 sfixtime = update[
"timestamp"].GetString();
1172 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
1173 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
1174 itr != update[
"values"].End(); ++itr) {
1175 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
1178 wxDateTime now = wxDateTime::Now();
1179 pTargetData->m_utc_hour = now.ToUTC().GetHour();
1180 pTargetData->m_utc_min = now.ToUTC().GetMinute();
1181 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
1183 pTargetData->b_active =
true;
1184 pTargetData->b_lost =
false;
1186 if (pTargetData->b_positionOnceValid) {
1187 long mmsi_long = pTargetData->MMSI;
1189 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
1190 (
void *)mmsi_long, SELTYPE_AISTARGET);
1191 pSel->SetUserData(pTargetData->MMSI);
1193 UpdateOneCPA(pTargetData.get());
1194 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
1197void AisDecoder::updateItem(std::shared_ptr<AisTargetData> pTargetData,
1198 bool bnewtarget,
const rapidjson::Value &item,
1199 wxString &sfixtime)
const {
1200 if (item.HasMember(
"path") && item.HasMember(
"value")) {
1201 const wxString &update_path = item[
"path"].GetString();
1202 if (update_path == _T(
"navigation.position")) {
1203 if (item[
"value"].HasMember(
"latitude") &&
1204 item[
"value"].HasMember(
"longitude")) {
1205 wxDateTime now = wxDateTime::Now();
1207 double lat = item[
"value"][
"latitude"].GetDouble();
1208 double lon = item[
"value"][
"longitude"].GetDouble();
1209 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1210 pTargetData->PositionReportTicks = now.GetTicks();
1211 pTargetData->StaticReportTicks = now.GetTicks();
1212 pTargetData->Lat = lat;
1213 pTargetData->Lon = lon;
1214 pTargetData->b_positionOnceValid =
true;
1215 pTargetData->b_positionDoubtful =
false;
1222 }
else if (update_path == _T(
"navigation.speedOverGround") &&
1223 item[
"value"].IsNumber()) {
1224 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
1225 }
else if (update_path == _T(
"navigation.courseOverGroundTrue") &&
1226 item[
"value"].IsNumber()) {
1227 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1228 }
else if (update_path == _T(
"navigation.headingTrue") &&
1229 item[
"value"].IsNumber()) {
1230 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1231 }
else if (update_path == _T(
"navigation.rateOfTurn") &&
1232 item[
"value"].IsNumber()) {
1233 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
1234 }
else if (update_path == _T(
"design.aisShipType")) {
1235 if (item[
"value"].HasMember(
"id")) {
1236 if (!pTargetData->b_isDSCtarget) {
1237 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1240 }
else if (update_path == _T(
"atonType")) {
1241 if (item[
"value"].HasMember(
"id")) {
1242 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1244 }
else if (update_path == _T(
"virtual")) {
1245 if (item[
"value"].GetBool()) {
1246 pTargetData->NavStatus = ATON_VIRTUAL;
1248 pTargetData->NavStatus = ATON_REAL;
1250 }
else if (update_path == _T(
"offPosition")) {
1251 if (item[
"value"].GetBool()) {
1252 if (ATON_REAL == pTargetData->NavStatus) {
1253 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
1254 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
1255 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
1258 }
else if (update_path == _T(
"design.draft")) {
1259 if (item[
"value"].HasMember(
"maximum") &&
1260 item[
"value"][
"maximum"].IsNumber()) {
1261 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
1262 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
1264 if (item[
"value"].HasMember(
"current") &&
1265 item[
"value"][
"current"].IsNumber()) {
1266 double draft = item[
"value"][
"current"].GetDouble();
1268 pTargetData->Draft = draft;
1269 pTargetData->Euro_Draft = draft;
1272 }
else if (update_path == _T(
"design.length")) {
1273 if (pTargetData->DimB == 0) {
1274 if (item[
"value"].HasMember(
"overall")) {
1275 if (item[
"value"][
"overall"].IsNumber()) {
1276 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
1277 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
1279 pTargetData->DimB = 0;
1282 }
else if (update_path == _T(
"sensors.ais.class")) {
1283 wxString aisclass = item[
"value"].GetString();
1284 if (aisclass == _T(
"A")) {
1285 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
1286 }
else if (aisclass == _T(
"B")) {
1287 if (!pTargetData->b_isDSCtarget) {
1288 if (!isBuoyMmsi(pTargetData->MMSI))
1289 pTargetData->Class = AIS_CLASS_B;
1291 pTargetData->Class = AIS_BUOY;
1294 pTargetData->NavStatus = UNDEFINED;
1296 }
else if (aisclass == _T(
"BASE")) {
1297 pTargetData->Class = AIS_BASE;
1298 }
else if (aisclass == _T(
"ATON")) {
1299 pTargetData->Class = AIS_ATON;
1301 }
else if (update_path == _T(
"sensors.ais.fromBow")) {
1302 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
1303 int length = pTargetData->DimA;
1304 if (item[
"value"].IsNumber()) {
1305 pTargetData->DimA = item[
"value"].GetDouble();
1306 pTargetData->DimB = length - item[
"value"].GetDouble();
1309 }
else if (update_path == _T(
"design.beam")) {
1310 if (pTargetData->DimD == 0) {
1311 if (item[
"value"].IsNumber()) {
1312 pTargetData->Euro_Beam = item[
"value"].GetDouble();
1313 pTargetData->DimC = item[
"value"].GetDouble();
1315 pTargetData->DimD = 0;
1317 }
else if (update_path == _T(
"sensors.ais.fromCenter")) {
1318 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
1319 int beam = pTargetData->DimC;
1320 int center = beam / 2;
1321 if (item[
"value"].IsNumber()) {
1324 pTargetData->DimC = center + item[
"value"].GetDouble();
1325 pTargetData->DimD = beam - pTargetData->DimC;
1328 }
else if (update_path == _T(
"navigation.state")) {
1329 wxString state = item[
"value"].GetString();
1330 if (state == _T(
"motoring")) {
1331 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
1332 }
else if (state == _T(
"anchored")) {
1333 pTargetData->NavStatus = AT_ANCHOR;
1334 }
else if (state == _T(
"not under command")) {
1335 pTargetData->NavStatus = NOT_UNDER_COMMAND;
1336 }
else if (state == _T(
"restricted manouverability")) {
1337 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
1338 }
else if (state == _T(
"constrained by draft")) {
1339 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
1340 }
else if (state == _T(
"moored")) {
1341 pTargetData->NavStatus = MOORED;
1342 }
else if (state == _T(
"aground")) {
1343 pTargetData->NavStatus = AGROUND;
1344 }
else if (state == _T(
"fishing")) {
1345 pTargetData->NavStatus = FISHING;
1346 }
else if (state == _T(
"sailing")) {
1347 pTargetData->NavStatus = UNDERWAY_SAILING;
1348 }
else if (state == _T(
"hazardous material high speed")) {
1349 pTargetData->NavStatus = HSC;
1350 }
else if (state == _T(
"hazardous material wing in ground")) {
1351 pTargetData->NavStatus = WIG;
1352 }
else if (state == _T(
"ais-sart")) {
1353 pTargetData->NavStatus = RESERVED_14;
1355 pTargetData->NavStatus = UNDEFINED;
1357 }
else if (update_path == _T(
"navigation.destination.commonName")) {
1358 const wxString &destination = item[
"value"].GetString();
1359 pTargetData->Destination[0] =
'\0';
1360 strncpy(pTargetData->Destination, destination.c_str(),
1361 DESTINATION_LEN - 1);
1362 }
else if (update_path == _T(
"navigation.specialManeuver")) {
1363 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
1364 pTargetData->IMO < 1) {
1365 const wxString &bluesign = item[
"value"].GetString();
1366 if (_T(
"not engaged") == bluesign) {
1367 pTargetData->blue_paddle = 1;
1369 if (_T(
"engaged") == bluesign) {
1370 pTargetData->blue_paddle = 2;
1372 pTargetData->b_blue_paddle =
1373 pTargetData->blue_paddle == 2 ? true :
false;
1375 }
else if (update_path == _T(
"sensors.ais.designatedAreaCode")) {
1376 if (item[
"value"].GetInt() == 200) {
1377 pTargetData->b_hasInlandDac =
true;
1379 }
else if (update_path == _T(
"sensors.ais.functionalId")) {
1380 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
1382 pTargetData->b_isEuroInland =
true;
1386 }
else if (update_path ==
"environment.date") {
1387 wxString issued = item[
"value"].GetString();
1391 ParseGPXDateTime(tz, issued);
1392 pTargetData->met_data.day = tz.GetDay();
1393 pTargetData->met_data.hour = tz.GetHour();
1394 pTargetData->met_data.minute = tz.GetMinute();
1396 }
else if (update_path ==
"environment.wind.averageSpeed" &&
1397 item[
"value"].IsNumber()) {
1398 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
1399 }
else if (update_path ==
"environment.wind.gust" &&
1400 item[
"value"].IsNumber()) {
1401 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
1402 }
else if (update_path ==
"environment.wind.directionTrue" &&
1403 item[
"value"].IsNumber()) {
1404 pTargetData->met_data.wind_dir =
1405 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1406 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
1407 item[
"value"].IsNumber()) {
1408 pTargetData->met_data.wind_gust_dir =
1409 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1410 }
else if (update_path ==
"environment.outside.temperature" &&
1411 item[
"value"].IsNumber()) {
1412 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
1413 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
1414 item[
"value"].IsNumber()) {
1415 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
1416 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
1417 item[
"value"].IsNumber()) {
1418 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
1419 }
else if (update_path ==
"environment.outside.pressure" &&
1420 item[
"value"].IsNumber()) {
1421 pTargetData->met_data.airpress =
1422 static_cast<int>(item[
"value"].GetDouble() / 100);
1423 }
else if (update_path ==
"environment.water.level" &&
1424 item[
"value"].IsNumber()) {
1425 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
1426 }
else if (update_path ==
"environment.water.current.drift" &&
1427 item[
"value"].IsNumber()) {
1428 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
1429 }
else if (update_path ==
"environment.water.current.set" &&
1430 item[
"value"].IsNumber()) {
1431 pTargetData->met_data.curr_dir =
1432 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1433 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
1434 item[
"value"].IsNumber()) {
1435 pTargetData->met_data.water_lev_trend =
1436 static_cast<int>(item[
"value"].GetDouble());
1437 }
else if (update_path ==
"environment.water.levelTendency") {
1439 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
1440 item[
"value"].IsNumber()) {
1441 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
1442 }
else if (update_path ==
"environment.water.waves.period" &&
1443 item[
"value"].IsNumber()) {
1444 pTargetData->met_data.wave_period =
1445 static_cast<int>(item[
"value"].GetDouble());
1446 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
1447 item[
"value"].IsNumber()) {
1448 pTargetData->met_data.wave_dir =
1449 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1450 }
else if (update_path ==
"environment.water.swell.height" &&
1451 item[
"value"].IsNumber()) {
1452 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
1453 }
else if (update_path ==
"environment.water.swell.period" &&
1454 item[
"value"].IsNumber()) {
1455 pTargetData->met_data.swell_per =
1456 static_cast<int>(item[
"value"].GetDouble());
1457 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
1458 item[
"value"].IsNumber()) {
1459 pTargetData->met_data.swell_dir =
1460 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1461 }
else if (update_path ==
"environment.water.temperature" &&
1462 item[
"value"].IsNumber()) {
1463 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
1464 }
else if (update_path ==
"environment.water.salinity" &&
1465 item[
"value"].IsNumber()) {
1466 pTargetData->met_data.salinity = item[
"value"].GetDouble();
1467 }
else if (update_path ==
"environment.water.ice") {
1469 }
else if (update_path ==
"environment.water.iceValue" &&
1470 item[
"value"].IsNumber()) {
1471 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
1472 }
else if (update_path ==
"environment.water.seaStateValue" &&
1473 item[
"value"].IsNumber()) {
1474 pTargetData->met_data.seastate =
1475 static_cast<int>(item[
"value"].GetDouble());
1476 }
else if (update_path ==
"environment.water.seaState") {
1478 }
else if (update_path ==
"environment.outside.precipitation") {
1480 }
else if (update_path ==
"environment.outside.precipitationValue" &&
1481 item[
"value"].IsNumber()) {
1482 pTargetData->met_data.precipitation =
1483 static_cast<int>(item[
"value"].GetDouble());
1484 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
1485 item[
"value"].IsNumber()) {
1486 pTargetData->met_data.airpress_tend =
1487 static_cast<int>(item[
"value"].GetDouble());
1488 }
else if (update_path ==
"environment.outside.pressureTendency") {
1490 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
1491 item[
"value"].IsNumber()) {
1492 pTargetData->met_data.hor_vis =
1493 GEODESIC_METERS2NM(item[
"value"].GetDouble());
1494 }
else if (update_path ==
1495 "environment.outside.horizontalVisibility.overRange") {
1496 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
1497 }
else if (update_path == _T(
"")) {
1498 if (item[
"value"].HasMember(
"name")) {
1499 const wxString &name = item[
"value"][
"name"].GetString();
1500 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
1501 pTargetData->b_nameValid =
true;
1502 pTargetData->MID = 123;
1503 }
else if (item[
"value"].HasMember(
"registrations")) {
1504 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
1505 pTargetData->IMO = wxAtoi(imo.Right(7));
1506 }
else if (item[
"value"].HasMember(
"communication")) {
1507 const wxString &callsign =
1508 item[
"value"][
"communication"][
"callsignVhf"].GetString();
1509 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
1511 if (item[
"value"].HasMember(
"mmsi") &&
1512 1994 != (pTargetData->MMSI) / 100000) {
1514 wxString tmp = item[
"value"][
"mmsi"].GetString();
1515 if (tmp.ToLong(&mmsi)) {
1516 pTargetData->MMSI = mmsi;
1518 if (97 == mmsi / 10000000) {
1519 pTargetData->Class = AIS_SART;
1521 if (111 == mmsi / 1000000) {
1522 pTargetData->b_SarAircraftPosnReport =
true;
1525 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
1530 wxLogMessage(wxString::Format(
1531 _T(
"** AisDecoder::updateItem: unhandled path %s"), update_path));
1533 rapidjson::StringBuffer buffer;
1534 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1535 item.Accept(writer);
1536 wxString msg(_T(
"update: "));
1537 msg.append(buffer.GetString());
1547AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
1548 wxString *accumulator) {
1550 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
1552 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
1554 if (!pos)
return AIS_GENERIC_ERROR;
1556 if (!accumulator)
return AIS_GENERIC_ERROR;
1559 if (!str.Mid(1, 5).IsSameAs(_T(
"AIVDO")))
return AIS_GENERIC_ERROR;
1562 wxStringTokenizer tkz(str, _T(
","));
1565 token = tkz.GetNextToken();
1567 token = tkz.GetNextToken();
1568 int nsentences = atoi(token.mb_str());
1570 token = tkz.GetNextToken();
1571 int isentence = atoi(token.mb_str());
1573 token = tkz.GetNextToken();
1574 token = tkz.GetNextToken();
1576 wxString string_to_parse;
1577 string_to_parse.Clear();
1590 if ((1 == nsentences) && (1 == isentence)) {
1591 string_to_parse = tkz.GetNextToken();
1594 else if (nsentences > 1) {
1595 if (1 == isentence) {
1596 *accumulator = tkz.GetNextToken();
1600 accumulator->Append(tkz.GetNextToken());
1603 if (isentence == nsentences) {
1604 string_to_parse = *accumulator;
1608 if (string_to_parse.IsEmpty() &&
1610 return AIS_INCOMPLETE_MULTIPART;
1619 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1621 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
1623 if (bdecode_result) {
1624 switch (TargetData->MID) {
1629 if (!TargetData->b_positionDoubtful) {
1630 pos->
kLat = TargetData->Lat;
1631 pos->
kLon = TargetData->Lon;
1637 if (TargetData->COG == 360.0)
1640 pos->
kCog = TargetData->COG;
1642 if (TargetData->SOG > 102.2)
1645 pos->
kSog = TargetData->SOG;
1647 if ((
int)TargetData->HDG == 511)
1650 pos->
kHdt = TargetData->HDG;
1658 return AIS_GENERIC_ERROR;
1663 return AIS_GENERIC_ERROR;
1671AisError AisDecoder::DecodeN0183(
const wxString &str) {
1672 AisError ret = AIS_GENERIC_ERROR;
1673 wxString string_to_parse;
1675 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
1676 double gpsg_cog, gpsg_sog, gpsg_utc_time;
1677 int gpsg_utc_hour = 0;
1678 int gpsg_utc_min = 0;
1679 int gpsg_utc_sec = 0;
1680 char gpsg_name_str[21];
1683 bool bdecode_result =
false;
1690 long arpa_tgt_num = 0;
1691 double arpa_sog = 0.;
1692 double arpa_cog = 0.;
1693 double arpa_lat = 0.;
1694 double arpa_lon = 0.;
1695 double arpa_dist = 0.;
1696 double arpa_brg = 0.;
1697 wxString arpa_brgunit;
1698 wxString arpa_status;
1699 wxString arpa_distunit;
1700 wxString arpa_cogunit;
1701 wxString arpa_reftarget;
1702 double arpa_mins, arpa_degs;
1703 double arpa_utc_time;
1704 int arpa_utc_hour = 0;
1705 int arpa_utc_min = 0;
1706 int arpa_utc_sec = 0;
1707 char arpa_name_str[21];
1708 bool arpa_lost =
true;
1709 bool arpa_nottracked =
false;
1711 double aprs_lat = 0.;
1712 double aprs_lon = 0.;
1713 char aprs_name_str[21];
1714 double aprs_mins, aprs_degs;
1716 std::shared_ptr<AisTargetData> pTargetData = 0;
1717 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1718 bool bnewtarget =
false;
1719 int last_report_ticks;
1723 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
1725 if (!NMEACheckSumOK(str)) {
1726 return AIS_NMEAVDX_CHECKSUM_BAD;
1728 if (str.Mid(1, 2).IsSameAs(_T(
"CD"))) {
1731 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
1735 wxString string(str);
1736 wxStringTokenizer tkz(
string, _T(
",*"));
1739 token = tkz.GetNextToken();
1740 token = tkz.GetNextToken();
1741 token.ToLong(&arpa_tgt_num);
1742 token = tkz.GetNextToken();
1743 token.ToDouble(&arpa_dist);
1744 token = tkz.GetNextToken();
1745 token.ToDouble(&arpa_brg);
1746 arpa_brgunit = tkz.GetNextToken();
1747 if (arpa_brgunit == _T(
"R")) {
1748 if (std::isnan(arpa_ref_hdg)) {
1749 if (!std::isnan(gHdt))
1754 arpa_brg += arpa_ref_hdg;
1755 if (arpa_brg >= 360.) arpa_brg -= 360.;
1757 token = tkz.GetNextToken();
1758 token.ToDouble(&arpa_sog);
1759 token = tkz.GetNextToken();
1760 token.ToDouble(&arpa_cog);
1761 arpa_cogunit = tkz.GetNextToken();
1762 if (arpa_cogunit == _T(
"R")) {
1763 if (std::isnan(arpa_ref_hdg)) {
1764 if (!std::isnan(gHdt))
1769 arpa_cog += arpa_ref_hdg;
1770 if (arpa_cog >= 360.) arpa_cog -= 360.;
1772 token = tkz.GetNextToken();
1773 token = tkz.GetNextToken();
1775 arpa_distunit = tkz.GetNextToken();
1776 token = tkz.GetNextToken();
1777 if (token == wxEmptyString)
1778 token = wxString::Format(_T(
"ARPA %ld"), arpa_tgt_num);
1779 int len = wxMin(token.Length(), 20);
1780 strncpy(arpa_name_str, token.mb_str(), len);
1781 arpa_name_str[len] = 0;
1782 arpa_status = tkz.GetNextToken();
1783 if (arpa_status != _T(
"L" )) {
1785 }
else if (arpa_status != wxEmptyString)
1786 arpa_nottracked =
true;
1787 arpa_reftarget = tkz.GetNextToken();
1788 if (tkz.HasMoreTokens()) {
1789 token = tkz.GetNextToken();
1790 token.ToDouble(&arpa_utc_time);
1791 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1792 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1794 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1796 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
1797 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
1798 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
1801 if (arpa_distunit == _T(
"K")) {
1802 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_KM, g_iDistanceFormat);
1803 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_KMH, g_iSpeedFormat);
1804 }
else if (arpa_distunit == _T(
"S")) {
1805 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_MI, g_iDistanceFormat);
1806 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_MPH, g_iSpeedFormat);
1809 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
1813 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
1816 wxString aprs_tll_str;
1817 wxString string(str);
1818 wxStringTokenizer tkz(
string, _T(
",*"));
1821 aprs_tll_str = tkz.GetNextToken();
1822 token = tkz.GetNextToken();
1823 token.ToLong(&arpa_tgt_num);
1824 token = tkz.GetNextToken();
1825 token.ToDouble(&arpa_lat);
1826 arpa_degs = (int)(arpa_lat / 100.0);
1827 arpa_mins = arpa_lat - arpa_degs * 100.0;
1828 arpa_lat = arpa_degs + arpa_mins / 60.0;
1829 token = tkz.GetNextToken();
1830 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1831 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1832 arpa_lat = 0. - arpa_lat;
1833 token = tkz.GetNextToken();
1834 token.ToDouble(&arpa_lon);
1835 arpa_degs = (int)(arpa_lon / 100.0);
1836 arpa_mins = arpa_lon - arpa_degs * 100.0;
1837 arpa_lon = arpa_degs + arpa_mins / 60.0;
1838 token = tkz.GetNextToken();
1839 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1840 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1841 arpa_lon = 0. - arpa_lon;
1842 token = tkz.GetNextToken();
1843 if (token == wxEmptyString)
1844 token = wxString::Format(_T(
"ARPA %d"), arpa_tgt_num);
1845 int len = wxMin(token.Length(), 20);
1846 strncpy(arpa_name_str, token.mb_str(), len);
1847 arpa_name_str[len] = 0;
1848 token = tkz.GetNextToken();
1849 token.ToDouble(&arpa_utc_time);
1850 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1851 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1853 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1858 if (arpa_status != _T(
"L"))
1860 else if (arpa_status != wxEmptyString)
1861 arpa_nottracked =
true;
1862 arpa_reftarget = tkz.GetNextToken();
1869 }
else if (str.Mid(3, 3).IsSameAs(_T(
"OSD"))) {
1871 wxString string(str);
1872 wxStringTokenizer tkz(
string, _T(
",*"));
1875 token = tkz.GetNextToken();
1876 token = tkz.GetNextToken();
1877 token.ToDouble(&arpa_ref_hdg);
1887 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(_T(
"WPL"))) {
1889 wxString string(str);
1890 wxStringTokenizer tkz(
string, _T(
",*"));
1893 token = tkz.GetNextToken();
1894 token = tkz.GetNextToken();
1895 token.ToDouble(&aprs_lat);
1896 aprs_degs = (int)(aprs_lat / 100.0);
1897 aprs_mins = aprs_lat - aprs_degs * 100.0;
1898 aprs_lat = aprs_degs + aprs_mins / 60.0;
1899 token = tkz.GetNextToken();
1900 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1901 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1902 aprs_lat = 0. - aprs_lat;
1903 token = tkz.GetNextToken();
1904 token.ToDouble(&aprs_lon);
1905 aprs_degs = (int)(aprs_lon / 100.0);
1906 aprs_mins = aprs_lon - aprs_degs * 100.0;
1907 aprs_lon = aprs_degs + aprs_mins / 60.0;
1908 token = tkz.GetNextToken();
1909 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1910 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1911 aprs_lon = 0. - aprs_lon;
1912 token = tkz.GetNextToken();
1913 int len = wxMin(token.Length(), 20);
1914 strncpy(aprs_name_str, token.mb_str(), len + 1);
1915 if (0 == g_WplAction) {
1917 aprs_name_str[len] = 0;
1918 for (i = 0; i < len; i++) {
1920 hash += (int)(aprs_name_str[i]);
1921 while (hash >= 100000) hash = hash / 100000;
1923 mmsi = aprs_mmsi = 199300000 + hash;
1927 }
else if (1 == g_WplAction) {
1929 aprs_name_str, wxEmptyString,
false);
1931 InsertWpt(pWP,
true);
1933 }
else if (str.Mid(1, 5).IsSameAs(_T(
"FRPOS"))) {
1937 wxString string(str);
1938 wxStringTokenizer tkz(
string, _T(
",*"));
1941 token = tkz.GetNextToken();
1943 token = tkz.GetNextToken();
1944 token.ToDouble(&gpsg_lat);
1945 gpsg_degs = (int)(gpsg_lat / 100.0);
1946 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
1947 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
1949 token = tkz.GetNextToken();
1950 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1951 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1952 gpsg_lat = 0. - gpsg_lat;
1954 token = tkz.GetNextToken();
1955 token.ToDouble(&gpsg_lon);
1956 gpsg_degs = (int)(gpsg_lon / 100.0);
1957 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
1958 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
1960 token = tkz.GetNextToken();
1961 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1962 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1963 gpsg_lon = 0. - gpsg_lon;
1965 token = tkz.GetNextToken();
1968 token = tkz.GetNextToken();
1969 token.ToDouble(&gpsg_sog);
1971 token = tkz.GetNextToken();
1972 token.ToDouble(&gpsg_cog);
1974 token = tkz.GetNextToken();
1977 token = tkz.GetNextToken();
1978 token.ToDouble(&gpsg_utc_time);
1979 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
1980 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
1982 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
1986 token = tkz.GetNextToken();
1987 int i, len, hash = 0;
1988 len = wxMin(wxStrlen(token), 20);
1989 strncpy(gpsg_name_str, token.mb_str(), len);
1990 gpsg_name_str[len] = 0;
1991 for (i = 0; i < len; i++) {
1993 hash += (int)(token[i]);
1994 while (hash >= 100000) hash = hash / 100000;
1997 gpsg_mmsi = 199000000 + hash;
1999 }
else if (!str.Mid(3, 2).IsSameAs(_T(
"VD"))) {
2000 return AIS_NMEAVDX_BAD;
2006 wxString string(str);
2007 wxStringTokenizer tkz(
string, _T(
","));
2010 token = tkz.GetNextToken();
2012 token = tkz.GetNextToken();
2013 nsentences = atoi(token.mb_str());
2015 token = tkz.GetNextToken();
2016 isentence = atoi(token.mb_str());
2018 token = tkz.GetNextToken();
2019 long lsequence_id = 0;
2020 token.ToLong(&lsequence_id);
2022 token = tkz.GetNextToken();
2024 token.ToLong(&lchannel);
2027 string_to_parse.Clear();
2031 if ((1 == nsentences) && (1 == isentence)) {
2032 string_to_parse = tkz.GetNextToken();
2035 else if (nsentences > 1) {
2036 if (1 == isentence) {
2037 sentence_accumulator = tkz.GetNextToken();
2041 sentence_accumulator += tkz.GetNextToken();
2044 if (isentence == nsentences) {
2045 string_to_parse = sentence_accumulator;
2049 if (mmsi || (!string_to_parse.IsEmpty() &&
2050 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
2052 wxCharBuffer abuf = string_to_parse.ToUTF8();
2054 return AIS_GENERIC_ERROR;
2059 if (!mmsi) mmsi = strbit.GetInt(9, 30);
2060 long mmsi_long = mmsi;
2063 int origin_mmsi = 0;
2064 int messID = strbit.GetInt(1, 6);
2065 int dac = strbit.GetInt(41, 10);
2066 int fi = strbit.GetInt(51, 6);
2068 int met_lon, met_lat;
2069 if (dac == 001 && fi == 31) {
2071 met_lon = strbit.GetInt(57, 25);
2072 met_lat = strbit.GetInt(82, 24);
2073 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
2076 }
else if (dac == 367 && fi == 33) {
2078 const int size = strbit.GetBitCount();
2079 if (size < 168)
return AIS_GENERIC_ERROR;
2080 const int startb = 56;
2081 const int slot_size = 112;
2082 const int extra_bits = (size - startb) % slot_size;
2083 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
2085 int mes_type = strbit.GetInt(57, 4);
2086 int site_ID = strbit.GetInt(77, 7);
2087 if (mes_type == 0) {
2089 met_lon = strbit.GetInt(90, 28);
2090 met_lat = strbit.GetInt(118, 27);
2091 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
2096 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
2102 return AIS_GENERIC_ERROR;
2108 if (mmsi == g_OwnShipmmsi)
return AIS_GENERIC_ERROR;
2111 auto it = AISTargetList.find(mmsi);
2112 if (it == AISTargetList.end())
2114 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2119 pTargetData->MMSI = mmsi;
2120 pTargetData->met_data.original_mmsi = origin_mmsi;
2123 pTargetData = it->second;
2126 pStaleTarget = pTargetData;
2128 pTargetData->MMSI = mmsi;
2129 pTargetData->met_data.original_mmsi = origin_mmsi;
2132 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2134 if (mmsi == props->MMSI) {
2136 if (TRACKTYPE_NEVER == props->TrackType) {
2137 pTargetData->b_show_track =
false;
2138 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
2139 pTargetData->b_show_track =
true;
2144 if (props->m_bignore)
return AIS_NoError;
2147 else if (props->m_bVDM) {
2149 if (str.Mid(3, 9).IsSameAs(wxT(
"VDM,1,1,,"))) {
2150 int message_ID = strbit.GetInt(1, 6);
2153 if ((message_ID <= 3) || (message_ID == 18)) {
2155 pTargetData->b_OwnShip =
true;
2157 wxString aivdostr = str;
2158 aivdostr.replace(1, 5,
"AIVDO");
2159 unsigned char calculated_checksum = 0;
2160 wxString::iterator i;
2161 for (i = aivdostr.begin() + 1; i != aivdostr.end() && *i !=
'*';
2163 calculated_checksum ^=
static_cast<unsigned char>(*i);
2166 if (i <= aivdostr.end() - 3)
2169 wxString::Format(_(
"%02X"), calculated_checksum));
2171 gps_watchdog_timeout_ticks =
2174 std::string full_sentence = aivdostr.ToStdString();
2175 std::string identifier(
"AIVDO");
2179 auto address = std::make_shared<NavAddr0183>(
"virtual");
2180 auto msg = std::make_shared<const Nmea0183Msg>(
2181 identifier, full_sentence, address);
2182 auto msg_all = std::make_shared<const Nmea0183Msg>(*msg,
"ALL");
2184 auto &msgbus = NavMsgBus::GetInstance();
2186 msgbus.Notify(std::move(msg));
2187 msgbus.Notify(std::move(msg_all));
2197 wxDateTime now = wxDateTime::Now();
2201 last_report_ticks = pStaleTarget->PositionReportTicks;
2203 last_report_ticks = now.GetTicks();
2207 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2211 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2212 pTargetData->PositionReportTicks = now.GetTicks();
2213 pTargetData->StaticReportTicks = now.GetTicks();
2214 pTargetData->m_utc_hour = gpsg_utc_hour;
2215 pTargetData->m_utc_min = gpsg_utc_min;
2216 pTargetData->m_utc_sec = gpsg_utc_sec;
2217 pTargetData->m_date_string = gpsg_date;
2218 pTargetData->MMSI = gpsg_mmsi;
2219 pTargetData->NavStatus = 0;
2220 pTargetData->Lat = gpsg_lat;
2221 pTargetData->Lon = gpsg_lon;
2222 pTargetData->b_positionOnceValid =
true;
2223 pTargetData->COG = gpsg_cog;
2224 pTargetData->SOG = gpsg_sog;
2225 pTargetData->ShipType = 52;
2226 pTargetData->Class = AIS_GPSG_BUDDY;
2227 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
2228 pTargetData->b_nameValid =
true;
2229 pTargetData->b_active =
true;
2230 pTargetData->b_lost =
false;
2232 bdecode_result =
true;
2233 }
else if (arpa_mmsi) {
2234 pTargetData->m_utc_hour = arpa_utc_hour;
2235 pTargetData->m_utc_min = arpa_utc_min;
2236 pTargetData->m_utc_sec = arpa_utc_sec;
2237 pTargetData->MMSI = arpa_mmsi;
2238 pTargetData->NavStatus = 15;
2239 if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
2242 (now.GetTicks() - pTargetData->PositionReportTicks);
2243 if (age_of_last > 0) {
2244 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
2245 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
2246 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2249 pTargetData->Lat = arpa_lat;
2250 pTargetData->Lon = arpa_lon;
2251 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
2252 if (arpa_dist != 0.)
2253 ll_gc_ll(gLat, gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
2257 pTargetData->COG = arpa_cog;
2258 pTargetData->SOG = arpa_sog;
2260 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2261 pTargetData->PositionReportTicks = now.GetTicks();
2262 pTargetData->StaticReportTicks = now.GetTicks();
2263 pTargetData->b_positionOnceValid =
true;
2264 pTargetData->ShipType = 55;
2265 pTargetData->Class = AIS_ARPA;
2267 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
2268 if (arpa_status != _T(
"Q"))
2269 pTargetData->b_nameValid =
true;
2271 pTargetData->b_nameValid =
false;
2272 pTargetData->b_active = !arpa_lost;
2273 pTargetData->b_lost = arpa_nottracked;
2275 bdecode_result =
true;
2276 }
else if (aprs_mmsi) {
2277 pTargetData->m_utc_hour = now.GetHour();
2278 pTargetData->m_utc_min = now.GetMinute();
2279 pTargetData->m_utc_sec = now.GetSecond();
2280 pTargetData->MMSI = aprs_mmsi;
2281 pTargetData->NavStatus = 15;
2283 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
2284 if (age_of_last > 0) {
2285 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
2286 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
2287 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2290 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2291 pTargetData->PositionReportTicks = now.GetTicks();
2292 pTargetData->StaticReportTicks = now.GetTicks();
2293 pTargetData->Lat = aprs_lat;
2294 pTargetData->Lon = aprs_lon;
2295 pTargetData->b_positionOnceValid =
true;
2296 pTargetData->ShipType = 56;
2297 pTargetData->Class = AIS_APRS;
2298 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
2299 pTargetData->b_nameValid =
true;
2300 pTargetData->b_active =
true;
2301 pTargetData->b_lost =
false;
2303 bdecode_result =
true;
2307 Parse_VDXBitstring(&strbit, pTargetData);
2311 getMmsiProperties(pTargetData);
2314 pTargetData->RecentPeriod =
2315 pTargetData->PositionReportTicks - last_report_ticks;
2325 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
2330 if ((n_msgs % 10000) == 0)
2331 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
2332 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
2338void AisDecoder::CommitAISTarget(std::shared_ptr<AisTargetData> pTargetData,
2339 const wxString &str,
bool message_valid,
2341 m_pLatestTargetData = pTargetData;
2343 if (!str.IsEmpty()) {
2344 if (str.Mid(3, 3).IsSameAs(_T(
"VDO")))
2345 pTargetData->b_OwnShip =
true;
2347 pTargetData->b_OwnShip =
false;
2350 if (!pTargetData->b_OwnShip) {
2352 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
2354 pTargetData->b_PersistTrack =
false;
2356 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2357 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2359 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2366 pTargetData->b_NoTrack =
false;
2371 if (message_valid) {
2373 if (pTargetData->MMSI) {
2374 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2377 AISTargetList[pTargetData->MMSI] =
2380 if (!pTargetData->area_notices.empty()) {
2381 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
2382 if (it == AIS_AreaNotice_Sources.end())
2383 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
2388 if (!pTargetData->b_OwnShip) {
2389 if (pTargetData->b_positionOnceValid) {
2390 long mmsi_long = pTargetData->MMSI;
2391 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2392 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2394 pSel->SetUserData(pTargetData->MMSI);
2398 UpdateOneCPA(pTargetData.get());
2401 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2414 if (!pTargetData->b_OwnShip) {
2415 if (pTargetData->b_positionOnceValid) {
2416 long mmsi_long = pTargetData->MMSI;
2417 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2418 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2420 pSel->SetUserData(pTargetData->MMSI);
2427void AisDecoder::getAISTarget(
long mmsi,
2428 std::shared_ptr<AisTargetData> &pTargetData,
2429 std::shared_ptr<AisTargetData> &pStaleTarget,
2430 bool &bnewtarget,
int &last_report_ticks,
2432 now = wxDateTime::Now();
2433 auto it = AISTargetList.find(mmsi);
2434 if (it == AISTargetList.end())
2436 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2440 pTargetData = it->second;
2441 pStaleTarget = pTargetData;
2448 last_report_ticks = pStaleTarget->PositionReportTicks;
2450 last_report_ticks = now.GetTicks();
2454 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
2457void AisDecoder::getMmsiProperties(
2458 std::shared_ptr<AisTargetData> &pTargetData) {
2459 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2460 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2462 pTargetData->b_isFollower = props->m_bFollower;
2463 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2464 if (TRACKTYPE_NEVER == props->TrackType) {
2465 pTargetData->b_show_track =
false;
2466 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
2467 pTargetData->b_show_track =
true;
2474std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
2476 double dsc_lat = 0.;
2477 double dsc_lon = 0.;
2478 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
2480 double dse_lat = 0.;
2481 double dse_lon = 0.;
2482 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
2485 int dsc_tx_mmsi = 0;
2487 double dse_cog = 0.;
2488 double dse_sog = 0.;
2489 wxString dse_shipName = wxEmptyString;
2494 std::shared_ptr<AisTargetData> pTargetData = NULL;
2498 wxString string(str);
2499 wxStringTokenizer tkz(
string, _T(
",*"));
2502 token = tkz.GetNextToken();
2504 if (str.Mid(3, 3).IsSameAs(_T(
"DSC"))) {
2505 m_dsc_last_string = str;
2507 token = tkz.GetNextToken();
2511 token = tkz.GetNextToken();
2513 if (dsc_fmt == 12 || dsc_fmt == 16) {
2514 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2516 token.ToDouble(&dsc_addr);
2517 dsc_mmsi = 0 - (int)(dsc_addr / 10);
2520 token = tkz.GetNextToken();
2521 token.ToLong(&dsc_cat);
2523 token = tkz.GetNextToken();
2524 if (!token.IsSameAs(wxEmptyString)) {
2525 token.ToLong(&dsc_nature);
2529 token = tkz.GetNextToken();
2531 token = tkz.GetNextToken();
2532 token.ToDouble(&dsc_tmp);
2534 token = tkz.GetNextToken();
2535 token = tkz.GetNextToken();
2536 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(wxEmptyString)) {
2538 dsc_tx_mmsi = dsc_mmsi;
2539 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2541 token = tkz.GetNextToken();
2542 if (dsc_fmt == 16 && dsc_cat == 12) {
2543 if (!token.IsSameAs(wxEmptyString)) {
2544 token.ToLong(&dsc_nature);
2548 token = tkz.GetNextToken();
2549 token = tkz.GetNextToken();
2551 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
2553 if (dsc_quadrant > 3)
2556 dsc_lat = (int)(dsc_tmp / 100000.0);
2557 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
2558 dsc_lat = dsc_lat - dsc_quadrant * 10000;
2559 dsc_degs = (int)(dsc_lat / 100.0);
2560 dsc_mins = dsc_lat - dsc_degs * 100.0;
2561 dsc_lat = dsc_degs + dsc_mins / 60.0;
2563 dsc_degs = (int)(dsc_lon / 100.0);
2564 dsc_mins = dsc_lon - dsc_degs * 100.0;
2565 dsc_lon = dsc_degs + dsc_mins / 60.0;
2566 switch (dsc_quadrant) {
2582 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
2584 }
else if (str.Mid(3, 3).IsSameAs(_T(
"DSE"))) {
2585 token = tkz.GetNextToken();
2586 token = tkz.GetNextToken();
2587 token = tkz.GetNextToken();
2588 token = tkz.GetNextToken();
2589 dse_mmsi = wxAtoi(token.Mid(
2595 token = tkz.GetNextToken();
2598 token.ToDouble(&dse_tmp);
2599 dse_lat = (int)(dse_tmp / 10000.0);
2600 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2601 dse_lat = dse_lat / 600000.0;
2602 dse_lon = dse_lon / 600000.0;
2605 while (tkz.HasMoreTokens()) {
2606 dseSymbol = tkz.GetNextToken();
2607 token = tkz.GetNextToken();
2608 if (dseSymbol.IsSameAs(_T(
"00"))) {
2609 token.ToDouble(&dse_tmp);
2610 dse_lat = (int)(dse_tmp / 10000.0);
2611 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2612 dse_lat = dse_lat / 600000.0;
2613 dse_lon = dse_lon / 600000.0;
2614 }
else if (dseSymbol.IsSameAs(_T(
"01"))) {
2615 }
else if (dseSymbol.IsSameAs(_T(
"02"))) {
2616 token.ToDouble(&dse_tmp);
2617 dse_sog = dse_tmp / 10.0;
2618 }
else if (dseSymbol.IsSameAs(_T(
"03"))) {
2619 token.ToDouble(&dse_tmp);
2620 dse_cog = dse_tmp / 10.0;
2621 }
else if (dseSymbol.IsSameAs(_T(
"04"))) {
2622 dse_shipName = DecodeDSEExpansionCharacters(token);
2623 }
else if (dseSymbol.IsSameAs(_T(
"05"))) {
2624 }
else if (dseSymbol.IsSameAs(_T(
"06"))) {
2627 mmsi = abs((
int)dse_mmsi);
2631 wxDateTime now = wxDateTime::Now();
2633 int last_report_ticks = now.GetTicks();
2636 auto it = AISTargetList.find(mmsi);
2637 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
2638 if (it == AISTargetList.end()) {
2640 pStaleTarget = it->second;
2641 last_report_ticks = pStaleTarget->PositionReportTicks;
2647 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
2649 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
2650 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
2652 m_ptentative_dsctarget->MMSI = mmsi;
2653 m_ptentative_dsctarget->NavStatus =
2655 m_ptentative_dsctarget->Lat = dsc_lat;
2656 m_ptentative_dsctarget->Lon = dsc_lon;
2657 m_ptentative_dsctarget->b_positionOnceValid =
true;
2658 m_ptentative_dsctarget->COG = 0;
2659 m_ptentative_dsctarget->SOG = 0;
2660 m_ptentative_dsctarget->ShipType = dsc_fmt;
2661 m_ptentative_dsctarget->Class = AIS_DSC;
2662 m_ptentative_dsctarget->b_isDSCtarget =
true;
2663 m_ptentative_dsctarget->b_nameValid =
true;
2664 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
2665 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %d",
2667 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
2668 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
2670 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %d",
2674 m_ptentative_dsctarget->b_active =
true;
2675 m_ptentative_dsctarget->b_lost =
false;
2676 m_ptentative_dsctarget->RecentPeriod =
2677 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2680 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
2685 if (dse_mmsi || b_take_dsc) {
2686 if (m_ptentative_dsctarget) {
2692 m_ptentative_dsctarget->Lat =
2693 m_ptentative_dsctarget->Lat +
2694 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
2695 m_ptentative_dsctarget->Lon =
2696 m_ptentative_dsctarget->Lon +
2697 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
2698 if (dse_shipName.length() > 0) {
2699 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
2700 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
2701 "%s", dse_shipName.ToAscii().data());
2703 m_ptentative_dsctarget->COG = dse_cog;
2704 m_ptentative_dsctarget->SOG = dse_sog;
2708 m_ptentative_dsctarget->RecentPeriod =
2709 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2714 auto it = AISTargetList.find(mmsi);
2715 if (it == AISTargetList.end()) {
2716 pTargetData = m_ptentative_dsctarget;
2718 pTargetData = it->second;
2719 std::vector<AISTargetTrackPoint> ptrack =
2720 std::move(pTargetData->m_ptrack);
2721 pTargetData->CloneFrom(
2722 m_ptentative_dsctarget
2725 pTargetData->m_ptrack =
2732 m_ptentative_dsctarget = NULL;
2734 m_pLatestTargetData = pTargetData;
2736 AISTargetList[pTargetData->MMSI] =
2739 long mmsi_long = pTargetData->MMSI;
2743 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2746 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2747 (
void *)mmsi_long, SELTYPE_AISTARGET);
2748 pSel->SetUserData(pTargetData->MMSI);
2751 UpdateOneCPA(pTargetData.get());
2754 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2762wxString AisDecoder::DecodeDSEExpansionCharacters(wxString dseData) {
2764 char lookupTable[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
' ',
2765 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
2766 'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
2767 'W',
'X',
'Y',
'Z',
'.',
',',
'-',
'/',
' '};
2769 for (
size_t i = 0; i < dseData.length(); i += 2) {
2770 result.append(1, lookupTable[strtol(dseData.Mid(i, 2).data(), NULL, 10)]);
2779 std::shared_ptr<AisTargetData> ptd) {
2780 bool parse_result =
false;
2781 bool b_posn_report =
false;
2783 wxDateTime now = wxDateTime::Now();
2785 int message_ID = bstr->
GetInt(1, 6);
2786 ptd->MID = message_ID;
2789 int met_mmsi = ptd->MMSI;
2792 ptd->MMSI = bstr->
GetInt(9, 30);
2794 switch (message_ID) {
2800 ptd->NavStatus = bstr->
GetInt(39, 4);
2801 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
2803 int lon = bstr->
GetInt(62, 28);
2804 if (lon & 0x08000000)
2806 double lon_tentative = lon / 600000.;
2808 int lat = bstr->
GetInt(90, 27);
2809 if (lat & 0x04000000)
2811 double lat_tentative = lat / 600000.;
2813 if ((lon_tentative <= 180.) &&
2817 ptd->Lon = lon_tentative;
2818 ptd->Lat = lat_tentative;
2819 ptd->b_positionDoubtful =
false;
2820 ptd->b_positionOnceValid =
true;
2821 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2822 ptd->PositionReportTicks = now.GetTicks();
2824 ptd->b_positionDoubtful =
true;
2827 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
2828 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
2830 ptd->ROTAIS = bstr->
GetInt(43, 8);
2831 double rot_dir = 1.0;
2833 if (ptd->ROTAIS == 128)
2835 else if ((ptd->ROTAIS & 0x80) == 0x80) {
2836 ptd->ROTAIS = ptd->ROTAIS - 256;
2840 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
2843 ptd->m_utc_sec = bstr->
GetInt(138, 6);
2845 if ((1 == message_ID) ||
2848 ptd->SyncState = bstr->
GetInt(151, 2);
2849 ptd->SlotTO = bstr->
GetInt(153, 2);
2850 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
2852 ptd->m_utc_hour = bstr->
GetInt(155, 5);
2854 ptd->m_utc_min = bstr->
GetInt(160, 7);
2856 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
2857 (ptd->m_utc_sec < 60)) {
2858 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
2859 rx_ticks = rx_time.GetTicks();
2861 first_rx_ticks = rx_ticks;
2869 ptd->blue_paddle = bstr->
GetInt(144, 2);
2870 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
2872 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
2875 int mmsi_start = ptd->MMSI / 10000000;
2877 if (mmsi_start == 97) {
2878 ptd->Class = AIS_SART;
2879 ptd->StaticReportTicks =
2893 parse_result =
true;
2894 b_posn_report =
true;
2903 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2905 int lon = bstr->
GetInt(58, 28);
2906 if (lon & 0x08000000)
2908 double lon_tentative = lon / 600000.;
2910 int lat = bstr->
GetInt(86, 27);
2911 if (lat & 0x04000000)
2913 double lat_tentative = lat / 600000.;
2915 if ((lon_tentative <= 180.) &&
2919 ptd->Lon = lon_tentative;
2920 ptd->Lat = lat_tentative;
2921 ptd->b_positionDoubtful =
false;
2922 ptd->b_positionOnceValid =
true;
2923 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2924 ptd->PositionReportTicks = now.GetTicks();
2926 ptd->b_positionDoubtful =
true;
2928 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2929 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2931 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2933 if (!ptd->b_isDSCtarget) {
2934 if (!isBuoyMmsi(ptd->MMSI))
2935 ptd->Class = AIS_CLASS_B;
2937 ptd->Class = AIS_BUOY;
2939 parse_result =
true;
2940 b_posn_report =
true;
2948 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2949 int lon = bstr->
GetInt(58, 28);
2950 if (lon & 0x08000000)
2952 double lon_tentative = lon / 600000.;
2954 int lat = bstr->
GetInt(86, 27);
2955 if (lat & 0x04000000)
2957 double lat_tentative = lat / 600000.;
2959 if ((lon_tentative <= 180.) &&
2963 ptd->Lon = lon_tentative;
2964 ptd->Lat = lat_tentative;
2965 ptd->b_positionDoubtful =
false;
2966 ptd->b_positionOnceValid =
true;
2967 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2968 ptd->PositionReportTicks = now.GetTicks();
2970 ptd->b_positionDoubtful =
true;
2972 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2973 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2974 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2976 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
2977 ptd->b_nameValid =
true;
2978 if (!ptd->b_isDSCtarget) {
2979 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
2981 ptd->DimA = bstr->
GetInt(272, 9);
2982 ptd->DimB = bstr->
GetInt(281, 9);
2983 ptd->DimC = bstr->
GetInt(290, 6);
2984 ptd->DimD = bstr->
GetInt(296, 6);
2986 if (!ptd->b_isDSCtarget) {
2988 if (!isBuoyMmsi(ptd->MMSI))
2989 ptd->Class = AIS_CLASS_B;
2991 ptd->Class = AIS_BUOY;
2993 parse_result =
true;
2994 b_posn_report =
true;
3007 int bitCorrection = 10;
3008 int resolution = 10;
3011 double lon_tentative = 181.;
3012 double lat_tentative = 91.;
3015 printf(
"AIS Message 27 - received:\r\n");
3016 printf(
"MMSI : %i\r\n", ptd->MMSI);
3022 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
3024 ptd->NavStatus = bstr->
GetInt(39, 4);
3026 int lon = bstr->
GetInt(45, 18);
3027 int lat = bstr->
GetInt(63, 17);
3029 lat_tentative = lat;
3030 lon_tentative = lon;
3033 if (lat >= (0x4000000 >> bitCorrection)) {
3034 lat_tentative = (0x8000000 >> bitCorrection) - lat;
3035 lat_tentative *= -1;
3039 if (lon >= (0x8000000 >> bitCorrection)) {
3040 lon_tentative = (0x10000000 >> bitCorrection) - lon;
3041 lon_tentative *= -1;
3045 lat_tentative = lat_tentative / resolution / 60.0;
3046 lon_tentative = lon_tentative / resolution / 60.0;
3049 printf(
"Latitude : %f\r\n", lat_tentative);
3050 printf(
"Longitude : %f\r\n", lon_tentative);
3054 int positionLatency = bstr->
GetInt(95, 1);
3056 if ((lon_tentative <= 180.) &&
3060 ptd->Lon = lon_tentative;
3061 ptd->Lat = lat_tentative;
3062 ptd->b_positionDoubtful =
false;
3063 ptd->b_positionOnceValid =
true;
3064 if (positionLatency == 0) {
3067 printf(
"Low latency position report.\r\n");
3069 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3070 ptd->PositionReportTicks = now.GetTicks();
3073 ptd->b_positionDoubtful =
true;
3075 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
3076 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
3078 b_posn_report =
true;
3079 parse_result =
true;
3085 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
3091 int AIS_version_indicator = bstr->
GetInt(39, 2);
3092 if (AIS_version_indicator < 4) {
3093 ptd->IMO = bstr->
GetInt(41, 30);
3095 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
3096 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3097 ptd->b_nameValid =
true;
3098 if (!ptd->b_isDSCtarget) {
3099 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
3102 ptd->DimA = bstr->
GetInt(241, 9);
3103 ptd->DimB = bstr->
GetInt(250, 9);
3104 ptd->DimC = bstr->
GetInt(259, 6);
3105 ptd->DimD = bstr->
GetInt(265, 6);
3107 ptd->ETA_Mo = bstr->
GetInt(275, 4);
3108 ptd->ETA_Day = bstr->
GetInt(279, 5);
3109 ptd->ETA_Hr = bstr->
GetInt(284, 5);
3110 ptd->ETA_Min = bstr->
GetInt(289, 6);
3112 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
3114 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
3116 ptd->StaticReportTicks = now.GetTicks();
3118 parse_result =
true;
3125 int part_number = bstr->
GetInt(39, 2);
3126 if (0 == part_number) {
3127 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3128 ptd->b_nameValid =
true;
3129 parse_result =
true;
3131 }
else if (1 == part_number) {
3132 if (!ptd->b_isDSCtarget) {
3133 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
3135 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
3137 ptd->DimA = bstr->
GetInt(133, 9);
3138 ptd->DimB = bstr->
GetInt(142, 9);
3139 ptd->DimC = bstr->
GetInt(151, 6);
3140 ptd->DimD = bstr->
GetInt(157, 6);
3141 parse_result =
true;
3147 ptd->Class = AIS_BASE;
3149 ptd->m_utc_hour = bstr->
GetInt(62, 5);
3150 ptd->m_utc_min = bstr->
GetInt(67, 6);
3151 ptd->m_utc_sec = bstr->
GetInt(73, 6);
3153 int lon = bstr->
GetInt(80, 28);
3154 if (lon & 0x08000000)
3156 double lon_tentative = lon / 600000.;
3158 int lat = bstr->
GetInt(108, 27);
3159 if (lat & 0x04000000)
3161 double lat_tentative = lat / 600000.;
3163 if ((lon_tentative <= 180.) &&
3167 ptd->Lon = lon_tentative;
3168 ptd->Lat = lat_tentative;
3169 ptd->b_positionDoubtful =
false;
3170 ptd->b_positionOnceValid =
true;
3171 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3172 ptd->PositionReportTicks = now.GetTicks();
3174 ptd->b_positionDoubtful =
true;
3180 parse_result =
true;
3181 b_posn_report =
true;
3187 ptd->SOG = bstr->
GetInt(51, 10);
3189 int lon = bstr->
GetInt(62, 28);
3190 if (lon & 0x08000000)
3192 double lon_tentative = lon / 600000.;
3194 int lat = bstr->
GetInt(90, 27);
3195 if (lat & 0x04000000)
3197 double lat_tentative = lat / 600000.;
3199 if ((lon_tentative <= 180.) &&
3203 ptd->Lon = lon_tentative;
3204 ptd->Lat = lat_tentative;
3205 ptd->b_positionDoubtful =
false;
3206 ptd->b_positionOnceValid =
true;
3207 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3208 ptd->PositionReportTicks = now.GetTicks();
3210 ptd->b_positionDoubtful =
true;
3213 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
3215 int alt_tent = bstr->
GetInt(39, 12);
3216 ptd->altitude = alt_tent;
3218 ptd->b_SarAircraftPosnReport =
true;
3220 parse_result =
true;
3221 b_posn_report =
true;
3227 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
3233 ptd->DimA = bstr->
GetInt(220, 9);
3234 ptd->DimB = bstr->
GetInt(229, 9);
3235 ptd->DimC = bstr->
GetInt(238, 6);
3236 ptd->DimD = bstr->
GetInt(244, 6);
3239 ptd->m_utc_sec = bstr->
GetInt(254, 6);
3241 int offpos = bstr->
GetInt(260, 1);
3242 int virt = bstr->
GetInt(270, 1);
3245 ptd->NavStatus = ATON_VIRTUAL;
3247 ptd->NavStatus = ATON_REAL;
3248 if (ptd->m_utc_sec <= 59 ) {
3249 ptd->NavStatus += 1;
3250 if (offpos) ptd->NavStatus += 1;
3253 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3256 if (bstr->GetBitCount() > 276) {
3257 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
3258 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
3259 ptd->ShipNameExtension[14] = 0;
3261 ptd->ShipNameExtension[0] = 0;
3264 ptd->b_nameValid =
true;
3266 parse_result =
true;
3268 ptd->Class = AIS_ATON;
3270 int lon = bstr->
GetInt(165, 28);
3272 if (lon & 0x08000000)
3274 double lon_tentative = lon / 600000.;
3276 int lat = bstr->
GetInt(193, 27);
3278 if (lat & 0x04000000)
3280 double lat_tentative = lat / 600000.;
3282 if ((lon_tentative <= 180.) &&
3286 ptd->Lon = lon_tentative;
3287 ptd->Lat = lat_tentative;
3288 ptd->b_positionDoubtful =
false;
3289 ptd->b_positionOnceValid =
true;
3290 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3291 ptd->PositionReportTicks = now.GetTicks();
3293 ptd->b_positionDoubtful =
true;
3295 b_posn_report =
true;
3300 int dac = bstr->
GetInt(41, 10);
3301 int fi = bstr->
GetInt(51, 6);
3307 ptd->b_isEuroInland =
true;
3309 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
3310 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
3311 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
3312 ptd->UN_shiptype = bstr->
GetInt(128, 14);
3313 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
3314 parse_result =
true;
3317 if (dac == 1 || dac == 366)
3321 if (bstr->GetBitCount() >= 111) {
3323 an.link_id = bstr->
GetInt(57, 10);
3324 an.notice_type = bstr->
GetInt(67, 7);
3325 an.month = bstr->
GetInt(74, 4);
3326 an.day = bstr->
GetInt(78, 5);
3327 an.hour = bstr->
GetInt(83, 5);
3328 an.minute = bstr->
GetInt(88, 6);
3329 an.duration_minutes = bstr->
GetInt(94, 18);
3331 wxDateTime now = wxDateTime::Now();
3334 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3335 now.GetYear(), an.hour, an.minute);
3340 if (an.start_time > now + wxTimeSpan::Hours(48))
3341 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3342 now.GetYear() - 1, an.hour, an.minute);
3345 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3350 if (an.expiry_time < now - wxTimeSpan::Hours(24)) {
3351 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3352 now.GetYear() + 1, an.hour, an.minute);
3354 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3359 int subarea_len = 87;
3362 float pos_scale = 60000.0;
3370 pos_scale = 600000.0;
3375 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
3376 for (
int i = 0; i < subarea_count; ++i) {
3377 int base = 111 + i * subarea_len;
3379 sa.shape = bstr->
GetInt(base + 1, 3);
3380 int scale_factor = 1;
3381 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
3384 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
3385 sa.text = wxString(t, wxConvUTF8);
3387 int scale_multipliers[4] = {1, 10, 100, 1000};
3388 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
3390 case AIS8_001_22_SHAPE_SECTOR:
3391 sa.left_bound_deg = bstr->
GetInt(
3392 base + 6 + lon_len + lat_len + prec_size + 12, 9);
3393 sa.right_bound_deg = bstr->
GetInt(
3394 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
3395 case AIS8_001_22_SHAPE_CIRCLE:
3397 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
3400 case AIS8_001_22_SHAPE_RECT:
3402 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
3404 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
3407 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
3412 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
3414 sa.orient_deg = bstr->
GetInt(
3415 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
3417 case AIS8_001_22_SHAPE_POLYLINE:
3418 case AIS8_001_22_SHAPE_POLYGON:
3419 for (
int i = 0; i < 4; ++i) {
3420 sa.angles[i] = bstr->
GetInt(base + 6 + i * 20, 10) * 0.5;
3422 bstr->
GetInt(base + 16 + i * 20, 10) * scale_factor;
3426 an.sub_areas.push_back(sa);
3428 ptd->area_notices[an.link_id] = an;
3429 parse_result =
true;
3435 if (bstr->GetBitCount() >= 360) {
3437 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
3440 double lon_tentative = 181.;
3441 double lat_tentative = 91.;
3443 int lon = bstr->
GetInt(57, 25);
3444 int lat = bstr->
GetInt(82, 24);
3446 if (lon & 0x01000000)
3448 lon_tentative = lon / 60000.;
3450 if (lat & 0x00800000)
3452 lat_tentative = lat / 60000.;
3454 ptd->Lon = lon_tentative;
3455 ptd->Lat = lat_tentative;
3458 wxString x = ptd->ShipName;
3459 if (x.Find(
"METEO") == wxNOT_FOUND) {
3461 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
3462 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
3463 slat.ToDouble(&id1);
3464 slon.ToDouble(&id2);
3465 wxString nameID =
"METEO ";
3466 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
3467 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
3470 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
3471 ptd->met_data.day = bstr->
GetInt(107, 5);
3472 ptd->met_data.hour = bstr->
GetInt(112, 5);
3473 ptd->met_data.minute = bstr->
GetInt(117, 6);
3474 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
3475 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
3476 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
3477 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
3479 int tmp = bstr->
GetInt(155, 11);
3480 if (tmp & 0x00000400)
3482 ptd->met_data.air_temp = tmp / 10.;
3483 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
3484 int dew = bstr->
GetInt(173, 10);
3485 if (dew & 0x00000200)
3487 ptd->met_data.dew_point = dew / 10.;
3492 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
3493 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
3495 int horVis = bstr->
GetInt(194, 8);
3496 if (horVis & 0x80u) {
3498 ptd->met_data.hor_vis_GT =
true;
3500 ptd->met_data.hor_vis_GT =
false;
3502 ptd->met_data.hor_vis = horVis / 10.0;
3504 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
3505 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
3506 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
3507 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
3508 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
3509 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
3510 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
3511 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
3512 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
3513 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
3514 ptd->met_data.seastate = bstr->
GetInt(323, 4);
3516 int wt = bstr->
GetInt(327, 10);
3517 if (wt & 0x00000200)
3519 ptd->met_data.water_temp = wt / 10.;
3521 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
3522 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
3523 ptd->met_data.ice = bstr->
GetInt(349, 2);
3525 ptd->Class = AIS_METEO;
3529 ptd->b_NoTrack =
true;
3530 ptd->b_show_track =
false;
3531 ptd->b_positionDoubtful =
false;
3532 ptd->b_positionOnceValid =
true;
3533 b_posn_report =
true;
3534 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3535 ptd->PositionReportTicks = now.GetTicks();
3536 ptd->b_nameValid =
true;
3538 parse_result =
true;
3544 if (dac == 367 && fi == 33) {
3548 const int size = bstr->GetBitCount();
3551 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
3552 const int startbits = 56;
3553 const int slotsize = 112;
3554 const int slots_count = (size - startbits) / slotsize;
3556 for (
int slot = 0; slot < slots_count; slot++) {
3557 slotbit = slot * slotsize;
3558 int type = bstr->
GetInt(slotbit + 57, 4);
3559 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
3560 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
3561 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
3564 if (!ptd->b_nameValid) {
3565 wxString nameID =
"METEO Site: ";
3567 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
3568 ptd->b_nameValid =
true;
3572 int lon = bstr->
GetInt(slotbit + 90, 28);
3573 if (lon & 0x08000000)
3575 ptd->Lon = lon / 600000.;
3577 int lat = bstr->
GetInt(slotbit + 118, 27);
3578 if (lat & 0x04000000)
3580 ptd->Lat = lat / 600000.;
3581 ptd->b_positionOnceValid =
true;
3583 }
else if (type == 1) {
3584 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
3585 ptd->b_nameValid =
true;
3587 }
else if (type == 2) {
3589 int descr = bstr->
GetInt(slotbit + 116, 3);
3590 if (descr == 1 || descr == 2) {
3591 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
3592 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
3593 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
3594 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
3597 }
else if (type == 3) {
3599 int descr = bstr->
GetInt(slotbit + 108, 3);
3600 if (descr == 1 || descr == 2) {
3601 int wltype = bstr->
GetInt(slotbit + 84, 1);
3602 int wl = bstr->
GetInt(slotbit + 85, 16);
3603 if (wl & 0x00004000)
3607 ptd->met_data.water_level = wl / 100.;
3609 ptd->met_data.water_lev_dev = wl / 100.;
3611 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
3612 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
3614 }
else if (type == 6) {
3615 int readbearing = bstr->
GetInt(slotbit + 84, 9);
3616 int readdistance = bstr->
GetInt(slotbit + 93, 9);
3617 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
3618 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
3619 int readLevel = bstr->
GetInt(slotbit + 119, 9);
3621 }
else if (type == 7) {
3623 bstr->
GetInt(slotbit + 111, 3);
3624 if (swell_descr == 1 || swell_descr == 2) {
3625 ptd->met_data.swell_height =
3626 bstr->
GetInt(slotbit + 84, 8) / 10.0;
3627 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
3628 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
3630 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
3631 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
3632 if (wt_descr == 1 || wt_descr == 2)
3633 ptd->met_data.water_temp =
3634 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
3636 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
3637 if (wawe_descr == 1 || wawe_descr == 2) {
3638 ptd->met_data.wave_height =
3639 bstr->
GetInt(slotbit + 134, 8) / 10.0;
3640 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
3641 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
3643 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
3645 }
else if (type == 8) {
3646 ptd->met_data.water_temp =
3647 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
3648 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
3650 }
else if (type == 9) {
3651 int tmp = bstr->
GetInt(slotbit + 84, 11);
3652 if (tmp & 0x00000400)
3654 ptd->met_data.air_temp = tmp / 10.;
3655 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
3666 ptd->met_data.precipitation = pp;
3667 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
3668 ptd->met_data.dew_point =
3669 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
3670 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
3671 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
3672 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
3674 }
else if (type == 11) {
3676 int descr = bstr->
GetInt(slotbit + 113, 3);
3677 if (descr == 1 || descr == 2) {
3678 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
3679 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
3680 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
3685 if (ptd->b_positionOnceValid) {
3686 ptd->Class = AIS_METEO;
3690 ptd->b_NoTrack =
true;
3691 ptd->b_show_track =
false;
3692 ptd->b_positionDoubtful =
false;
3693 b_posn_report =
true;
3694 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3695 ptd->PositionReportTicks = now.GetTicks();
3696 ptd->b_nameValid =
true;
3698 parse_result =
true;
3708 char msg_14_text[968];
3709 if (bstr->GetBitCount() > 40) {
3710 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
3711 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
3713 nd = wxMin(nd, 967);
3714 msg_14_text[nd] = 0;
3715 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
3717 parse_result =
true;
3735 if (b_posn_report) ptd->b_lost =
false;
3737 if (
true == parse_result) {
3739 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
3740 ptd->b_active =
true;
3743 return parse_result;
3746bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3747 unsigned char checksum_value = 0;
3748 int sentence_hex_sum;
3750 wxCharBuffer buf = str_in.ToUTF8();
3751 if (!buf.data())
return false;
3753 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3754 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3755 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3757 int string_length = strlen(str_ascii);
3759 int payload_length = 0;
3760 while ((payload_length < string_length) &&
3761 (str_ascii[payload_length] !=
'*'))
3764 if (payload_length == string_length)
3769 while (index < payload_length) {
3770 checksum_value ^= str_ascii[index];
3774 if (string_length > 4) {
3776 scanstr[0] = str_ascii[payload_length + 1];
3777 scanstr[1] = str_ascii[payload_length + 2];
3779 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3781 if (sentence_hex_sum == checksum_value)
return true;
3787void AisDecoder::UpdateAllCPA(
void) {
3789 for (
const auto &it : GetTargetList()) {
3790 std::shared_ptr<AisTargetData> td = it.second;
3792 if (NULL != td) UpdateOneCPA(td.get());
3796void AisDecoder::UpdateAllTracks(
void) {
3798 for (
const auto &it : GetTargetList()) {
3799 std::shared_ptr<AisTargetData> td = it.second;
3801 if (NULL != td) UpdateOneTrack(td.get());
3807 if (!ptarget->b_positionOnceValid)
return;
3809 if (ptarget->m_ptrack.size() > 0) {
3811 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3812 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3815 ptarget->m_ptrack.pop_back();
3816 ptarget->b_positionDoubtful =
true;
3823 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3826 ptrackpoint.m_lat = ptarget->Lat;
3827 ptrackpoint.m_lon = ptarget->Lon;
3828 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3830 ptarget->m_ptrack.push_back(ptrackpoint);
3832 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3834 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3836 t->SetName(wxString::Format(
3837 _T(
"AIS %s (%u) %s %s"), ptarget->GetFullName().c_str(),
3838 ptarget->MMSI, wxDateTime::Now().FormatISODate().c_str(),
3839 wxDateTime::Now().FormatISOTime().c_str()));
3840 g_TrackList.push_back(t);
3842 m_persistent_tracks[ptarget->MMSI] = t;
3844 t = m_persistent_tracks[ptarget->MMSI];
3847 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3849 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3852 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3853 tp1->m_lon, tp, tp1, t);
3863 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3865 ptarget->m_ptrack.erase(
3866 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3868 return track.m_time < test_time;
3870 ptarget->m_ptrack.end());
3875void AisDecoder::DeletePersistentTrack(
Track *track) {
3876 for (std::map<int, Track *>::iterator iterator = m_persistent_tracks.begin();
3877 iterator != m_persistent_tracks.end(); iterator++) {
3878 if (iterator->second == track) {
3879 int mmsi = iterator->first;
3880 m_persistent_tracks.erase(iterator);
3882 if (0 == m_persistent_tracks.count(mmsi)) {
3883 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3884 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
3886 if (props->m_bPersistentTrack) {
3890 std::shared_ptr<AisTargetData> td =
3891 Get_Target_Data_From_MMSI(mmsi);
3893 props->m_bPersistentTrack =
false;
3894 td->b_mPropPersistTrack =
false;
3896 if (!m_callbacks.confirm_stop_track()) {
3897 props->m_bPersistentTrack =
true;
3909void AisDecoder::UpdateAllAlarms(
void) {
3910 m_bGeneralAlert =
false;
3913 for (
const auto &it : GetTargetList()) {
3914 std::shared_ptr<AisTargetData> td = it.second;
3918 if (!m_bGeneralAlert) {
3920 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3921 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3922 m_bGeneralAlert =
true;
3925 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3926 m_bGeneralAlert =
false;
3929 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3930 m_bGeneralAlert =
false;
3933 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3936 if (td->Class == AIS_SART && td->NavStatus == 14)
3937 m_bGeneralAlert =
true;
3940 if ((td->Class == AIS_DSC) &&
3941 ((td->ShipType == 12) || (td->ShipType == 16)))
3942 m_bGeneralAlert =
true;
3945 ais_alert_type this_alarm = AIS_NO_ALERT;
3948 if (td->Class == AIS_SART && td->NavStatus == 14)
3949 this_alarm = AIS_ALERT_SET;
3952 if ((td->Class == AIS_DSC) &&
3953 ((td->ShipType == 12) || (td->ShipType == 16)))
3954 this_alarm = AIS_ALERT_SET;
3956 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3957 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3960 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3961 td->n_alert_state = AIS_NO_ALERT;
3967 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3968 (td->SOG <= g_ShowMoored_Kts)) {
3969 td->n_alert_state = AIS_NO_ALERT;
3975 if (td->Range_NM > g_CPAMax_NM) {
3976 td->n_alert_state = AIS_NO_ALERT;
3981 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3982 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3983 (td->Class != AIS_METEO)) {
3985 if (td->TCPA < g_TCPA_Max) {
3986 if (td->b_isFollower)
3987 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3989 this_alarm = AIS_ALERT_SET;
3992 if (td->b_isFollower)
3993 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3995 this_alarm = AIS_ALERT_SET;
4003 if (g_bAIS_ACK_Timeout || (td->Class == AIS_SART) ||
4004 ((td->Class == AIS_DSC) &&
4005 ((td->ShipType == 12) || (td->ShipType == 16)))) {
4006 if (td->b_in_ack_timeout) {
4007 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
4008 if (delta.GetMinutes() > g_AckTimeout_Mins)
4009 td->b_in_ack_timeout =
false;
4015 if (td->b_in_ack_timeout) {
4016 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
4020 td->n_alert_state = this_alarm;
4026 ptarget->Range_NM = -1.;
4035 DistanceBearingMercator(ptarget->Lat, ptarget->Lon, gLat, gLon, &brg, &dist);
4036 ptarget->Range_NM = dist;
4039 if (dist <= 1e-5) ptarget->Brg = -1.0;
4041 if (!ptarget->b_positionOnceValid || !bGPSValid) {
4042 ptarget->bCPA_Valid =
false;
4046 if (ptarget->Class == AIS_METEO) {
4047 ptarget->bCPA_Valid =
false;
4056 if (ptarget->b_OwnShip) {
4058 ptarget->TCPA = -100;
4059 ptarget->bCPA_Valid =
false;
4063 double cpa_calc_ownship_cog = gCog;
4064 double cpa_calc_target_cog = ptarget->COG;
4067 if (std::isnan(gSog) || (gSog > 102.2)) {
4068 ptarget->bCPA_Valid =
false;
4073 if (std::isnan(gCog) || gCog == 360.0) {
4075 cpa_calc_ownship_cog =
4079 ptarget->bCPA_Valid =
false;
4085 if (ptarget->COG == 360.0) {
4086 if (ptarget->SOG > 102.2) {
4087 ptarget->bCPA_Valid =
false;
4089 }
else if (ptarget->SOG < .01)
4090 cpa_calc_target_cog =
4094 ptarget->bCPA_Valid =
false;
4100 double v0 = gSog * 1852.;
4101 double v1 = ptarget->SOG * 1852.;
4103 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4107 ptarget->bCPA_Valid =
false;
4114 double east1 = (ptarget->Lon - gLon) * 60 * 1852;
4115 double north1 = (ptarget->Lat - gLat) * 60 * 1852;
4117 double east = east1 * (cos(gLat * PI / 180.));
4119 double north = north1;
4122 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4123 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4124 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4125 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4128 double fc = (v0 * cosa) - (v1 * cosb);
4129 double fs = (v0 * sina) - (v1 * sinb);
4131 double d = (fc * fc) + (fs * fs);
4139 tcpa = ((fc * east) + (fs * north)) / d;
4142 ptarget->TCPA = tcpa * 60.;
4147 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4149 ll_gc_ll(gLat, gLon, cpa_calc_ownship_cog, gSog * tcpa, &OwnshipLatCPA,
4151 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4152 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4155 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4158 ptarget->bCPA_Valid =
true;
4160 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4164void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4167 if (m_ptentative_dsctarget) {
4168 ProcessDSx(m_dsc_last_string,
true);
4172void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4177 wxDateTime now = wxDateTime::Now();
4180 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4183 auto it = current_targets.begin();
4184 std::vector<int> remove_array;
4186 while (it != current_targets.end()) {
4187 if (it->second == NULL)
4189 current_targets.erase(it);
4194 std::shared_ptr<AisTargetData> xtd = it->second;
4196 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4197 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4209 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4211 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4212 double iECD_LostTimeOut = 0.0;
4216 if (xtd->Class == AIS_CLASS_B) {
4217 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR))
4218 iECD_LostTimeOut = 18 * 60;
4220 iECD_LostTimeOut = 180;
4222 if (xtd->Class == AIS_CLASS_A) {
4223 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4225 iECD_LostTimeOut = 18 * 60;
4227 iECD_LostTimeOut = 60;
4229 iECD_LostTimeOut = 60;
4232 if ((target_posn_age > iECD_LostTimeOut) &&
4233 (xtd->Class != AIS_GPSG_BUDDY))
4234 xtd->b_active =
false;
4236 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4237 }
else if (g_bMarkLost) {
4238 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4239 (xtd->Class != AIS_GPSG_BUDDY))
4240 xtd->b_active =
false;
4243 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4244 removelost_Mins = 18.0;
4248 if (g_bRemoveLost || g_bInlandEcdis) {
4250 (xtd->Class == AIS_ARPA &&
4252 if (((target_posn_age > removelost_Mins * 60) &&
4253 (xtd->Class != AIS_GPSG_BUDDY)) ||
4258 xtd->b_positionOnceValid =
false;
4266 long mmsi_long = xtd->MMSI;
4267 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4272 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4273 xtd->b_removed =
true;
4275 remove_array.push_back(xtd->MMSI);
4282 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
4284 if (xtd->MMSI == props->MMSI) {
4285 if (props->m_bignore) {
4286 remove_array.push_back(xtd->MMSI);
4287 xtd->b_removed =
true;
4295 if (xtd->MMSI == g_OwnShipmmsi) {
4296 remove_array.push_back(xtd->MMSI);
4297 xtd->b_removed =
true;
4305 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4306 auto itd = current_targets.find(remove_array[i]);
4307 if (itd != current_targets.end()) {
4308 std::shared_ptr<AisTargetData> td = itd->second;
4309 current_targets.erase(itd);
4318 m_bSuppressed =
false;
4319 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4320 (g_bShowScaled && g_bAllowShowScaled))
4321 m_bSuppressed =
true;
4323 m_bAIS_Audio_Alert_On =
false;
4332 std::shared_ptr<AisTargetData> palert_target = NULL;
4333 int audioType = AISAUDIO_NONE;
4335 if (!g_pais_alert_dialog_active) {
4336 pAISMOBRoute = NULL;
4337 double tcpa_min = 1e6;
4338 double sart_range = 1e6;
4339 std::shared_ptr<AisTargetData> palert_target_cpa = NULL;
4340 std::shared_ptr<AisTargetData> palert_target_sart = NULL;
4341 std::shared_ptr<AisTargetData> palert_target_dsc = NULL;
4343 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4344 std::shared_ptr<AisTargetData> td = it->second;
4346 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4347 if (g_bAIS_CPA_Alert && td->b_active) {
4348 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4349 if (td->TCPA < tcpa_min) {
4350 tcpa_min = td->TCPA;
4351 palert_target_cpa = td;
4355 }
else if ((td->Class == AIS_DSC) &&
4356 ((td->ShipType == 12) || (td->ShipType == 16))) {
4358 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4359 palert_target_dsc = td;
4362 td->b_isDSCtarget =
false;
4367 else if (td->Class == AIS_SART) {
4369 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4370 if (td->Range_NM < sart_range) {
4371 tcpa_min = sart_range;
4372 palert_target_sart = td;
4382 palert_target = palert_target_cpa;
4383 if (palert_target) audioType = AISAUDIO_CPA;
4385 if (palert_target_sart) {
4386 palert_target = palert_target_sart;
4387 audioType = AISAUDIO_SART;
4390 if (palert_target_dsc) {
4391 palert_target = palert_target_dsc;
4392 audioType = AISAUDIO_DSC;
4396 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4401 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4404std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
int mmsi) {
4405 if (AISTargetList.find(mmsi) == AISTargetList.end())
4408 return AISTargetList[mmsi];
4411ArrayOfMmsiProperties g_MMSI_Props_Array;
4415MmsiProperties::MmsiProperties(wxString &spec) {
4417 wxStringTokenizer tkz(spec, _T(
";"));
4420 s = tkz.GetNextToken();
4425 s = tkz.GetNextToken();
4427 if (s.Upper() == _T(
"ALWAYS"))
4428 TrackType = TRACKTYPE_ALWAYS;
4429 else if (s.Upper() == _T(
"NEVER"))
4430 TrackType = TRACKTYPE_NEVER;
4433 s = tkz.GetNextToken();
4435 if (s.Upper() == _T(
"IGNORE")) m_bignore =
true;
4438 s = tkz.GetNextToken();
4440 if (s.Upper() == _T(
"MOB")) m_bMOB =
true;
4443 s = tkz.GetNextToken();
4445 if (s.Upper() == _T(
"VDM")) m_bVDM =
true;
4448 s = tkz.GetNextToken();
4450 if (s.Upper() == _T(
"FOLLOWER")) m_bFollower =
true;
4453 s = tkz.GetNextToken();
4455 if (s.Upper() == _T(
"PERSIST")) m_bPersistentTrack =
true;
4458 s = tkz.GetNextToken();
4460 m_ShipName = s.Upper();
4464MmsiProperties::~MmsiProperties() {}
4466void MmsiProperties::Init(
void) {
4468 TrackType = TRACKTYPE_DEFAULT;
4472 m_bFollower =
false;
4473 m_bPersistentTrack =
false;
4474 m_ShipName = wxEmptyString;
4477wxString MmsiProperties::Serialize(
void) {
4481 sMMSI.Printf(_T(
"%d"), MMSI);
4485 if (TRACKTYPE_ALWAYS == TrackType)
4486 sMMSI << _T(
"always");
4487 else if (TRACKTYPE_NEVER == TrackType)
4488 sMMSI << _T(
"never");
4493 sMMSI << _T(
"ignore");
4508 sMMSI << _T(
"Follower");
4512 if (m_bPersistentTrack) {
4513 sMMSI << _T(
"PERSIST");
4517 if (m_ShipName == wxEmptyString) {
4518 m_ShipName = GetShipNameFromFile(MMSI);
4520 sMMSI << m_ShipName;
4525 AIS_Target_Name_Hash *AISTargetNamesC,
4526 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4527 if (g_benableAISNameCache) {
4528 wxString ship_name = wxEmptyString;
4531 if (!pTargetData->b_nameValid) {
4532 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4533 if (it != AISTargetNamesC->end()) {
4534 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4535 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4536 ship_name.length() + 1);
4537 pTargetData->b_nameValid =
true;
4538 pTargetData->b_nameFromCache =
true;
4539 }
else if (!g_bUseOnlyConfirmedAISName) {
4540 it = AISTargetNamesNC->find(mmsi);
4541 if (it != AISTargetNamesNC->end()) {
4542 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4543 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4544 ship_name.length() + 1);
4545 pTargetData->b_nameValid =
true;
4546 pTargetData->b_nameFromCache =
true;
4551 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4552 (pTargetData->MID == 19) ||
4553 (pTargetData->MID == 123) ||
4554 (pTargetData->MID == 124)) {
4556 pTargetData->b_nameFromCache =
false;
4557 ship_name = trimAISField(pTargetData->ShipName);
4558 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4559 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4561 AISTargetNamesC->end()) {
4562 if ((*AISTargetNamesC)[mmsi] ==
4564 if (itNC != AISTargetNamesNC->end()) {
4566 AISTargetNamesNC->erase(itNC);
4569 if (itNC != AISTargetNamesNC->end()) {
4571 if ((*AISTargetNamesNC)[mmsi] ==
4574 (*AISTargetNamesC)[mmsi] = ship_name;
4576 AISTargetNamesNC->erase(itNC);
4579 (*AISTargetNamesNC)[mmsi] = ship_name;
4581 if (g_bUseOnlyConfirmedAISName)
4582 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4583 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4585 (*AISTargetNamesNC)[mmsi] = ship_name;
4590 AISTargetNamesNC->end()) {
4591 if ((*AISTargetNamesNC)[mmsi] ==
4594 (*AISTargetNamesC)[mmsi] = ship_name;
4596 AISTargetNamesNC->erase(itNC);
4598 (*AISTargetNamesNC)[mmsi] = ship_name;
4601 (*AISTargetNamesNC)[mmsi] = ship_name;
4603 if (g_bUseOnlyConfirmedAISName) {
4604 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4605 strncpy(pTargetData->ShipName,
"Unknown ",
4613wxString GetShipNameFromFile(
int nmmsi) {
4614 wxString name = wxEmptyString;
4615 if (g_benableAISNameCache) {
4616 std::ifstream infile(AISTargetNameFileName.mb_str());
4619 while (getline(infile, line)) {
4620 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()), _T(
","));
4621 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4622 name = tokenizer.GetNextToken().Trim();
4625 tokenizer.GetNextToken();
4634int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4638 if ((!lon_bits || lon_bits == 999) && siteID) {
4641 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4642 if (points.size()) {
4643 for (
const auto &point : points) {
4645 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4647 new_mmsi = point.mmsi;
4653 if (!found && !lon_bits) {
4658 double lon_tentative = 181.;
4659 double lat_tentative = 91.;
4661 if (lon_bits == 25) {
4662 if (m_lon & 0x01000000)
4663 m_lon |= 0xFE000000;
4664 lon_tentative = m_lon / 60000.;
4666 if (m_lat & 0x00800000)
4667 m_lat |= 0xFF000000;
4668 lat_tentative = m_lat / 60000.;
4670 }
else if (lon_bits == 28) {
4671 if (m_lon & 0x08000000)
4672 m_lon |= 0xf0000000;
4673 lon_tentative = m_lon / 600000.;
4675 if (m_lat & 0x04000000)
4676 m_lat |= 0xf8000000;
4677 lat_tentative = m_lat / 600000.;
4682 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4683 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4691 static int nextMeteommsi = 199400000;
4692 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4694 if (lon_bits != 999 && points.size()) {
4695 wxString t_lat, t_lon;
4696 for (
const auto &point : points) {
4698 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4700 new_mmsi = point.mmsi;
4710 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4711 new_mmsi = nextMeteommsi;
4716bool isBuoyMmsi(
const int msi) {
4721 int mid = msi / 1000000;
4722 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 std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString error_msg="")
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.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Represents a waypoint or mark within the navigation system.
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
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.
A generic position and navigation data structure.
double kCog
Course over ground in degrees.
double kHdt
True heading in degrees.
double kHdm
Magnetic heading in degrees.
double kLat
Latitude in decimal degrees.
double kSog
Speed over ground in knots.
double kVar
Magnetic variation in degrees.
double kLon
Longitude in decimal degrees.