26 #include <wx/wxprec.h>
41 #include <wx/datetime.h>
44 #include <wx/string.h>
45 #include <wx/textfile.h>
47 #include <wx/tokenzr.h>
51 #include "rapidjson/document.h"
52 #include "rapidjson/writer.h"
53 #include "rapidjson/stringbuffer.h"
55 #include "model/ais_decoder.h"
57 #include "model/meteo_points.h"
58 #include "model/ais_target_data.h"
59 #include "model/comm_navmsg_bus.h"
60 #include "model/config_vars.h"
61 #include "model/geodesic.h"
62 #include "model/georef.h"
63 #include "model/idents.h"
64 #include "model/multiplexer.h"
65 #include "model/navutil_base.h"
66 #include "model/own_ship.h"
67 #include "model/route_point.h"
68 #include "model/select.h"
69 #include "SoundFactory.h"
70 #include "model/track.h"
71 #include "N2KParser.h"
74 static const long long lNaN = 0xfff8000000000000;
75 #define NAN (*(double *)&lNaN)
79 wxEvtHandler* g_pais_alert_dialog_active;
83 bool g_bUseOnlyConfirmedAISName;
84 wxString GetShipNameFromFile(
int);
85 wxString AISTargetNameFileName;
107 EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
108 EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
111 static const
double ms_to_knot_factor = 1.9438444924406;
117 static
bool b_firstrx;
118 static
int first_rx_ticks;
120 static
double arpa_ref_hdg = NAN;
123 static inline
double GeodesicRadToDeg(
double rads) {
124 return rads * 180.0 / M_PI;
127 static inline double MS2KNOTS(
double ms) {
128 return ms * 1.9438444924406;
131 int AisMeteoNewMmsi(
int,
int,
int,
int,
int);
135 AIS_Target_Name_Hash *AISTargetNamesC,
136 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
139 : m_signalk_selfid(
""), m_callbacks(callbacks) {
141 AISTargetNamesC =
new AIS_Target_Name_Hash;
142 AISTargetNamesNC =
new AIS_Target_Name_Hash;
144 if (g_benableAISNameCache) {
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();
168 BuildERIShipTypeHash();
170 g_pais_alert_dialog_active =
nullptr;
171 m_bAIS_Audio_Alert_On =
false;
175 m_bAIS_AlertPlaying =
false;
177 TimerAIS.SetOwner(
this, TIMER_AIS1);
178 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
180 m_ptentative_dsctarget = NULL;
181 m_dsc_timer.SetOwner(
this, TIMER_DSC);
191 AisDecoder::~AisDecoder(
void) {
200 if (outfile.Open(AISTargetNameFileName)) {
202 content = wxT(
"+++==Confirmed Entry's==+++");
203 AIS_Target_Name_Hash::iterator it;
204 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
205 content.append(_T(
"\r\n"));
206 content.append(wxString::Format(wxT(
"%i"), it->first));
207 content.append(_T(
",")).append(it->second);
209 content.append(_T(
"\r\n"));
210 content.append(_T(
"+++==Non Confirmed Entry's==+++"));
211 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
212 content.append(_T(
"\r\n"));
213 content.append(wxString::Format(wxT(
"%i"), it->first));
214 content.append(_T(
",")).append(it->second);
216 outfile.Write(content);
220 AISTargetNamesC->clear();
221 delete AISTargetNamesC;
222 AISTargetNamesNC->clear();
223 delete AISTargetNamesNC;
228 m_AIS_Audio_Alert_Timer.Stop();
233 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
235 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
239 void AisDecoder::InitCommListeners(
void) {
242 auto& msgbus = NavMsgBus::GetInstance();
247 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
249 auto ptr = ev.GetSharedPtr();
250 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
251 HandleN0183_AIS( n0183_msg );
256 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
259 auto ptr = ev.GetSharedPtr();
260 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
261 HandleN0183_AIS( n0183_msg );
266 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
268 auto ptr = ev.GetSharedPtr();
269 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
270 HandleN0183_AIS( n0183_msg );
275 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
277 auto ptr = ev.GetSharedPtr();
278 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
279 HandleN0183_AIS( n0183_msg );
284 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
287 auto ptr = ev.GetSharedPtr();
288 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
289 HandleN0183_AIS( n0183_msg );
294 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
296 auto ptr = ev.GetSharedPtr();
297 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
298 HandleN0183_AIS(n0183_msg);
303 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
305 auto ptr = ev.GetSharedPtr();
306 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
307 HandleN0183_AIS( n0183_msg );
312 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
314 auto ptr = ev.GetSharedPtr();
315 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
316 HandleN0183_AIS( n0183_msg );
321 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
323 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
328 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
329 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
331 HandleN2K_129038(UnpackEvtPointer<Nmea2000Msg>(ev));
336 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
337 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
339 HandleN2K_129039(UnpackEvtPointer<Nmea2000Msg>(ev));
344 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
345 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
347 HandleN2K_129041(UnpackEvtPointer<Nmea2000Msg>(ev));
352 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
353 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
355 HandleN2K_129794(UnpackEvtPointer<Nmea2000Msg>(ev));
360 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
361 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
363 HandleN2K_129809(UnpackEvtPointer<Nmea2000Msg>(ev));
368 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
369 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
371 HandleN2K_129810(UnpackEvtPointer<Nmea2000Msg>(ev));
376 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
377 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
379 HandleN2K_129793(UnpackEvtPointer<Nmea2000Msg>(ev));
385 bool AisDecoder::HandleN0183_AIS( std::shared_ptr <const Nmea0183Msg> n0183_msg ){
386 std::string str = n0183_msg->payload;
387 wxString sentence(str.c_str());
388 DecodeN0183(sentence);
393 bool AisDecoder::HandleN2K_129038( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
394 std::vector<unsigned char> v = n2k_msg->payload;
397 tN2kAISRepeat Repeat;
408 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
409 tN2kAISTransceiverInformation AISTransceiverInformation;
412 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID,
413 Latitude, Longitude, Accuracy, RAIM, Seconds,
414 COG, SOG, Heading, ROT, NavStat, AISTransceiverInformation)) {
419 long mmsi_long = mmsi;
420 std::shared_ptr<AisTargetData>pTargetData = 0;
421 bool bnewtarget =
false;
423 auto it = AISTargetList.find(mmsi);
424 if (it == AISTargetList.end())
426 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
430 pTargetData = it->second;
433 wxDateTime now = wxDateTime::Now();
437 pTargetData->MMSI = mmsi;
438 pTargetData->MID = MessageID;
439 pTargetData->MMSI = mmsi;
440 pTargetData->Class = AIS_CLASS_A;
442 if( 97 == pTargetData->MMSI / 10000000) {
443 pTargetData->Class = AIS_SART;
445 pTargetData->StaticReportTicks = now.GetTicks();
447 pTargetData->NavStatus = (ais_nav_status)NavStat;
448 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
449 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
450 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
451 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
452 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
454 pTargetData->ROTAIS = ROT;
456 double rot_dir = 1.0;
468 pTargetData->b_OwnShip =
469 AISTransceiverInformation == tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
470 pTargetData->b_active =
true;
471 pTargetData->b_lost =
false;
472 pTargetData->b_positionOnceValid =
true;
473 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
474 pTargetData->PositionReportTicks = now.GetTicks();
476 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
477 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
487 bool AisDecoder::HandleN2K_129039( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
488 std::vector<unsigned char> v = n2k_msg->payload;
498 tN2kAISRepeat Repeat;
508 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
509 tN2kAISTransceiverInformation AISTransceiverInformation;
511 bool DSC, Band, Msg22, State, Display;
514 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID,
515 Latitude, Longitude, Accuracy, RAIM, Seconds, COG,
516 SOG, AISTransceiverInformation, Heading,
517 Unit, Display, DSC, Band, Msg22, Mode, State)) {
522 long mmsi_long = mmsi;
523 std::shared_ptr<AisTargetData> pTargetData = 0;
524 bool bnewtarget =
false;
526 auto it = AISTargetList.find(mmsi);
527 if (it == AISTargetList.end())
529 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
533 pTargetData = it->second;
536 wxDateTime now = wxDateTime::Now();
540 pTargetData->MMSI = mmsi;
541 pTargetData->MID = MessageID;
542 pTargetData->MMSI = mmsi;
543 pTargetData->Class = AIS_CLASS_B;
544 pTargetData->NavStatus = (ais_nav_status)NavStat;
545 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
546 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
547 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
548 if(!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
549 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
551 pTargetData->b_positionOnceValid =
true;
552 pTargetData->b_active =
true;
553 pTargetData->b_lost =
false;
554 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
555 pTargetData->PositionReportTicks = now.GetTicks();
556 pTargetData->b_OwnShip =
557 AISTransceiverInformation == tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
559 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
560 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
572 bool AisDecoder::HandleN2K_129041( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
573 std::vector<unsigned char> v = n2k_msg->payload;
575 tN2kAISAtoNReportData data;
578 struct tN2kAISAtoNReportData {
580 tN2kAISRepeat Repeat;
589 double PositionReferenceStarboard ;
590 double PositionReferenceTrueNorth;
591 tN2kAISAtoNType AtoNType;
592 bool OffPositionIndicator;
593 bool VirtualAtoNFlag;
594 bool AssignedModeFlag;
595 tN2kGNSStype GNSSType;
597 tN2kAISTransceiverInformation AISTransceiverInformation;
598 char AtoNName[34 + 1];
601 if (ParseN2kPGN129041(v, data)){
604 int mmsi = data.UserID;
605 long mmsi_long = mmsi;
606 std::shared_ptr<AisTargetData> pTargetData = 0;
607 bool bnewtarget =
false;
609 auto it = AISTargetList.find(mmsi);
610 if (it == AISTargetList.end())
612 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
616 pTargetData = it->second;
620 pTargetData->MMSI = mmsi;
622 wxDateTime now = wxDateTime::Now();
625 int offpos = data.OffPositionIndicator;
626 int virt = data.VirtualAtoNFlag;
629 pTargetData->NavStatus = ATON_VIRTUAL;
631 pTargetData->NavStatus = ATON_REAL;
633 pTargetData->m_utc_sec = data.Seconds;
635 if (pTargetData->m_utc_sec <= 59 ){
636 pTargetData->NavStatus += 1;
637 if (offpos) pTargetData->NavStatus += 1;
640 data.AtoNName[34] = 0;
641 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
642 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
643 pTargetData->b_nameValid =
true;
644 pTargetData->MID = 124;
646 pTargetData->ShipType = data.AtoNType;
647 pTargetData->Class = AIS_ATON;
649 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
650 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
651 pTargetData->b_positionDoubtful =
false;
652 pTargetData->b_positionOnceValid =
true;
653 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
654 pTargetData->PositionReportTicks = now.GetTicks();
658 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
659 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
669 bool AisDecoder::HandleN2K_129794( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
670 std::vector<unsigned char> v = n2k_msg->payload;
673 tN2kAISRepeat Repeat;
677 char Name[SHIP_NAME_LEN];
686 char Destination[DESTINATION_LEN];
687 tN2kAISVersion AISversion;
688 tN2kGNSStype GNSStype;
690 tN2kAISTranceiverInfo AISinfo;
693 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID,
694 IMOnumber, Callsign, Name, VesselType, Length,
695 Beam, PosRefStbd, PosRefBow, ETAdate, ETAtime,
696 Draught, Destination, AISversion, GNSStype,
702 long mmsi_long = mmsi;
703 std::shared_ptr<AisTargetData> pTargetData = 0;
704 bool bnewtarget =
false;
706 auto it = AISTargetList.find(mmsi);
707 if (it == AISTargetList.end())
709 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
713 pTargetData = it->second;
717 pTargetData->MMSI = mmsi;
718 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
719 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
720 Name[
sizeof(Name) - 1] = 0;
721 pTargetData->b_nameValid =
true;
722 pTargetData->MID = 124;
724 pTargetData->b_OwnShip =
725 AISinfo == tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
727 pTargetData->DimA = PosRefBow;
728 pTargetData->DimB = Length - PosRefBow;
729 pTargetData->DimC = Beam - PosRefStbd;
730 pTargetData->DimD = PosRefStbd;
731 pTargetData->Draft = Draught;
732 pTargetData->IMO = IMOnumber;
733 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
734 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
735 pTargetData->ShipType = (
unsigned char)VesselType;
736 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
737 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
738 Destination[
sizeof(Destination) - 1] = 0;
740 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
741 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
742 wxDateTime t((time_t)secs);
744 wxDateTime tz = t.ToUTC();
745 pTargetData->ETA_Mo = tz.GetMonth() + 1;
746 pTargetData->ETA_Day = tz.GetDay();
747 pTargetData->ETA_Hr = tz.GetHour();
748 pTargetData->ETA_Min = tz.GetMinute();
752 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
753 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
762 bool AisDecoder::HandleN2K_129809( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
763 std::vector<unsigned char> v = n2k_msg->payload;
766 tN2kAISRepeat Repeat;
770 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name))
775 long mmsi_long = mmsi;
776 std::shared_ptr<AisTargetData> pTargetData = 0;
777 bool bnewtarget =
false;
779 auto it = AISTargetList.find(mmsi);
780 if (it == AISTargetList.end())
782 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
786 pTargetData = it->second;
790 pTargetData->MMSI = mmsi;
791 Name[
sizeof(Name) - 1] = 0;
792 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
793 pTargetData->b_nameValid =
true;
794 pTargetData->MID = 124;
796 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
797 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
809 bool AisDecoder::HandleN2K_129810( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
810 std::vector<unsigned char> v = n2k_msg->payload;
813 tN2kAISRepeat Repeat;
822 uint32_t MothershipID;
824 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID,
825 VesselType, Vendor, Callsign, Length, Beam,
826 PosRefStbd, PosRefBow, MothershipID))
831 long mmsi_long = mmsi;
832 std::shared_ptr<AisTargetData> pTargetData = 0;
833 bool bnewtarget =
false;
835 auto it = AISTargetList.find(mmsi);
836 if (it == AISTargetList.end())
838 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
842 pTargetData = it->second;
846 pTargetData->MMSI = mmsi;
847 pTargetData->DimA = PosRefBow;
848 pTargetData->DimB = Length - PosRefBow;
849 pTargetData->DimC = Beam - PosRefStbd;
850 pTargetData->DimD = PosRefStbd;
851 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
852 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
853 pTargetData->ShipType = (
unsigned char)VesselType;
855 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
856 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
866 bool AisDecoder::HandleN2K_129793( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
867 std::vector<unsigned char> v = n2k_msg->payload;
870 tN2kAISRepeat Repeat;
874 unsigned int SecondsSinceMidnight;
875 unsigned int DaysSinceEpoch;
877 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID,
879 SecondsSinceMidnight, DaysSinceEpoch))
881 wxDateTime now = wxDateTime::Now();
887 long mmsi_long = mmsi;
888 std::shared_ptr<AisTargetData> pTargetData = 0;
889 bool bnewtarget =
false;
891 auto it = AISTargetList.find(mmsi);
892 if (it == AISTargetList.end())
894 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
898 pTargetData = it->second;
902 pTargetData->MMSI = mmsi;
903 pTargetData->Class = AIS_BASE;
905 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
906 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
907 pTargetData->b_positionDoubtful =
false;
908 pTargetData->b_positionOnceValid =
true;
909 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
910 pTargetData->PositionReportTicks = now.GetTicks();
915 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
916 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
933 void AisDecoder::BuildERIShipTypeHash(
void) {
934 make_hash_ERI(8000, _(
"Vessel, type unknown"));
935 make_hash_ERI(8150, _(
"Freightbarge"));
936 make_hash_ERI(8160, _(
"Tankbarge"));
937 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
938 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
939 make_hash_ERI(8430, _(
"Pushboat, single"));
940 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
941 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
942 make_hash_ERI(8490, _(
"Bunkership"));
943 make_hash_ERI(8010, _(
"Motor freighter"));
944 make_hash_ERI(8020, _(
"Motor tanker"));
945 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
946 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
947 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
948 make_hash_ERI(8030, _(
"Container vessel"));
949 make_hash_ERI(8040, _(
"Gas tanker"));
950 make_hash_ERI(8050, _(
"Motor freighter, tug"));
951 make_hash_ERI(8060, _(
"Motor tanker, tug"));
952 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
953 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
954 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
955 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
956 make_hash_ERI(8110, _(
"Tug, freighter"));
957 make_hash_ERI(8120, _(
"Tug, tanker"));
958 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
959 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
960 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
961 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
962 make_hash_ERI(8170, _(
"Freightbarge with containers"));
963 make_hash_ERI(8180, _(
"Tankbarge, gas"));
964 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
965 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
966 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
967 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
968 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
969 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
970 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
971 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
972 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
973 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
975 _(
"Pushtow, two barges at least one tanker or gas barge"));
977 _(
"Pushtow, three barges at least one tanker or gas barge"));
979 _(
"Pushtow, four barges at least one tanker or gas barge"));
981 _(
"Pushtow, five barges at least one tanker or gas barge"));
983 _(
"Pushtow, six barges at least one tanker or gas barge"));
985 _(
"Pushtow, seven barges at least one tanker or gas barge"));
987 _(
"Pushtow, eight barges at least one tanker or gas barge"));
989 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
990 make_hash_ERI(8400, _(
"Tug, single"));
991 make_hash_ERI(8410, _(
"Tug, one or more tows"));
992 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
993 make_hash_ERI(8430, _(
"Pushboat, single"));
994 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
995 make_hash_ERI(8441, _(
"Ferry"));
996 make_hash_ERI(8442, _(
"Red cross ship"));
997 make_hash_ERI(8443, _(
"Cruise ship"));
998 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
999 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
1000 "cable-ship, buoy-ship, dredge"));
1001 make_hash_ERI(8480, _(
"Fishing boat"));
1002 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
1003 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
1004 make_hash_ERI(1510, _(
"Unit carrier maritime"));
1005 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
1006 make_hash_ERI(1530, _(
"Tanker"));
1007 make_hash_ERI(1540, _(
"Liquified gas tanker"));
1008 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
1009 make_hash_ERI(1900, _(
"Fast ship"));
1010 make_hash_ERI(1910, _(
"Hydrofoil"));
1016 void AisDecoder::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg){
1017 rapidjson::Document root;
1019 root.Parse(sK_msg->raw_message);
1021 if (root.HasParseError())
return;
1023 if (root.HasMember(
"self")) {
1029 if (m_signalk_selfid.IsEmpty()) {
1034 int meteo_SiteID = 0;
1035 if (root.HasMember(
"context") && root[
"context"].IsString()) {
1036 wxString context = root[
"context"].GetString();
1037 if (context == m_signalk_selfid) {
1039 wxLogMessage(_T(
"** Ignore context own ship.."));
1043 wxString mmsi_string;
1044 if (context.StartsWith(_T(
"vessels.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1045 context.StartsWith(_T(
"atons.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1046 context.StartsWith(_T(
"aircraft.urn:mrn:imo:mmsi:"), &mmsi_string)) {
1049 if (mmsi_string.ToLong(&mmsi)) {
1055 else if (context.StartsWith(_T(
"meteo.urn:mrn:imo:mmsi:"), &mmsi_string)) {
1057 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
1058 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
1061 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
1072 if (g_pMUX && g_pMUX->IsLogActive()) {
1074 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
1075 std::string source = sK_msg->source->to_string();
1076 g_pMUX->LogInputMessage( logmsg, source,
false,
false);
1080 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1081 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
1083 if (props->m_bignore) {
1090 wxJSONWriter writer;
1091 writer.Write(root, dbg);
1093 wxString msg( _T(
"AisDecoder::OnEvtSignalK: ") );
1097 std::shared_ptr<AisTargetData> pTargetData = 0;
1098 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1099 bool bnewtarget =
false;
1100 int last_report_ticks;
1102 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
1105 pTargetData->MMSI = mmsi;
1106 getMmsiProperties(pTargetData);
1107 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
1108 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin(); itr != root[
"updates"].End(); ++itr) {
1109 handleUpdate(pTargetData, bnewtarget, *itr);
1114 if (97 == mmsi / 10000000) {
1115 pTargetData->Class = AIS_SART;
1116 }
else if (1994 == mmsi / 100000) {
1118 pTargetData->Class = AIS_METEO;
1119 pTargetData->met_data.original_mmsi = origin_mmsi;
1120 pTargetData->met_data.stationID = meteo_SiteID;
1123 wxString met_name = pTargetData->ShipName;
1124 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
1127 s_id << meteo_SiteID;
1128 id1 = wxAtoi(s_id.Mid(1, 3));
1129 id2 = wxAtoi(s_id.Mid(4, 3));
1130 met_name =
"METEO ";
1131 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
1132 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
1134 pTargetData->b_nameValid =
true;
1135 pTargetData->MID = 123;
1136 pTargetData->COG = -1.;
1137 pTargetData->HDG = 511;
1138 pTargetData->SOG = -1.;
1139 pTargetData->b_NoTrack =
true;
1140 pTargetData->b_show_track =
false;
1142 pTargetData->b_OwnShip =
false;
1143 AISTargetList[pTargetData->MMSI] = pTargetData;
1147 void AisDecoder::handleUpdate(std::shared_ptr<AisTargetData> pTargetData,
bool bnewtarget,
1148 const rapidjson::Value &update) {
1149 wxString sfixtime =
"";
1151 if (update.HasMember(
"timestamp")) {
1152 sfixtime = update[
"timestamp"].GetString();
1154 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
1155 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin(); itr != update[
"values"].End(); ++itr) {
1156 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
1159 wxDateTime now = wxDateTime::Now();
1160 pTargetData->m_utc_hour = now.ToUTC().GetHour();
1161 pTargetData->m_utc_min = now.ToUTC().GetMinute();
1162 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
1164 pTargetData->b_active =
true;
1165 pTargetData->b_lost =
false;
1167 if (pTargetData->b_positionOnceValid) {
1168 long mmsi_long = pTargetData->MMSI;
1170 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
1171 (
void *)mmsi_long, SELTYPE_AISTARGET);
1172 pSel->SetUserData(pTargetData->MMSI);
1174 UpdateOneCPA(pTargetData.get());
1175 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
1178 void AisDecoder::updateItem(std::shared_ptr<AisTargetData> pTargetData,
bool bnewtarget,
1179 const rapidjson::Value &item, wxString &sfixtime)
const {
1180 if (item.HasMember(
"path") && item.HasMember(
"value")) {
1181 const wxString &update_path = item[
"path"].GetString();
1182 if (update_path == _T(
"navigation.position")) {
1183 if (item[
"value"].HasMember(
"latitude") && item[
"value"].HasMember(
"longitude")) {
1184 wxDateTime now = wxDateTime::Now();
1186 double lat = item[
"value"][
"latitude"].GetDouble();
1187 double lon = item[
"value"][
"longitude"].GetDouble();
1188 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1189 pTargetData->PositionReportTicks = now.GetTicks();
1190 pTargetData->StaticReportTicks = now.GetTicks();
1191 pTargetData->Lat = lat;
1192 pTargetData->Lon = lon;
1193 pTargetData->b_positionOnceValid =
true;
1194 pTargetData->b_positionDoubtful =
false;
1201 }
else if (update_path == _T(
"navigation.speedOverGround") && item[
"value"].IsNumber()) {
1202 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
1203 }
else if (update_path == _T(
"navigation.courseOverGroundTrue") && item[
"value"].IsNumber()) {
1204 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1205 }
else if (update_path == _T(
"navigation.headingTrue") && item[
"value"].IsNumber()) {
1206 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1207 }
else if (update_path == _T(
"navigation.rateOfTurn") && item[
"value"].IsNumber()) {
1208 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
1209 }
else if (update_path == _T(
"design.aisShipType")) {
1210 if (item[
"value"].HasMember(
"id")) {
1211 if (!pTargetData->b_isDSCtarget) {
1212 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1215 }
else if (update_path == _T(
"atonType")) {
1216 if (item[
"value"].HasMember(
"id")) {
1217 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1219 }
else if (update_path == _T(
"virtual")) {
1220 if (item[
"value"].GetBool()) {
1221 pTargetData->NavStatus = ATON_VIRTUAL;
1223 pTargetData->NavStatus = ATON_REAL;
1225 }
else if (update_path == _T(
"offPosition")) {
1226 if (item[
"value"].GetBool()) {
1227 if (ATON_REAL == pTargetData->NavStatus) {
1228 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
1229 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
1230 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
1233 }
else if (update_path == _T(
"design.draft")) {
1234 if (item[
"value"].HasMember(
"maximum") && item[
"value"].IsNumber()) {
1235 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
1236 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
1238 if (item[
"value"].HasMember(
"current") && item[
"value"].IsNumber()) {
1239 double draft = item[
"value"][
"current"].GetDouble();
1241 pTargetData->Draft = draft;
1242 pTargetData->Euro_Draft = draft;
1245 }
else if (update_path == _T(
"design.length")) {
1246 if (pTargetData->DimB == 0) {
1247 if (item[
"value"].HasMember(
"overall")) {
1248 if (item[
"value"][
"overall"].IsNumber()) {
1249 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
1250 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
1252 pTargetData->DimB = 0;
1255 }
else if (update_path == _T(
"sensors.ais.class")) {
1256 wxString aisclass = item[
"value"].GetString();
1257 if (aisclass == _T(
"A")) {
1258 if(!pTargetData->b_isDSCtarget)
1259 pTargetData->Class = AIS_CLASS_A;
1260 }
else if (aisclass == _T(
"B")) {
1261 if (!pTargetData->b_isDSCtarget)
1262 pTargetData->Class = AIS_CLASS_B;
1263 pTargetData->NavStatus =
1265 }
else if (aisclass == _T(
"BASE")) {
1266 pTargetData->Class = AIS_BASE;
1267 }
else if (aisclass == _T(
"ATON")) {
1268 pTargetData->Class = AIS_ATON;
1270 }
else if (update_path == _T(
"sensors.ais.fromBow")) {
1271 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
1272 int length = pTargetData->DimA;
1273 if (item[
"value"].IsNumber()) {
1274 pTargetData->DimA = item[
"value"].GetDouble();
1275 pTargetData->DimB = length - item[
"value"].GetDouble();
1278 }
else if (update_path == _T(
"design.beam")) {
1279 if (pTargetData->DimD == 0) {
1280 if (item[
"value"].IsNumber()) {
1281 pTargetData->Euro_Beam = item[
"value"].GetDouble();
1282 pTargetData->DimC = item[
"value"].GetDouble();
1284 pTargetData->DimD = 0;
1286 }
else if (update_path == _T(
"sensors.ais.fromCenter")) {
1287 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
1288 int beam = pTargetData->DimC;
1289 int center = beam / 2;
1290 if (item[
"value"].IsNumber()) {
1292 pTargetData->DimC = center + item[
"value"].GetDouble();
1293 pTargetData->DimD = beam - pTargetData->DimC;
1296 }
else if (update_path == _T(
"navigation.state")) {
1297 wxString state = item[
"value"].GetString();
1298 if (state == _T(
"motoring")) {
1299 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
1300 }
else if (state == _T(
"anchored")) {
1301 pTargetData->NavStatus = AT_ANCHOR;
1302 }
else if (state == _T(
"not under command")) {
1303 pTargetData->NavStatus = NOT_UNDER_COMMAND;
1304 }
else if (state == _T(
"restricted manouverability")) {
1305 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
1306 }
else if (state == _T(
"constrained by draft")) {
1307 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
1308 }
else if (state == _T(
"moored")) {
1309 pTargetData->NavStatus = MOORED;
1310 }
else if (state == _T(
"aground")) {
1311 pTargetData->NavStatus = AGROUND;
1312 }
else if (state == _T(
"fishing")) {
1313 pTargetData->NavStatus = FISHING;
1314 }
else if (state == _T(
"sailing")) {
1315 pTargetData->NavStatus = UNDERWAY_SAILING;
1316 }
else if (state == _T(
"hazardous material high speed")) {
1317 pTargetData->NavStatus = HSC;
1318 }
else if (state == _T(
"hazardous material wing in ground")) {
1319 pTargetData->NavStatus = WIG;
1320 }
else if (state == _T(
"ais-sart")) {
1321 pTargetData->NavStatus = RESERVED_14;
1323 pTargetData->NavStatus = UNDEFINED;
1325 }
else if (update_path == _T(
"navigation.destination.commonName")) {
1326 const wxString &destination = item[
"value"].GetString();
1327 pTargetData->Destination[0] =
'\0';
1328 strncpy(pTargetData->Destination, destination.c_str(),
1329 DESTINATION_LEN - 1);
1330 }
else if (update_path == _T(
"navigation.specialManeuver")) {
1331 if (strcmp(
"not available", item[
"value"].GetString()) != 0 && pTargetData->IMO < 1) {
1332 const wxString &bluesign = item[
"value"].GetString();
1333 if (_T(
"not engaged") == bluesign) {
1334 pTargetData->blue_paddle = 1;
1336 if (_T(
"engaged") == bluesign) {
1337 pTargetData->blue_paddle = 2;
1339 pTargetData->b_blue_paddle =
1340 pTargetData->blue_paddle == 2 ? true :
false;
1342 }
else if (update_path == _T(
"sensors.ais.designatedAreaCode")) {
1343 if (item[
"value"].GetInt() == 200) {
1344 pTargetData->b_hasInlandDac =
true;
1346 }
else if (update_path == _T(
"sensors.ais.functionalId")) {
1347 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
1349 pTargetData->b_isEuroInland =
true;
1353 }
else if (update_path ==
"environment.date") {
1354 wxString issued = item[
"value"].GetString();
1358 ParseGPXDateTime(tz, issued);
1359 pTargetData->met_data.day = tz.GetDay();
1360 pTargetData->met_data.hour = tz.GetHour();
1361 pTargetData->met_data.minute = tz.GetMinute();
1363 }
else if (update_path ==
"environment.wind.averageSpeed" &&
1364 item[
"value"].IsNumber()) {
1365 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
1366 }
else if (update_path ==
"environment.wind.gust" &&
1367 item[
"value"].IsNumber()) {
1368 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
1369 }
else if (update_path ==
"environment.wind.directionTrue" &&
1370 item[
"value"].IsNumber()) {
1371 pTargetData->met_data.wind_dir = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1372 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
1373 item[
"value"].IsNumber()) {
1374 pTargetData->met_data.wind_gust_dir = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1375 }
else if (update_path ==
"environment.outside.temperature" &&
1376 item[
"value"].IsNumber()) {
1377 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
1378 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
1379 item[
"value"].IsNumber()) {
1380 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
1381 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
1382 item[
"value"].IsNumber()) {
1383 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
1384 }
else if (update_path ==
"environment.outside.pressure" &&
1385 item[
"value"].IsNumber()) {
1386 pTargetData->met_data.airpress =
static_cast<int>(item[
"value"].GetDouble() / 100);
1387 }
else if (update_path ==
"environment.water.level" &&
1388 item[
"value"].IsNumber()) {
1389 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
1390 }
else if (update_path ==
"environment.water.current.drift" &&
1391 item[
"value"].IsNumber()) {
1392 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
1393 }
else if (update_path ==
"environment.water.current.set" &&
1394 item[
"value"].IsNumber()) {
1395 pTargetData->met_data.curr_dir = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1396 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
1397 item[
"value"].IsNumber()) {
1398 pTargetData->met_data.water_lev_trend =
static_cast<int>(item[
"value"].GetDouble());
1399 }
else if (update_path ==
"environment.water.levelTendency") {
1401 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
1402 item[
"value"].IsNumber()) {
1403 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
1404 }
else if (update_path ==
"environment.water.waves.period" &&
1405 item[
"value"].IsNumber()) {
1406 pTargetData->met_data.wave_period =
static_cast<int>(item[
"value"].GetDouble());
1407 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
1408 item[
"value"].IsNumber()) {
1409 pTargetData->met_data.wave_dir = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1410 }
else if (update_path ==
"environment.water.swell.height" &&
1411 item[
"value"].IsNumber()) {
1412 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
1413 }
else if (update_path ==
"environment.water.swell.period" &&
1414 item[
"value"].IsNumber()) {
1415 pTargetData->met_data.swell_per =
static_cast<int>(item[
"value"].GetDouble());
1416 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
1417 item[
"value"].IsNumber()) {
1418 pTargetData->met_data.swell_dir = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1419 }
else if (update_path ==
"environment.water.temperature" &&
1420 item[
"value"].IsNumber()) {
1421 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
1422 }
else if (update_path ==
"environment.water.salinity" &&
1423 item[
"value"].IsNumber()) {
1424 pTargetData->met_data.salinity = item[
"value"].GetDouble();
1425 }
else if (update_path ==
"environment.water.ice"){
1427 }
else if (update_path ==
"environment.water.iceValue" &&
1428 item[
"value"].IsNumber()) {
1429 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
1430 }
else if (update_path ==
"environment.water.seaStateValue" &&
1431 item[
"value"].IsNumber()) {
1432 pTargetData->met_data.seastate =
static_cast<int>(item[
"value"].GetDouble());
1433 }
else if (update_path ==
"environment.water.seaState") {
1435 }
else if (update_path ==
"environment.outside.precipitation") {
1437 }
else if (update_path ==
"environment.outside.precipitationValue" &&
1438 item[
"value"].IsNumber()) {
1439 pTargetData->met_data.precipitation =
static_cast<int>(item[
"value"].GetDouble());
1440 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
1441 item[
"value"].IsNumber()) {
1442 pTargetData->met_data.airpress_tend =
static_cast<int>(item[
"value"].GetDouble());
1443 }
else if (update_path ==
"environment.outside.pressureTendency") {
1445 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
1446 item[
"value"].IsNumber()) {
1447 pTargetData->met_data.hor_vis = GEODESIC_METERS2NM(item[
"value"].GetDouble());
1448 }
else if (update_path ==
"environment.outside.horizontalVisibility.overRange") {
1449 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
1450 }
else if (update_path == _T(
"")) {
1451 if (item[
"value"].HasMember(
"name")) {
1452 const wxString &name = item[
"value"][
"name"].GetString();
1453 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
1454 pTargetData->b_nameValid =
true;
1455 pTargetData->MID = 123;
1456 }
else if (item[
"value"].HasMember(
"registrations")) {
1457 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
1458 pTargetData->IMO = wxAtoi(imo.Right(7));
1459 }
else if (item[
"value"].HasMember(
"communication")) {
1460 const wxString &callsign =
1461 item[
"value"][
"communication"][
"callsignVhf"].GetString();
1462 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
1464 if (item[
"value"].HasMember(
"mmsi") &&
1465 1994 != (pTargetData->MMSI) / 100000) {
1467 wxString tmp = item[
"value"][
"mmsi"].GetString();
1468 if (tmp.ToLong(&mmsi)) {
1469 pTargetData->MMSI = mmsi;
1471 if (97 == mmsi / 10000000) {
1472 pTargetData->Class = AIS_SART;
1474 if (111 == mmsi / 1000000) {
1475 pTargetData->b_SarAircraftPosnReport =
true;
1478 AISshipNameCache(pTargetData.get(), AISTargetNamesC,
1479 AISTargetNamesNC, mmsi);
1483 wxLogMessage(wxString::Format(
1484 _T(
"** AisDecoder::updateItem: unhandled path %s"), update_path));
1486 rapidjson::StringBuffer buffer;
1487 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1488 item.Accept(writer);
1489 wxString msg(_T(
"update: "));
1490 msg.append(buffer.GetString());
1500 AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
1502 wxString *accumulator) {
1504 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
1506 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
1508 if (!pos)
return AIS_GENERIC_ERROR;
1510 if (!accumulator)
return AIS_GENERIC_ERROR;
1513 if (!str.Mid(1, 5).IsSameAs(_T(
"AIVDO")))
return AIS_GENERIC_ERROR;
1516 wxStringTokenizer tkz(str, _T(
","));
1519 token = tkz.GetNextToken();
1521 token = tkz.GetNextToken();
1522 int nsentences = atoi(token.mb_str());
1524 token = tkz.GetNextToken();
1525 int isentence = atoi(token.mb_str());
1527 token = tkz.GetNextToken();
1528 token = tkz.GetNextToken();
1530 wxString string_to_parse;
1531 string_to_parse.Clear();
1544 if ((1 == nsentences) && (1 == isentence)) {
1545 string_to_parse = tkz.GetNextToken();
1548 else if (nsentences > 1) {
1549 if (1 == isentence) {
1550 *accumulator = tkz.GetNextToken();
1554 accumulator->Append(tkz.GetNextToken());
1557 if (isentence == nsentences) {
1558 string_to_parse = *accumulator;
1562 if (string_to_parse.IsEmpty() &&
1564 return AIS_INCOMPLETE_MULTIPART;
1573 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1575 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
1577 if (bdecode_result) {
1578 switch (TargetData->MID) {
1583 if (!TargetData->b_positionDoubtful) {
1584 pos->kLat = TargetData->Lat;
1585 pos->kLon = TargetData->Lon;
1591 if (TargetData->COG == 360.0)
1594 pos->kCog = TargetData->COG;
1596 if (TargetData->SOG > 102.2)
1599 pos->kSog = TargetData->SOG;
1601 if ((
int)TargetData->HDG == 511)
1604 pos->kHdt = TargetData->HDG;
1612 return AIS_GENERIC_ERROR;
1617 return AIS_GENERIC_ERROR;
1625 AisError AisDecoder::DecodeN0183(
const wxString &str) {
1626 AisError ret = AIS_GENERIC_ERROR;
1627 wxString string_to_parse;
1629 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
1630 double gpsg_cog, gpsg_sog, gpsg_utc_time;
1631 int gpsg_utc_hour = 0;
1632 int gpsg_utc_min = 0;
1633 int gpsg_utc_sec = 0;
1634 char gpsg_name_str[21];
1637 bool bdecode_result =
false;
1644 long arpa_tgt_num = 0;
1645 double arpa_sog = 0.;
1646 double arpa_cog = 0.;
1647 double arpa_lat = 0.;
1648 double arpa_lon = 0.;
1649 double arpa_dist = 0.;
1650 double arpa_brg = 0.;
1651 wxString arpa_brgunit;
1652 wxString arpa_status;
1653 wxString arpa_distunit;
1654 wxString arpa_cogunit;
1655 wxString arpa_reftarget;
1656 double arpa_mins, arpa_degs;
1657 double arpa_utc_time;
1658 int arpa_utc_hour = 0;
1659 int arpa_utc_min = 0;
1660 int arpa_utc_sec = 0;
1661 char arpa_name_str[21];
1662 bool arpa_lost =
true;
1663 bool arpa_nottracked =
false;
1665 double aprs_lat = 0.;
1666 double aprs_lon = 0.;
1667 char aprs_name_str[21];
1668 double aprs_mins, aprs_degs;
1670 std::shared_ptr<AisTargetData> pTargetData = 0;
1671 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1672 bool bnewtarget =
false;
1673 int last_report_ticks;
1677 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
1679 if (!NMEACheckSumOK(str)) {
1680 return AIS_NMEAVDX_CHECKSUM_BAD;
1682 if (str.Mid(1, 2).IsSameAs(_T(
"CD"))) {
1685 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
1689 wxString string(str);
1690 wxStringTokenizer tkz(
string, _T(
",*"));
1693 token = tkz.GetNextToken();
1694 token = tkz.GetNextToken();
1695 token.ToLong(&arpa_tgt_num);
1696 token = tkz.GetNextToken();
1697 token.ToDouble(&arpa_dist);
1698 token = tkz.GetNextToken();
1699 token.ToDouble(&arpa_brg);
1700 arpa_brgunit = tkz.GetNextToken();
1701 if (arpa_brgunit == _T(
"R")) {
1702 if (std::isnan(arpa_ref_hdg)) {
1703 if (!std::isnan(gHdt))
1708 arpa_brg += arpa_ref_hdg;
1709 if (arpa_brg >= 360.) arpa_brg -= 360.;
1711 token = tkz.GetNextToken();
1712 token.ToDouble(&arpa_sog);
1713 token = tkz.GetNextToken();
1714 token.ToDouble(&arpa_cog);
1715 arpa_cogunit = tkz.GetNextToken();
1716 if (arpa_cogunit == _T(
"R")) {
1717 if (std::isnan(arpa_ref_hdg)) {
1718 if (!std::isnan(gHdt))
1723 arpa_cog += arpa_ref_hdg;
1724 if (arpa_cog >= 360.) arpa_cog -= 360.;
1726 token = tkz.GetNextToken();
1727 token = tkz.GetNextToken();
1729 arpa_distunit = tkz.GetNextToken();
1730 token = tkz.GetNextToken();
1731 if (token == wxEmptyString)
1732 token = wxString::Format(_T(
"ARPA %ld"), arpa_tgt_num);
1733 int len = wxMin(token.Length(), 20);
1734 strncpy(arpa_name_str, token.mb_str(), len);
1735 arpa_name_str[len] = 0;
1736 arpa_status = tkz.GetNextToken();
1737 if (arpa_status != _T(
"L" )) {
1739 }
else if (arpa_status != wxEmptyString)
1740 arpa_nottracked =
true;
1741 arpa_reftarget = tkz.GetNextToken();
1742 if (tkz.HasMoreTokens()) {
1743 token = tkz.GetNextToken();
1744 token.ToDouble(&arpa_utc_time);
1745 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1746 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1748 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1750 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
1751 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
1752 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
1755 if (arpa_distunit == _T(
"K")) {
1756 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_KM, g_iDistanceFormat);
1757 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_KMH, g_iSpeedFormat);
1758 }
else if (arpa_distunit == _T(
"S")) {
1759 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_MI, g_iDistanceFormat);
1760 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_MPH, g_iSpeedFormat);
1763 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
1767 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
1770 wxString aprs_tll_str;
1771 wxString string(str);
1772 wxStringTokenizer tkz(
string, _T(
",*"));
1775 aprs_tll_str = tkz.GetNextToken();
1776 token = tkz.GetNextToken();
1777 token.ToLong(&arpa_tgt_num);
1778 token = tkz.GetNextToken();
1779 token.ToDouble(&arpa_lat);
1780 arpa_degs = (int)(arpa_lat / 100.0);
1781 arpa_mins = arpa_lat - arpa_degs * 100.0;
1782 arpa_lat = arpa_degs + arpa_mins / 60.0;
1783 token = tkz.GetNextToken();
1784 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1785 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1786 arpa_lat = 0. - arpa_lat;
1787 token = tkz.GetNextToken();
1788 token.ToDouble(&arpa_lon);
1789 arpa_degs = (int)(arpa_lon / 100.0);
1790 arpa_mins = arpa_lon - arpa_degs * 100.0;
1791 arpa_lon = arpa_degs + arpa_mins / 60.0;
1792 token = tkz.GetNextToken();
1793 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1794 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1795 arpa_lon = 0. - arpa_lon;
1796 token = tkz.GetNextToken();
1797 if (token == wxEmptyString)
1798 token = wxString::Format(_T(
"ARPA %d"), arpa_tgt_num);
1799 int len = wxMin(token.Length(), 20);
1800 strncpy(arpa_name_str, token.mb_str(), len);
1801 arpa_name_str[len] = 0;
1802 token = tkz.GetNextToken();
1803 token.ToDouble(&arpa_utc_time);
1804 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1805 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1807 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1808 arpa_status = tkz.GetNextToken();
1811 if (arpa_status != _T(
"L"))
1813 else if (arpa_status != wxEmptyString)
1814 arpa_nottracked =
true;
1815 arpa_reftarget = tkz.GetNextToken();
1822 }
else if (str.Mid(3, 3).IsSameAs(_T(
"OSD"))) {
1824 wxString string(str);
1825 wxStringTokenizer tkz(
string, _T(
",*"));
1828 token = tkz.GetNextToken();
1829 token = tkz.GetNextToken();
1830 token.ToDouble(&arpa_ref_hdg);
1840 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(_T(
"WPL"))) {
1842 wxString string(str);
1843 wxStringTokenizer tkz(
string, _T(
",*"));
1846 token = tkz.GetNextToken();
1847 token = tkz.GetNextToken();
1848 token.ToDouble(&aprs_lat);
1849 aprs_degs = (int)(aprs_lat / 100.0);
1850 aprs_mins = aprs_lat - aprs_degs * 100.0;
1851 aprs_lat = aprs_degs + aprs_mins / 60.0;
1852 token = tkz.GetNextToken();
1853 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1854 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1855 aprs_lat = 0. - aprs_lat;
1856 token = tkz.GetNextToken();
1857 token.ToDouble(&aprs_lon);
1858 aprs_degs = (int)(aprs_lon / 100.0);
1859 aprs_mins = aprs_lon - aprs_degs * 100.0;
1860 aprs_lon = aprs_degs + aprs_mins / 60.0;
1861 token = tkz.GetNextToken();
1862 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1863 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1864 aprs_lon = 0. - aprs_lon;
1865 token = tkz.GetNextToken();
1866 int len = wxMin(token.Length(), 20);
1867 strncpy(aprs_name_str, token.mb_str(), len + 1);
1868 if (0 == g_WplAction) {
1870 aprs_name_str[len] = 0;
1871 for (i = 0; i < len; i++) {
1873 hash += (int)(aprs_name_str[i]);
1874 while (hash >= 100000) hash = hash / 100000;
1876 mmsi = aprs_mmsi = 199300000 + hash;
1880 }
else if (1 == g_WplAction) {
1882 aprs_name_str, wxEmptyString,
false);
1883 pWP->m_bIsolatedMark =
true;
1884 InsertWpt(pWP,
true);
1886 }
else if (str.Mid(1, 5).IsSameAs(_T(
"FRPOS"))) {
1890 wxString string(str);
1891 wxStringTokenizer tkz(
string, _T(
",*"));
1894 token = tkz.GetNextToken();
1896 token = tkz.GetNextToken();
1897 token.ToDouble(&gpsg_lat);
1898 gpsg_degs = (int)(gpsg_lat / 100.0);
1899 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
1900 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
1902 token = tkz.GetNextToken();
1903 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1904 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1905 gpsg_lat = 0. - gpsg_lat;
1907 token = tkz.GetNextToken();
1908 token.ToDouble(&gpsg_lon);
1909 gpsg_degs = (int)(gpsg_lon / 100.0);
1910 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
1911 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
1913 token = tkz.GetNextToken();
1914 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1915 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1916 gpsg_lon = 0. - gpsg_lon;
1918 token = tkz.GetNextToken();
1921 token = tkz.GetNextToken();
1922 token.ToDouble(&gpsg_sog);
1924 token = tkz.GetNextToken();
1925 token.ToDouble(&gpsg_cog);
1927 token = tkz.GetNextToken();
1930 token = tkz.GetNextToken();
1931 token.ToDouble(&gpsg_utc_time);
1932 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
1933 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
1935 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
1939 token = tkz.GetNextToken();
1940 int i, len, hash = 0;
1941 len = wxMin(wxStrlen(token), 20);
1942 strncpy(gpsg_name_str, token.mb_str(), len);
1943 gpsg_name_str[len] = 0;
1944 for (i = 0; i < len; i++) {
1946 hash += (int)(token[i]);
1947 while (hash >= 100000) hash = hash / 100000;
1950 gpsg_mmsi = 199000000 + hash;
1952 }
else if (!str.Mid(3, 2).IsSameAs(_T(
"VD"))) {
1953 return AIS_NMEAVDX_BAD;
1959 wxString string(str);
1960 wxStringTokenizer tkz(
string, _T(
","));
1963 token = tkz.GetNextToken();
1965 token = tkz.GetNextToken();
1966 nsentences = atoi(token.mb_str());
1968 token = tkz.GetNextToken();
1969 isentence = atoi(token.mb_str());
1971 token = tkz.GetNextToken();
1972 long lsequence_id = 0;
1973 token.ToLong(&lsequence_id);
1975 token = tkz.GetNextToken();
1977 token.ToLong(&lchannel);
1980 string_to_parse.Clear();
1984 if ((1 == nsentences) && (1 == isentence)) {
1985 string_to_parse = tkz.GetNextToken();
1988 else if (nsentences > 1) {
1989 if (1 == isentence) {
1990 sentence_accumulator = tkz.GetNextToken();
1994 sentence_accumulator += tkz.GetNextToken();
1997 if (isentence == nsentences) {
1998 string_to_parse = sentence_accumulator;
2002 if (mmsi || (!string_to_parse.IsEmpty() &&
2003 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
2005 wxCharBuffer abuf = string_to_parse.ToUTF8();
2007 return AIS_GENERIC_ERROR;
2012 if (!mmsi) mmsi = strbit.GetInt(9, 30);
2013 long mmsi_long = mmsi;
2016 int origin_mmsi = 0;
2017 int messID = strbit.GetInt(1, 6);
2018 int dac = strbit.GetInt(41, 10);
2019 int fi = strbit.GetInt(51, 6);
2021 int met_lon, met_lat;
2022 if (dac == 001 && fi == 31) {
2024 met_lon = strbit.GetInt(57, 25);
2025 met_lat = strbit.GetInt(82, 24);
2026 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
2029 }
else if (dac == 367 && fi == 33) {
2031 const int size = strbit.GetBitCount();
2032 if (size < 168)
return AIS_GENERIC_ERROR;
2033 const int startb = 56;
2034 const int slot_size = 112;
2035 const int extra_bits = (size - startb) % slot_size;
2036 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
2038 int mes_type = strbit.GetInt(57, 4);
2039 int site_ID = strbit.GetInt(77, 7);
2040 if (mes_type == 0) {
2042 met_lon = strbit.GetInt(90, 28);
2043 met_lat = strbit.GetInt(118, 27);
2044 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
2049 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
2055 return AIS_GENERIC_ERROR;
2062 auto it = AISTargetList.find(mmsi);
2063 if (it == AISTargetList.end())
2065 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2070 pTargetData->MMSI = mmsi;
2071 pTargetData->met_data.original_mmsi = origin_mmsi;
2074 pTargetData = it->second;
2076 if(!bnewtarget) pStaleTarget = pTargetData;
2078 pTargetData->MMSI = mmsi;
2079 pTargetData->met_data.original_mmsi = origin_mmsi;
2082 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2084 if (mmsi == props->MMSI) {
2086 if (TRACKTYPE_NEVER == props->TrackType) {
2087 pTargetData->b_show_track =
false;
2089 else if (TRACKTYPE_ALWAYS == props->TrackType) {
2090 pTargetData->b_show_track =
true;
2095 if (props->m_bignore)
return AIS_NoError;
2098 else if (props->m_bVDM) {
2100 if (str.Mid(3, 9).IsSameAs(wxT(
"VDM,1,1,,"))) {
2101 int message_ID = strbit.GetInt(1, 6);
2104 if ((message_ID <= 3) || (message_ID == 18)) {
2106 pTargetData->b_OwnShip =
true;
2108 wxString aivdostr = str;
2109 aivdostr.replace(1, 5,
"AIVDO");
2110 unsigned char calculated_checksum = 0;
2111 wxString::iterator i;
2112 for (i = aivdostr.begin() + 1; i != aivdostr.end() && *i !=
'*';
2114 calculated_checksum ^=
static_cast<unsigned char>(*i);
2117 if (i <= aivdostr.end() - 3)
2120 wxString::Format(_(
"%02X"), calculated_checksum));
2122 gps_watchdog_timeout_ticks =
2125 std::string full_sentence = aivdostr.ToStdString();
2126 std::string identifier(
"AIVDO");
2130 auto address = std::make_shared<NavAddr0183>(
"virtual");
2132 std::make_shared<const Nmea0183Msg>(identifier, full_sentence, address);
2133 auto msg_all = std::make_shared<const Nmea0183Msg>(*msg,
"ALL");
2135 auto &msgbus = NavMsgBus::GetInstance();
2137 msgbus.Notify(std::move(msg));
2138 msgbus.Notify(std::move(msg_all));
2148 wxDateTime now = wxDateTime::Now();
2152 last_report_ticks = pStaleTarget->PositionReportTicks;
2154 last_report_ticks = now.GetTicks();
2158 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2162 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2163 pTargetData->PositionReportTicks = now.GetTicks();
2164 pTargetData->StaticReportTicks = now.GetTicks();
2165 pTargetData->m_utc_hour = gpsg_utc_hour;
2166 pTargetData->m_utc_min = gpsg_utc_min;
2167 pTargetData->m_utc_sec = gpsg_utc_sec;
2168 pTargetData->m_date_string = gpsg_date;
2169 pTargetData->MMSI = gpsg_mmsi;
2170 pTargetData->NavStatus = 0;
2171 pTargetData->Lat = gpsg_lat;
2172 pTargetData->Lon = gpsg_lon;
2173 pTargetData->b_positionOnceValid =
true;
2174 pTargetData->COG = gpsg_cog;
2175 pTargetData->SOG = gpsg_sog;
2176 pTargetData->ShipType = 52;
2177 pTargetData->Class = AIS_GPSG_BUDDY;
2178 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
2179 pTargetData->b_nameValid =
true;
2180 pTargetData->b_active =
true;
2181 pTargetData->b_lost =
false;
2183 bdecode_result =
true;
2184 }
else if (arpa_mmsi) {
2185 pTargetData->m_utc_hour = arpa_utc_hour;
2186 pTargetData->m_utc_min = arpa_utc_min;
2187 pTargetData->m_utc_sec = arpa_utc_sec;
2188 pTargetData->MMSI = arpa_mmsi;
2189 pTargetData->NavStatus = 15;
2190 if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
2193 (now.GetTicks() - pTargetData->PositionReportTicks);
2194 if (age_of_last > 0) {
2195 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
2196 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
2197 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2200 pTargetData->Lat = arpa_lat;
2201 pTargetData->Lon = arpa_lon;
2202 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
2203 if (arpa_dist != 0.)
2204 ll_gc_ll(gLat, gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
2208 pTargetData->COG = arpa_cog;
2209 pTargetData->SOG = arpa_sog;
2211 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2212 pTargetData->PositionReportTicks = now.GetTicks();
2213 pTargetData->StaticReportTicks = now.GetTicks();
2214 pTargetData->b_positionOnceValid =
true;
2215 pTargetData->ShipType = 55;
2216 pTargetData->Class = AIS_ARPA;
2218 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
2219 if (arpa_status != _T(
"Q"))
2220 pTargetData->b_nameValid =
true;
2222 pTargetData->b_nameValid =
false;
2223 pTargetData->b_active = !arpa_lost;
2224 pTargetData->b_lost = arpa_nottracked;
2226 bdecode_result =
true;
2227 }
else if (aprs_mmsi) {
2228 pTargetData->m_utc_hour = now.GetHour();
2229 pTargetData->m_utc_min = now.GetMinute();
2230 pTargetData->m_utc_sec = now.GetSecond();
2231 pTargetData->MMSI = aprs_mmsi;
2232 pTargetData->NavStatus = 15;
2234 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
2235 if (age_of_last > 0) {
2236 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
2237 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
2238 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2241 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2242 pTargetData->PositionReportTicks = now.GetTicks();
2243 pTargetData->StaticReportTicks = now.GetTicks();
2244 pTargetData->Lat = aprs_lat;
2245 pTargetData->Lon = aprs_lon;
2246 pTargetData->b_positionOnceValid =
true;
2247 pTargetData->ShipType = 56;
2248 pTargetData->Class = AIS_APRS;
2249 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
2250 pTargetData->b_nameValid =
true;
2251 pTargetData->b_active =
true;
2252 pTargetData->b_lost =
false;
2254 bdecode_result =
true;
2258 Parse_VDXBitstring(&strbit, pTargetData);
2262 getMmsiProperties(pTargetData);
2265 pTargetData->RecentPeriod =
2266 pTargetData->PositionReportTicks - last_report_ticks;
2276 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
2281 if ((n_msgs % 10000) == 0)
2282 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
2283 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
2289 void AisDecoder::CommitAISTarget(std::shared_ptr<AisTargetData> pTargetData,
2290 const wxString &str,
2294 m_pLatestTargetData = pTargetData;
2296 if (!str.IsEmpty()) {
2297 if (str.Mid(3, 3).IsSameAs(_T(
"VDO")))
2298 pTargetData->b_OwnShip =
true;
2300 pTargetData->b_OwnShip =
false;
2303 if (!pTargetData->b_OwnShip) {
2305 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
2307 pTargetData->b_PersistTrack =
false;
2309 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2310 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2312 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2319 pTargetData->b_NoTrack =
false;
2324 if (message_valid) {
2326 if (pTargetData->MMSI) {
2327 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC, pTargetData->MMSI);
2329 AISTargetList[pTargetData->MMSI] =
2332 if (!pTargetData->area_notices.empty()) {
2333 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
2334 if (it == AIS_AreaNotice_Sources.end())
2335 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
2340 if (!pTargetData->b_OwnShip) {
2341 if (pTargetData->b_positionOnceValid) {
2342 long mmsi_long = pTargetData->MMSI;
2343 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2344 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2346 pSel->SetUserData(pTargetData->MMSI);
2350 UpdateOneCPA(pTargetData.get());
2353 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2366 if (!pTargetData->b_OwnShip) {
2367 if (pTargetData->b_positionOnceValid) {
2368 long mmsi_long = pTargetData->MMSI;
2369 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2370 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2372 pSel->SetUserData(pTargetData->MMSI);
2382 void AisDecoder::getAISTarget(
long mmsi, std::shared_ptr<AisTargetData> &pTargetData,
2383 std::shared_ptr<AisTargetData> &pStaleTarget,
bool &bnewtarget,
2384 int &last_report_ticks, wxDateTime &now) {
2385 now = wxDateTime::Now();
2386 auto it = AISTargetList.find(mmsi);
2387 if (it == AISTargetList.end())
2389 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2393 pTargetData = it->second;
2394 pStaleTarget = pTargetData;
2401 last_report_ticks = pStaleTarget->PositionReportTicks;
2403 last_report_ticks = now.GetTicks();
2407 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
2410 void AisDecoder::getMmsiProperties(std::shared_ptr<AisTargetData> &pTargetData) {
2411 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2412 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2414 pTargetData->b_isFollower = props->m_bFollower;
2415 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2416 if (TRACKTYPE_NEVER == props->TrackType) {
2417 pTargetData->b_show_track =
false;
2419 else if (TRACKTYPE_ALWAYS == props->TrackType) {
2420 pTargetData->b_show_track =
true;
2427 std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
bool b_take_dsc) {
2428 double dsc_lat = 0.;
2429 double dsc_lon = 0.;
2430 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
2432 double dse_lat = 0.;
2433 double dse_lon = 0.;
2434 long dsc_fmt, dsc_quadrant,dsc_cat, dsc_nature;
2437 int dsc_tx_mmsi = 0;
2439 double dse_cog = 0.;
2440 double dse_sog = 0.;
2441 wxString dse_shipName = wxEmptyString;
2446 std::shared_ptr<AisTargetData>pTargetData = NULL;
2450 wxString string(str);
2451 wxStringTokenizer tkz(
string, _T(
",*"));
2454 token = tkz.GetNextToken();
2456 if (str.Mid(3, 3).IsSameAs(_T(
"DSC"))) {
2457 m_dsc_last_string = str;
2459 token = tkz.GetNextToken();
2460 token.ToLong(&dsc_fmt);
2462 token = tkz.GetNextToken();
2464 if (dsc_fmt == 12 || dsc_fmt == 16) {
2465 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2468 token.ToDouble(&dsc_addr);
2469 dsc_mmsi = 0 - (int)( dsc_addr / 10 );
2472 token = tkz.GetNextToken();
2473 token.ToLong(&dsc_cat);
2475 token = tkz.GetNextToken();
2476 if (!token.IsSameAs(wxEmptyString)) {
2477 token.ToLong(&dsc_nature);
2479 else dsc_nature = 99;
2481 token = tkz.GetNextToken();
2483 token = tkz.GetNextToken();
2484 token.ToDouble(&dsc_tmp);
2486 token = tkz.GetNextToken();
2487 token = tkz.GetNextToken();
2488 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(wxEmptyString) ) {
2490 dsc_tx_mmsi = dsc_mmsi;
2491 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2493 token = tkz.GetNextToken();
2494 if (dsc_fmt == 16 && dsc_cat == 12) {
2495 if (!token.IsSameAs(wxEmptyString)) {
2496 token.ToLong(&dsc_nature);
2498 else dsc_nature = 99;
2500 token = tkz.GetNextToken();
2501 token = tkz.GetNextToken();
2503 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
2505 if (dsc_quadrant > 3)
2508 dsc_lat = (int)(dsc_tmp / 100000.0);
2509 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
2510 dsc_lat = dsc_lat - dsc_quadrant * 10000;
2511 dsc_degs = (int)(dsc_lat / 100.0);
2512 dsc_mins = dsc_lat - dsc_degs * 100.0;
2513 dsc_lat = dsc_degs + dsc_mins / 60.0;
2515 dsc_degs = (int)(dsc_lon / 100.0);
2516 dsc_mins = dsc_lon - dsc_degs * 100.0;
2517 dsc_lon = dsc_degs + dsc_mins / 60.0;
2518 switch (dsc_quadrant) {
2534 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
2536 }
else if (str.Mid(3, 3).IsSameAs(_T(
"DSE"))) {
2537 token = tkz.GetNextToken();
2538 token = tkz.GetNextToken();
2539 token = tkz.GetNextToken();
2540 token = tkz.GetNextToken();
2541 dse_mmsi = wxAtoi(token.Mid(0, 9));
2546 token = tkz.GetNextToken();
2549 token.ToDouble(&dse_tmp);
2550 dse_lat = (int)(dse_tmp / 10000.0);
2551 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2552 dse_lat = dse_lat / 600000.0;
2553 dse_lon = dse_lon / 600000.0;
2556 while (tkz.HasMoreTokens()) {
2557 dseSymbol = tkz.GetNextToken();
2558 token = tkz.GetNextToken();
2559 if (dseSymbol.IsSameAs(_T(
"00"))) {
2560 token.ToDouble(&dse_tmp);
2561 dse_lat = (int)(dse_tmp / 10000.0);
2562 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2563 dse_lat = dse_lat / 600000.0;
2564 dse_lon = dse_lon / 600000.0;
2566 else if (dseSymbol.IsSameAs(_T(
"01"))) {
2568 else if (dseSymbol.IsSameAs(_T(
"02"))) {
2569 token.ToDouble(&dse_tmp);
2570 dse_sog = dse_tmp / 10.0;
2572 else if (dseSymbol.IsSameAs(_T(
"03"))) {
2573 token.ToDouble(&dse_tmp);
2574 dse_cog = dse_tmp / 10.0;
2576 else if (dseSymbol.IsSameAs(_T(
"04"))) {
2577 dse_shipName = DecodeDSEExpansionCharacters(token);
2579 else if (dseSymbol.IsSameAs(_T(
"05"))) {
2581 else if (dseSymbol.IsSameAs(_T(
"06"))) {
2584 mmsi = abs((
int)dse_mmsi);
2588 wxDateTime now = wxDateTime::Now();
2590 int last_report_ticks = now.GetTicks();
2593 auto it = AISTargetList.find(mmsi);
2594 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
2595 if (it == AISTargetList.end()) {
2597 pStaleTarget = it->second;
2598 last_report_ticks = pStaleTarget->PositionReportTicks;
2604 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
2606 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
2607 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
2609 m_ptentative_dsctarget->MMSI = mmsi;
2610 m_ptentative_dsctarget->NavStatus = 99;
2611 m_ptentative_dsctarget->Lat = dsc_lat;
2612 m_ptentative_dsctarget->Lon = dsc_lon;
2613 m_ptentative_dsctarget->b_positionOnceValid =
true;
2614 m_ptentative_dsctarget->COG = 0;
2615 m_ptentative_dsctarget->SOG = 0;
2616 m_ptentative_dsctarget->ShipType = dsc_fmt;
2617 m_ptentative_dsctarget->Class = AIS_DSC;
2618 m_ptentative_dsctarget->b_isDSCtarget =
true;
2619 m_ptentative_dsctarget->b_nameValid =
true;
2620 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
2621 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %d",
2623 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
2624 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
2626 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %d",
2630 m_ptentative_dsctarget->b_active =
true;
2631 m_ptentative_dsctarget->b_lost =
false;
2632 m_ptentative_dsctarget->RecentPeriod =
2633 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2636 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
2641 if (dse_mmsi || b_take_dsc) {
2642 if (m_ptentative_dsctarget) {
2648 m_ptentative_dsctarget->Lat =
2649 m_ptentative_dsctarget->Lat +
2650 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
2651 m_ptentative_dsctarget->Lon =
2652 m_ptentative_dsctarget->Lon +
2653 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
2654 if (dse_shipName.length() > 0) {
2655 memset(m_ptentative_dsctarget->ShipName,
'\0',SHIP_NAME_LEN);
2656 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
2657 "%s", dse_shipName.ToAscii().data());
2659 m_ptentative_dsctarget->COG = dse_cog;
2660 m_ptentative_dsctarget->SOG = dse_sog;
2664 m_ptentative_dsctarget->RecentPeriod =
2665 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2670 auto it = AISTargetList.find(mmsi);
2671 if (it == AISTargetList.end()) {
2672 pTargetData = m_ptentative_dsctarget;
2674 pTargetData = it->second;
2675 std::vector<AISTargetTrackPoint> ptrack =
2676 std::move(pTargetData->m_ptrack);
2677 pTargetData->CloneFrom(
2678 m_ptentative_dsctarget.get());
2680 pTargetData->m_ptrack =
2687 m_ptentative_dsctarget = NULL;
2689 m_pLatestTargetData = pTargetData;
2691 AISTargetList[pTargetData->MMSI] =
2694 long mmsi_long = pTargetData->MMSI;
2698 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2701 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2702 (
void *)mmsi_long, SELTYPE_AISTARGET);
2703 pSel->SetUserData(pTargetData->MMSI);
2706 UpdateOneCPA(pTargetData.get());
2709 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2717 wxString AisDecoder::DecodeDSEExpansionCharacters(wxString dseData) {
2719 char lookupTable[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
' ',
2720 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
2721 'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
2722 'Y',
'Z',
'.',
',',
'-',
'/',
' ' };
2724 for (
size_t i = 0; i < dseData.length(); i += 2) {
2725 result.append(1, lookupTable[strtol(dseData.Mid(i, 2).data(), NULL, 10)]);
2733 bool AisDecoder::Parse_VDXBitstring(
AisBitstring *bstr,
2734 std::shared_ptr<AisTargetData> ptd) {
2735 bool parse_result =
false;
2736 bool b_posn_report =
false;
2738 wxDateTime now = wxDateTime::Now();
2740 int message_ID = bstr->
GetInt(1, 6);
2741 ptd->MID = message_ID;
2744 int met_mmsi = ptd->MMSI;
2747 ptd->MMSI = bstr->
GetInt(9, 30);
2749 switch (message_ID) {
2755 ptd->NavStatus = bstr->
GetInt(39, 4);
2756 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
2758 int lon = bstr->
GetInt(62, 28);
2759 if (lon & 0x08000000)
2761 double lon_tentative = lon / 600000.;
2763 int lat = bstr->
GetInt(90, 27);
2764 if (lat & 0x04000000)
2766 double lat_tentative = lat / 600000.;
2768 if ((lon_tentative <= 180.) &&
2772 ptd->Lon = lon_tentative;
2773 ptd->Lat = lat_tentative;
2774 ptd->b_positionDoubtful =
false;
2775 ptd->b_positionOnceValid =
true;
2776 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2777 ptd->PositionReportTicks = now.GetTicks();
2779 ptd->b_positionDoubtful =
true;
2782 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
2783 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
2785 ptd->ROTAIS = bstr->
GetInt(43, 8);
2786 double rot_dir = 1.0;
2788 if (ptd->ROTAIS == 128)
2790 else if ((ptd->ROTAIS & 0x80) == 0x80) {
2791 ptd->ROTAIS = ptd->ROTAIS - 256;
2795 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
2798 ptd->m_utc_sec = bstr->
GetInt(138, 6);
2800 if ((1 == message_ID) ||
2803 ptd->SyncState = bstr->
GetInt(151, 2);
2804 ptd->SlotTO = bstr->
GetInt(153, 2);
2805 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
2807 ptd->m_utc_hour = bstr->
GetInt(155, 5);
2809 ptd->m_utc_min = bstr->
GetInt(160, 7);
2811 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
2812 (ptd->m_utc_sec < 60)) {
2813 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
2814 rx_ticks = rx_time.GetTicks();
2816 first_rx_ticks = rx_ticks;
2824 ptd->blue_paddle = bstr->
GetInt(144, 2);
2825 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
2827 if (!ptd->b_isDSCtarget)
2828 ptd->Class = AIS_CLASS_A;
2831 int mmsi_start = ptd->MMSI / 10000000;
2833 if (mmsi_start == 97) {
2834 ptd->Class = AIS_SART;
2835 ptd->StaticReportTicks =
2849 parse_result =
true;
2850 b_posn_report =
true;
2859 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2861 int lon = bstr->
GetInt(58, 28);
2862 if (lon & 0x08000000)
2864 double lon_tentative = lon / 600000.;
2866 int lat = bstr->
GetInt(86, 27);
2867 if (lat & 0x04000000)
2869 double lat_tentative = lat / 600000.;
2871 if ((lon_tentative <= 180.) &&
2875 ptd->Lon = lon_tentative;
2876 ptd->Lat = lat_tentative;
2877 ptd->b_positionDoubtful =
false;
2878 ptd->b_positionOnceValid =
true;
2879 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2880 ptd->PositionReportTicks = now.GetTicks();
2882 ptd->b_positionDoubtful =
true;
2884 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2885 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2887 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2889 if (!ptd->b_isDSCtarget)
2890 ptd->Class = AIS_CLASS_B;
2892 parse_result =
true;
2893 b_posn_report =
true;
2901 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2902 int lon = bstr->
GetInt(58, 28);
2903 if (lon & 0x08000000)
2905 double lon_tentative = lon / 600000.;
2907 int lat = bstr->
GetInt(86, 27);
2908 if (lat & 0x04000000)
2910 double lat_tentative = lat / 600000.;
2912 if ((lon_tentative <= 180.) &&
2916 ptd->Lon = lon_tentative;
2917 ptd->Lat = lat_tentative;
2918 ptd->b_positionDoubtful =
false;
2919 ptd->b_positionOnceValid =
true;
2920 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
2921 ptd->PositionReportTicks = now.GetTicks();
2923 ptd->b_positionDoubtful =
true;
2925 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2926 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2927 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2929 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
2930 ptd->b_nameValid =
true;
2931 if (!ptd->b_isDSCtarget) {
2932 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
2934 ptd->DimA = bstr->
GetInt(272, 9);
2935 ptd->DimB = bstr->
GetInt(281, 9);
2936 ptd->DimC = bstr->
GetInt(290, 6);
2937 ptd->DimD = bstr->
GetInt(296, 6);
2939 if (!ptd->b_isDSCtarget)
2940 ptd->Class = AIS_CLASS_B;
2941 parse_result =
true;
2942 b_posn_report =
true;
2955 int bitCorrection = 10;
2956 int resolution = 10;
2959 double lon_tentative = 181.;
2960 double lat_tentative = 91.;
2963 printf(
"AIS Message 27 - received:\r\n");
2964 printf(
"MMSI : %i\r\n", ptd->MMSI);
2970 if (!ptd->b_isDSCtarget)
2971 ptd->Class = AIS_CLASS_A;
2973 ptd->NavStatus = bstr->
GetInt(39, 4);
2975 int lon = bstr->
GetInt(45, 18);
2976 int lat = bstr->
GetInt(63, 17);
2978 lat_tentative = lat;
2979 lon_tentative = lon;
2982 if (lat >= (0x4000000 >> bitCorrection)) {
2983 lat_tentative = (0x8000000 >> bitCorrection) - lat;
2984 lat_tentative *= -1;
2988 if (lon >= (0x8000000 >> bitCorrection)) {
2989 lon_tentative = (0x10000000 >> bitCorrection) - lon;
2990 lon_tentative *= -1;
2994 lat_tentative = lat_tentative / resolution / 60.0;
2995 lon_tentative = lon_tentative / resolution / 60.0;
2998 printf(
"Latitude : %f\r\n", lat_tentative);
2999 printf(
"Longitude : %f\r\n", lon_tentative);
3003 int positionLatency = bstr->
GetInt(95, 1);
3005 if ((lon_tentative <= 180.) &&
3009 ptd->Lon = lon_tentative;
3010 ptd->Lat = lat_tentative;
3011 ptd->b_positionDoubtful =
false;
3012 ptd->b_positionOnceValid =
true;
3013 if (positionLatency == 0) {
3016 printf(
"Low latency position report.\r\n");
3018 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3019 ptd->PositionReportTicks = now.GetTicks();
3022 ptd->b_positionDoubtful =
true;
3024 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
3025 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
3027 b_posn_report =
true;
3028 parse_result =
true;
3034 if (!ptd->b_isDSCtarget)
3035 ptd->Class = AIS_CLASS_A;
3041 int AIS_version_indicator = bstr->
GetInt(39, 2);
3042 if (AIS_version_indicator < 4) {
3043 ptd->IMO = bstr->
GetInt(41, 30);
3045 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
3046 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3047 ptd->b_nameValid =
true;
3048 if (!ptd->b_isDSCtarget) {
3049 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
3052 ptd->DimA = bstr->
GetInt(241, 9);
3053 ptd->DimB = bstr->
GetInt(250, 9);
3054 ptd->DimC = bstr->
GetInt(259, 6);
3055 ptd->DimD = bstr->
GetInt(265, 6);
3057 ptd->ETA_Mo = bstr->
GetInt(275, 4);
3058 ptd->ETA_Day = bstr->
GetInt(279, 5);
3059 ptd->ETA_Hr = bstr->
GetInt(284, 5);
3060 ptd->ETA_Min = bstr->
GetInt(289, 6);
3062 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
3064 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
3066 ptd->StaticReportTicks = now.GetTicks();
3068 parse_result =
true;
3075 int part_number = bstr->
GetInt(39, 2);
3076 if (0 == part_number) {
3077 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3078 ptd->b_nameValid =
true;
3079 parse_result =
true;
3081 }
else if (1 == part_number) {
3082 if (!ptd->b_isDSCtarget) {
3083 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
3085 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
3087 ptd->DimA = bstr->
GetInt(133, 9);
3088 ptd->DimB = bstr->
GetInt(142, 9);
3089 ptd->DimC = bstr->
GetInt(151, 6);
3090 ptd->DimD = bstr->
GetInt(157, 6);
3091 parse_result =
true;
3097 ptd->Class = AIS_BASE;
3099 ptd->m_utc_hour = bstr->
GetInt(62, 5);
3100 ptd->m_utc_min = bstr->
GetInt(67, 6);
3101 ptd->m_utc_sec = bstr->
GetInt(73, 6);
3103 int lon = bstr->
GetInt(80, 28);
3104 if (lon & 0x08000000)
3106 double lon_tentative = lon / 600000.;
3108 int lat = bstr->
GetInt(108, 27);
3109 if (lat & 0x04000000)
3111 double lat_tentative = lat / 600000.;
3113 if ((lon_tentative <= 180.) &&
3117 ptd->Lon = lon_tentative;
3118 ptd->Lat = lat_tentative;
3119 ptd->b_positionDoubtful =
false;
3120 ptd->b_positionOnceValid =
true;
3121 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3122 ptd->PositionReportTicks = now.GetTicks();
3124 ptd->b_positionDoubtful =
true;
3130 parse_result =
true;
3131 b_posn_report =
true;
3137 ptd->SOG = bstr->
GetInt(51, 10);
3139 int lon = bstr->
GetInt(62, 28);
3140 if (lon & 0x08000000)
3142 double lon_tentative = lon / 600000.;
3144 int lat = bstr->
GetInt(90, 27);
3145 if (lat & 0x04000000)
3147 double lat_tentative = lat / 600000.;
3149 if ((lon_tentative <= 180.) &&
3153 ptd->Lon = lon_tentative;
3154 ptd->Lat = lat_tentative;
3155 ptd->b_positionDoubtful =
false;
3156 ptd->b_positionOnceValid =
true;
3157 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3158 ptd->PositionReportTicks = now.GetTicks();
3160 ptd->b_positionDoubtful =
true;
3163 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
3165 int alt_tent = bstr->
GetInt(39, 12);
3166 ptd->altitude = alt_tent;
3168 ptd->b_SarAircraftPosnReport =
true;
3170 parse_result =
true;
3171 b_posn_report =
true;
3177 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
3183 ptd->DimA = bstr->
GetInt(220, 9);
3184 ptd->DimB = bstr->
GetInt(229, 9);
3185 ptd->DimC = bstr->
GetInt(238, 6);
3186 ptd->DimD = bstr->
GetInt(244, 6);
3189 ptd->m_utc_sec = bstr->
GetInt(254, 6);
3191 int offpos = bstr->
GetInt(260, 1);
3192 int virt = bstr->
GetInt(270, 1);
3195 ptd->NavStatus = ATON_VIRTUAL;
3197 ptd->NavStatus = ATON_REAL;
3198 if (ptd->m_utc_sec <= 59 ) {
3199 ptd->NavStatus += 1;
3200 if (offpos) ptd->NavStatus += 1;
3203 bstr->GetStr( 44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3206 if (bstr->GetBitCount() > 276) {
3207 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
3208 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
3209 ptd->ShipNameExtension[14] = 0;
3211 ptd->ShipNameExtension[0] = 0;
3214 ptd->b_nameValid =
true;
3216 parse_result =
true;
3218 ptd->Class = AIS_ATON;
3220 int lon = bstr->
GetInt(165, 28);
3222 if (lon & 0x08000000)
3224 double lon_tentative = lon / 600000.;
3226 int lat = bstr->
GetInt(193, 27);
3228 if (lat & 0x04000000)
3230 double lat_tentative = lat / 600000.;
3232 if ((lon_tentative <= 180.) &&
3236 ptd->Lon = lon_tentative;
3237 ptd->Lat = lat_tentative;
3238 ptd->b_positionDoubtful =
false;
3239 ptd->b_positionOnceValid =
true;
3240 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3241 ptd->PositionReportTicks = now.GetTicks();
3243 ptd->b_positionDoubtful =
true;
3245 b_posn_report =
true;
3250 int dac = bstr->
GetInt(41, 10);
3251 int fi = bstr->
GetInt(51, 6);
3257 ptd->b_isEuroInland =
true;
3259 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
3260 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
3261 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
3262 ptd->UN_shiptype = bstr->
GetInt(128, 14);
3263 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
3264 parse_result =
true;
3267 if (dac == 1 || dac == 366)
3271 if (bstr->GetBitCount() >= 111) {
3273 an.link_id = bstr->
GetInt(57, 10);
3274 an.notice_type = bstr->
GetInt(67, 7);
3275 an.month = bstr->
GetInt(74, 4);
3276 an.day = bstr->
GetInt(78, 5);
3277 an.hour = bstr->
GetInt(83, 5);
3278 an.minute = bstr->
GetInt(88, 6);
3279 an.duration_minutes = bstr->
GetInt(94, 18);
3281 wxDateTime now = wxDateTime::Now();
3284 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3285 now.GetYear(), an.hour, an.minute);
3290 if (an.start_time > now + wxTimeSpan::Hours(48))
3291 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3292 now.GetYear() - 1, an.hour, an.minute);
3295 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3300 if (an.expiry_time < now - wxTimeSpan::Hours(24)) {
3301 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3302 now.GetYear() + 1, an.hour, an.minute);
3304 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3308 int subarea_len = 87;
3311 float pos_scale = 60000.0;
3317 pos_scale = 600000.0;
3321 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
3322 for (
int i = 0; i < subarea_count; ++i) {
3323 int base = 111 + i * subarea_len;
3325 sa.shape = bstr->
GetInt(base + 1, 3);
3326 int scale_factor = 1;
3327 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
3330 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
3331 sa.text = wxString(t, wxConvUTF8);
3333 int scale_multipliers[4] = {1, 10, 100, 1000};
3334 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
3336 case AIS8_001_22_SHAPE_SECTOR:
3337 sa.left_bound_deg = bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size + 12, 9);
3338 sa.right_bound_deg = bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
3339 case AIS8_001_22_SHAPE_CIRCLE:
3340 sa.radius_m = bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) * scale_factor;
3342 case AIS8_001_22_SHAPE_RECT:
3343 sa.longitude = bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
3344 sa.latitude = bstr->
GetInt(base + 6 + lon_len, lat_len,
true) / pos_scale;
3345 sa.e_dim_m = bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size, 8) * scale_factor;
3346 sa.n_dim_m = bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size + 8, 8) * scale_factor;
3347 sa.orient_deg = bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
3349 case AIS8_001_22_SHAPE_POLYLINE:
3350 case AIS8_001_22_SHAPE_POLYGON:
3351 for (
int i = 0; i < 4; ++i) {
3352 sa.angles[i] = bstr->
GetInt(base + 6 + i * 20, 10) * 0.5;
3354 bstr->
GetInt(base + 16 + i * 20, 10) * scale_factor;
3358 an.sub_areas.push_back(sa);
3360 ptd->area_notices[an.link_id] = an;
3361 parse_result =
true;
3367 if (bstr->GetBitCount() >= 360) {
3369 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
3372 double lon_tentative = 181.;
3373 double lat_tentative = 91.;
3375 int lon = bstr->
GetInt(57, 25);
3376 int lat = bstr->
GetInt(82, 24);
3378 if (lon & 0x01000000)
3380 lon_tentative = lon / 60000.;
3382 if (lat & 0x00800000)
3384 lat_tentative = lat / 60000.;
3386 ptd->Lon = lon_tentative;
3387 ptd->Lat = lat_tentative;
3390 wxString x = ptd->ShipName;
3391 if (x.Find(
"METEO") == wxNOT_FOUND) {
3393 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
3394 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
3395 slat.ToDouble(&id1);
3396 slon.ToDouble(&id2);
3397 wxString nameID =
"METEO ";
3398 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
3399 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
3402 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
3403 ptd->met_data.day = bstr->
GetInt(107, 5);
3404 ptd->met_data.hour = bstr->
GetInt(112, 5);
3405 ptd->met_data.minute = bstr->
GetInt(117, 6);
3406 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
3407 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
3408 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
3409 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
3411 int tmp = bstr->
GetInt(155, 11);
3412 if (tmp & 0x00000400)
3414 ptd->met_data.air_temp = tmp / 10.;
3415 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
3416 int dew = bstr->
GetInt(173, 10);
3417 if (dew & 0x00000200)
3419 ptd->met_data.dew_point = dew / 10.;
3424 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
3425 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
3427 int horVis = bstr->
GetInt(194, 8);
3428 if (horVis & 0x80u) {
3430 ptd->met_data.hor_vis_GT =
true;
3432 ptd->met_data.hor_vis_GT =
false;
3434 ptd->met_data.hor_vis = horVis / 10.0;
3436 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
3437 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
3438 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
3439 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
3440 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
3441 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
3442 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
3443 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
3444 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
3445 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
3446 ptd->met_data.seastate = bstr->
GetInt(323, 4);
3448 int wt = bstr->
GetInt(327, 10);
3449 if (wt & 0x00000200)
3451 ptd->met_data.water_temp = wt / 10.;
3453 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
3454 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
3455 ptd->met_data.ice = bstr->
GetInt(349, 2);
3457 ptd->Class = AIS_METEO;
3461 ptd->b_NoTrack =
true;
3462 ptd->b_show_track =
false;
3463 ptd->b_positionDoubtful =
false;
3464 ptd->b_positionOnceValid =
true;
3465 b_posn_report =
true;
3466 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3467 ptd->PositionReportTicks = now.GetTicks();
3468 ptd->b_nameValid =
true;
3470 parse_result =
true;
3476 if (dac == 367 && fi == 33) {
3479 const int size = bstr->GetBitCount();
3482 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
3483 const int startbits = 56;
3484 const int slotsize = 112;
3485 const int slots_count = (size - startbits) / slotsize;
3487 for (
int slot = 0; slot < slots_count; slot++) {
3488 slotbit = slot * slotsize;
3489 int type = bstr->
GetInt(slotbit + 57, 4);
3490 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
3491 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
3492 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
3495 if (!ptd->b_nameValid) {
3496 wxString nameID =
"METEO Site: ";
3498 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
3499 ptd->b_nameValid =
true;
3503 int lon = bstr->
GetInt(slotbit + 90, 28);
3504 if (lon & 0x08000000)
3506 ptd->Lon = lon / 600000.;
3508 int lat = bstr->
GetInt(slotbit + 118, 27);
3509 if (lat & 0x04000000)
3511 ptd->Lat = lat / 600000.;
3512 ptd->b_positionOnceValid =
true;
3514 }
else if (type == 1) {
3515 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
3516 ptd->b_nameValid =
true;
3518 }
else if (type == 2) {
3520 int descr = bstr->
GetInt(slotbit + 116, 3);
3521 if (descr == 1 || descr == 2) {
3522 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
3523 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
3524 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
3525 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
3528 }
else if (type == 3) {
3530 int descr = bstr->
GetInt(slotbit + 108, 3);
3531 if (descr == 1 || descr == 2) {
3532 int wltype = bstr->
GetInt(slotbit + 84, 1);
3533 int wl = bstr->
GetInt(slotbit + 85, 16);
3534 if (wl & 0x00004000)
3538 ptd->met_data.water_level = wl/100.;
3540 ptd->met_data.water_lev_dev = wl / 100.;
3542 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
3543 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
3545 }
else if (type == 6) {
3546 int readbearing = bstr->
GetInt(slotbit + 84, 9);
3547 int readdistance = bstr->
GetInt(slotbit + 93, 9);
3548 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
3549 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
3550 int readLevel = bstr->
GetInt(slotbit + 119, 9);
3552 }
else if (type == 7) {
3553 int swell_descr = bstr->
GetInt(slotbit + 111, 3);
3554 if (swell_descr == 1 || swell_descr == 2) {
3555 ptd->met_data.swell_height = bstr->
GetInt(slotbit + 84, 8) / 10.0;
3556 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
3557 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
3559 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
3560 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
3561 if (wt_descr == 1 || wt_descr == 2)
3562 ptd->met_data.water_temp = bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
3564 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
3565 if (wawe_descr == 1 || wawe_descr == 2) {
3566 ptd->met_data.wave_height = bstr->
GetInt(slotbit + 134, 8) / 10.0;
3567 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
3568 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
3570 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
3572 }
else if (type == 8) {
3573 ptd->met_data.water_temp = bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
3574 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
3576 }
else if (type == 9) {
3577 int tmp = bstr->
GetInt(slotbit + 84, 11);
3578 if (tmp & 0x00000400)
3580 ptd->met_data.air_temp = tmp / 10.;
3581 int pp , precip = bstr->
GetInt(slotbit + 98, 2);
3592 ptd->met_data.precipitation = pp;
3593 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
3594 ptd->met_data.dew_point = bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
3595 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
3596 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
3597 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
3599 }
else if (type == 11) {
3601 int descr = bstr->
GetInt(slotbit + 113, 3);
3602 if (descr == 1 || descr == 2) {
3603 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
3604 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
3605 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
3610 if (ptd->b_positionOnceValid) {
3611 ptd->Class = AIS_METEO;
3615 ptd->b_NoTrack =
true;
3616 ptd->b_show_track =
false;
3617 ptd->b_positionDoubtful =
false;
3618 b_posn_report =
true;
3619 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
3620 ptd->PositionReportTicks = now.GetTicks();
3621 ptd->b_nameValid =
true;
3623 parse_result =
true;
3633 char msg_14_text[968];
3634 if (bstr->GetBitCount() > 40) {
3635 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
3636 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
3638 nd = wxMin(nd, 967);
3639 msg_14_text[nd] = 0;
3640 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
3642 parse_result =
true;
3660 if (b_posn_report) ptd->b_lost =
false;
3662 if (
true == parse_result) {
3664 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
3665 ptd->b_active =
true;
3668 return parse_result;
3671 bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3672 unsigned char checksum_value = 0;
3673 int sentence_hex_sum;
3675 wxCharBuffer buf = str_in.ToUTF8();
3676 if (!buf.data())
return false;
3678 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3679 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3680 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3682 int string_length = strlen(str_ascii);
3684 int payload_length = 0;
3685 while ((payload_length < string_length) &&
3686 (str_ascii[payload_length] !=
'*'))
3689 if (payload_length == string_length)
3694 while (index < payload_length) {
3695 checksum_value ^= str_ascii[index];
3699 if (string_length > 4) {
3701 scanstr[0] = str_ascii[payload_length + 1];
3702 scanstr[1] = str_ascii[payload_length + 2];
3704 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3706 if (sentence_hex_sum == checksum_value)
return true;
3712 void AisDecoder::UpdateAllCPA(
void) {
3714 for (
const auto &it : GetTargetList()) {
3715 std::shared_ptr<AisTargetData> td = it.second;
3717 if (NULL != td) UpdateOneCPA(td.get());
3721 void AisDecoder::UpdateAllTracks(
void) {
3723 for (
const auto &it : GetTargetList()) {
3724 std::shared_ptr<AisTargetData> td = it.second;
3726 if (NULL != td) UpdateOneTrack(td.get());
3732 if (!ptarget->b_positionOnceValid)
return;
3734 if (ptarget->m_ptrack.size() > 0) {
3736 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3737 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3740 ptarget->m_ptrack.pop_back();
3741 ptarget->b_positionDoubtful =
true;
3749 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3752 ptrackpoint.m_lat = ptarget->Lat;
3753 ptrackpoint.m_lon = ptarget->Lon;
3754 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3756 ptarget->m_ptrack.push_back(ptrackpoint);
3758 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3760 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3762 t->SetName(wxString::Format(
3763 _T(
"AIS %s (%u) %s %s"), ptarget->GetFullName().c_str(),
3764 ptarget->MMSI, wxDateTime::Now().FormatISODate().c_str(),
3765 wxDateTime::Now().FormatISOTime().c_str()));
3766 g_TrackList.push_back(t);
3768 m_persistent_tracks[ptarget->MMSI] = t;
3770 t = m_persistent_tracks[ptarget->MMSI];
3773 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3775 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3778 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3779 tp1->m_lon, tp, tp1, t);
3790 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3792 ptarget->m_ptrack.erase(
3793 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3795 return track.m_time < test_time;
3797 ptarget->m_ptrack.end());
3802 void AisDecoder::DeletePersistentTrack(
Track *track) {
3803 for (std::map<int, Track *>::iterator iterator = m_persistent_tracks.begin();
3804 iterator != m_persistent_tracks.end(); iterator++) {
3805 if (iterator->second == track) {
3806 int mmsi = iterator->first;
3807 m_persistent_tracks.erase(iterator);
3809 if (0 == m_persistent_tracks.count(mmsi)) {
3810 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3811 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
3813 if (props->m_bPersistentTrack) {
3817 std::shared_ptr<AisTargetData> td = Get_Target_Data_From_MMSI(mmsi);
3819 props->m_bPersistentTrack =
false;
3820 td->b_mPropPersistTrack =
false;
3822 if (!m_callbacks.confirm_stop_track()) {
3823 props->m_bPersistentTrack =
true;
3835 void AisDecoder::UpdateAllAlarms(
void) {
3836 m_bGeneralAlert =
false;
3839 for (
const auto &it : GetTargetList()) {
3840 std::shared_ptr <AisTargetData> td = it.second;
3844 if (!m_bGeneralAlert) {
3846 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3847 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3848 m_bGeneralAlert =
true;
3851 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3852 m_bGeneralAlert =
false;
3855 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3856 m_bGeneralAlert =
false;
3859 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3862 if (td->Class == AIS_SART && td->NavStatus == 14)
3863 m_bGeneralAlert =
true;
3866 if (( td->Class == AIS_DSC ) && ( ( td->ShipType == 12 ) || ( td->ShipType == 16 ) ))
3867 m_bGeneralAlert =
true;
3870 ais_alert_type this_alarm = AIS_NO_ALERT;
3873 if (td->Class == AIS_SART && td->NavStatus == 14)
3874 this_alarm = AIS_ALERT_SET;
3877 if ((td->Class == AIS_DSC) && (( td->ShipType == 12 ) || ( td->ShipType == 16 )) )
3878 this_alarm = AIS_ALERT_SET;
3880 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3881 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3884 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3885 td->n_alert_state = AIS_NO_ALERT;
3891 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3892 (td->SOG <= g_ShowMoored_Kts)) {
3893 td->n_alert_state = AIS_NO_ALERT;
3899 if (td->Range_NM > g_CPAMax_NM) {
3900 td->n_alert_state = AIS_NO_ALERT;
3905 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3906 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3907 (td->Class != AIS_METEO)) {
3909 if (td->TCPA < g_TCPA_Max) {
3910 if (td->b_isFollower)
3911 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3913 this_alarm = AIS_ALERT_SET;
3916 if (td->b_isFollower)
3917 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3919 this_alarm = AIS_ALERT_SET;
3927 if (g_bAIS_ACK_Timeout || (td->Class == AIS_SART) ||
3928 ((td->Class == AIS_DSC) && ((td->ShipType == 12) || (td->ShipType == 16)) )) {
3929 if (td->b_in_ack_timeout) {
3930 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3931 if (delta.GetMinutes() > g_AckTimeout_Mins)
3932 td->b_in_ack_timeout =
false;
3938 if (td->b_in_ack_timeout) {
3939 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
3943 td->n_alert_state = this_alarm;
3949 ptarget->Range_NM = -1.;
3958 DistanceBearingMercator(ptarget->Lat, ptarget->Lon, gLat, gLon, &brg, &dist);
3959 ptarget->Range_NM = dist;
3962 if (dist <= 1e-5) ptarget->Brg = -1.0;
3964 if (!ptarget->b_positionOnceValid || !bGPSValid) {
3965 ptarget->bCPA_Valid =
false;
3969 if (ptarget->Class == AIS_METEO) {
3970 ptarget->bCPA_Valid =
false;
3979 if (ptarget->b_OwnShip) {
3981 ptarget->TCPA = -100;
3982 ptarget->bCPA_Valid =
false;
3986 double cpa_calc_ownship_cog = gCog;
3987 double cpa_calc_target_cog = ptarget->COG;
3990 if (std::isnan(gSog) || (gSog > 102.2)) {
3991 ptarget->bCPA_Valid =
false;
3996 if (std::isnan(gCog) || gCog == 360.0) {
3998 cpa_calc_ownship_cog =
4002 ptarget->bCPA_Valid =
false;
4008 if (ptarget->COG == 360.0) {
4009 if (ptarget->SOG > 102.2) {
4010 ptarget->bCPA_Valid =
false;
4012 }
else if (ptarget->SOG < .01)
4013 cpa_calc_target_cog =
4017 ptarget->bCPA_Valid =
false;
4023 double v0 = gSog * 1852.;
4024 double v1 = ptarget->SOG * 1852.;
4026 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4030 ptarget->bCPA_Valid =
false;
4037 double east1 = (ptarget->Lon - gLon) * 60 * 1852;
4038 double north1 = (ptarget->Lat - gLat) * 60 * 1852;
4040 double east = east1 * (cos(gLat * PI / 180.));
4042 double north = north1;
4045 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4046 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4047 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4048 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4051 double fc = (v0 * cosa) - (v1 * cosb);
4052 double fs = (v0 * sina) - (v1 * sinb);
4054 double d = (fc * fc) + (fs * fs);
4062 tcpa = ((fc * east) + (fs * north)) / d;
4065 ptarget->TCPA = tcpa * 60.;
4070 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4072 ll_gc_ll(gLat, gLon, cpa_calc_ownship_cog, gSog * tcpa, &OwnshipLatCPA,
4074 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4075 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4078 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4081 ptarget->bCPA_Valid =
true;
4083 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4087 void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4090 if (m_ptentative_dsctarget) {
4091 ProcessDSx(m_dsc_last_string,
true);
4095 void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4100 wxDateTime now = wxDateTime::Now();
4103 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets = GetTargetList();
4105 auto it = current_targets.begin();
4106 std::vector<int> remove_array;
4108 while (it != current_targets.end()) {
4109 if (it->second == NULL)
4111 current_targets.erase(it);
4115 std::shared_ptr<AisTargetData> xtd = it->second;
4117 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4118 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4130 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4132 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4133 double iECD_LostTimeOut = 0.0;
4137 if (xtd->Class == AIS_CLASS_B) {
4138 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR))
4139 iECD_LostTimeOut = 18 * 60;
4141 iECD_LostTimeOut = 180;
4143 if (xtd->Class == AIS_CLASS_A) {
4144 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4146 iECD_LostTimeOut = 18 * 60;
4148 iECD_LostTimeOut = 60;
4150 iECD_LostTimeOut = 60;
4153 if ((target_posn_age > iECD_LostTimeOut) && (xtd->Class != AIS_GPSG_BUDDY))
4154 xtd->b_active =
false;
4156 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4157 }
else if (g_bMarkLost) {
4158 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4159 (xtd->Class != AIS_GPSG_BUDDY))
4160 xtd->b_active =
false;
4163 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4164 removelost_Mins = 18.0;
4168 if (g_bRemoveLost || g_bInlandEcdis) {
4170 (xtd->Class == AIS_ARPA &&
4172 if (((target_posn_age > removelost_Mins * 60) &&
4173 (xtd->Class != AIS_GPSG_BUDDY)) ||
4178 xtd->b_positionOnceValid =
false;
4186 long mmsi_long = xtd->MMSI;
4187 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4192 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4193 xtd->b_removed =
true;
4195 remove_array.push_back(xtd->MMSI);
4202 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
4204 if (xtd->MMSI == props->MMSI) {
4205 if (props->m_bignore) {
4206 remove_array.push_back(xtd->MMSI);
4207 xtd->b_removed =
true;
4218 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4219 auto itd = current_targets.find(remove_array[i]);
4220 if (itd != current_targets.end()) {
4221 std::shared_ptr<AisTargetData> td = itd->second;
4222 current_targets.erase(itd);
4231 m_bSuppressed =
false;
4232 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4233 (g_bShowScaled && g_bAllowShowScaled))
4234 m_bSuppressed =
true;
4236 m_bAIS_Audio_Alert_On =
false;
4245 std::shared_ptr<AisTargetData> palert_target = NULL;
4246 int audioType = AISAUDIO_NONE;
4248 if (!g_pais_alert_dialog_active) {
4249 pAISMOBRoute = NULL;
4250 double tcpa_min = 1e6;
4251 double sart_range = 1e6;
4252 std::shared_ptr<AisTargetData> palert_target_cpa = NULL;
4253 std::shared_ptr<AisTargetData> palert_target_sart = NULL;
4254 std::shared_ptr<AisTargetData> palert_target_dsc = NULL;
4256 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4257 std::shared_ptr<AisTargetData> td = it->second;
4259 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4260 if (g_bAIS_CPA_Alert && td->b_active) {
4261 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4262 if (td->TCPA < tcpa_min) {
4263 tcpa_min = td->TCPA;
4264 palert_target_cpa = td;
4268 }
else if ((td->Class == AIS_DSC) && ((td->ShipType == 12) || (td->ShipType == 16)) ) {
4270 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4271 palert_target_dsc = td;
4274 td->b_isDSCtarget =
false;
4279 else if (td->Class == AIS_SART) {
4281 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4282 if (td->Range_NM < sart_range) {
4283 tcpa_min = sart_range;
4284 palert_target_sart = td;
4294 palert_target = palert_target_cpa;
4295 if (palert_target) audioType = AISAUDIO_CPA;
4297 if (palert_target_sart) {
4298 palert_target = palert_target_sart;
4299 audioType = AISAUDIO_SART;
4302 if (palert_target_dsc) {
4303 palert_target = palert_target_dsc;
4304 audioType = AISAUDIO_DSC;
4309 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4315 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4318 std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
int mmsi) {
4319 if (AISTargetList.find(mmsi) == AISTargetList.end())
4322 return AISTargetList[mmsi];
4325 ArrayOfMmsiProperties g_MMSI_Props_Array;
4329 MmsiProperties::MmsiProperties(wxString &spec) {
4331 wxStringTokenizer tkz(spec, _T(
";"));
4334 s = tkz.GetNextToken();
4339 s = tkz.GetNextToken();
4341 if (s.Upper() == _T(
"ALWAYS"))
4342 TrackType = TRACKTYPE_ALWAYS;
4343 else if (s.Upper() == _T(
"NEVER"))
4344 TrackType = TRACKTYPE_NEVER;
4347 s = tkz.GetNextToken();
4349 if (s.Upper() == _T(
"IGNORE")) m_bignore =
true;
4352 s = tkz.GetNextToken();
4354 if (s.Upper() == _T(
"MOB")) m_bMOB =
true;
4357 s = tkz.GetNextToken();
4359 if (s.Upper() == _T(
"VDM")) m_bVDM =
true;
4362 s = tkz.GetNextToken();
4364 if (s.Upper() == _T(
"FOLLOWER")) m_bFollower =
true;
4367 s = tkz.GetNextToken();
4369 if (s.Upper() == _T(
"PERSIST")) m_bPersistentTrack =
true;
4372 s = tkz.GetNextToken();
4374 m_ShipName = s.Upper();
4378 MmsiProperties::~MmsiProperties() {}
4380 void MmsiProperties::Init(
void) {
4382 TrackType = TRACKTYPE_DEFAULT;
4386 m_bFollower =
false;
4387 m_bPersistentTrack =
false;
4388 m_ShipName = wxEmptyString;
4391 wxString MmsiProperties::Serialize(
void) {
4395 sMMSI.Printf(_T(
"%d"), MMSI);
4399 if (TRACKTYPE_ALWAYS == TrackType)
4400 sMMSI << _T(
"always");
4401 else if (TRACKTYPE_NEVER == TrackType)
4402 sMMSI << _T(
"never");
4407 sMMSI << _T(
"ignore");
4422 sMMSI << _T(
"Follower");
4426 if (m_bPersistentTrack) {
4427 sMMSI << _T(
"PERSIST");
4431 if (m_ShipName == wxEmptyString) {
4432 m_ShipName = GetShipNameFromFile(MMSI);
4434 sMMSI << m_ShipName;
4439 AIS_Target_Name_Hash *AISTargetNamesC,
4440 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4441 if (g_benableAISNameCache) {
4442 wxString ship_name = wxEmptyString;
4445 if (!pTargetData->b_nameValid) {
4446 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4447 if (it != AISTargetNamesC->end()) {
4448 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4449 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4450 ship_name.length() + 1);
4451 pTargetData->b_nameValid =
true;
4452 pTargetData->b_nameFromCache =
true;
4453 }
else if (!g_bUseOnlyConfirmedAISName) {
4454 it = AISTargetNamesNC->find(mmsi);
4455 if (it != AISTargetNamesNC->end()) {
4456 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4457 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4458 ship_name.length() + 1);
4459 pTargetData->b_nameValid =
true;
4460 pTargetData->b_nameFromCache =
true;
4465 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4466 (pTargetData->MID == 19) ||
4467 (pTargetData->MID == 123) ||
4468 (pTargetData->MID == 124) ) {
4470 pTargetData->b_nameFromCache =
false;
4471 ship_name = trimAISField(pTargetData->ShipName);
4472 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4473 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4475 AISTargetNamesC->end()) {
4476 if ((*AISTargetNamesC)[mmsi] ==
4478 if (itNC != AISTargetNamesNC->end()) {
4480 AISTargetNamesNC->erase(itNC);
4483 if (itNC != AISTargetNamesNC->end()) {
4485 if ((*AISTargetNamesNC)[mmsi] ==
4488 (*AISTargetNamesC)[mmsi] = ship_name;
4490 AISTargetNamesNC->erase(itNC);
4493 (*AISTargetNamesNC)[mmsi] = ship_name;
4495 if (g_bUseOnlyConfirmedAISName)
4496 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4497 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4499 (*AISTargetNamesNC)[mmsi] = ship_name;
4504 AISTargetNamesNC->end()) {
4505 if ((*AISTargetNamesNC)[mmsi] ==
4508 (*AISTargetNamesC)[mmsi] = ship_name;
4510 AISTargetNamesNC->erase(itNC);
4512 (*AISTargetNamesNC)[mmsi] = ship_name;
4515 (*AISTargetNamesNC)[mmsi] = ship_name;
4517 if (g_bUseOnlyConfirmedAISName) {
4518 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4519 strncpy(pTargetData->ShipName,
"Unknown ", SHIP_NAME_LEN - 1);
4526 wxString GetShipNameFromFile(
int nmmsi) {
4527 wxString name = wxEmptyString;
4528 if (g_benableAISNameCache) {
4529 std::ifstream infile(AISTargetNameFileName.mb_str());
4532 while (getline(infile, line)) {
4533 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()), _T(
","));
4534 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4535 name = tokenizer.GetNextToken().Trim();
4538 tokenizer.GetNextToken();
4547 int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
int siteID = 0) {
4550 if ((!lon_bits || lon_bits == 999) && siteID) {
4553 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4554 if (points.size()) {
4555 for (
const auto &point : points) {
4557 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4559 new_mmsi = point.mmsi;
4565 if (!found && !lon_bits) {
4570 double lon_tentative = 181.;
4571 double lat_tentative = 91.;
4573 if (lon_bits == 25) {
4574 if (m_lon & 0x01000000)
4575 m_lon |= 0xFE000000;
4576 lon_tentative = m_lon / 60000.;
4578 if (m_lat & 0x00800000)
4579 m_lat |= 0xFF000000;
4580 lat_tentative = m_lat / 60000.;
4582 }
else if (lon_bits == 28) {
4583 if (m_lon & 0x08000000)
4584 m_lon |= 0xf0000000;
4585 lon_tentative = m_lon / 600000.;
4587 if (m_lat & 0x04000000)
4588 m_lat |= 0xf8000000;
4589 lat_tentative = m_lat / 600000.;
4594 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4595 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4603 static int nextMeteommsi = 199400000;
4604 auto& points = AisMeteoPoints::GetInstance().GetPoints();
4606 if (lon_bits != 999 && points.size()) {
4607 wxString t_lat, t_lon;
4608 for (
const auto& point: points) {
4610 if (slat.IsSameAs(point.lat) &&
4611 slon.IsSameAs(point.lon)) {
4613 new_mmsi = point.mmsi;
4622 points.push_back(
AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4623 new_mmsi = nextMeteommsi;
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.
A regular Nmea0183 message.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Adds a std::shared<void> element to wxCommandEvent.
A parsed SignalK message over ipv4.
wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt)
Event from IO thread to main.