41#include <wx/datetime.h>
43#include "rapidjson/document.h"
44#include "rapidjson/writer.h"
45#include "rapidjson/stringbuffer.h"
48#include <wx/textfile.h>
50#include <wx/tokenzr.h>
52#include "ais_decoder.h"
53#include "ais_target_data.h"
54#include "comm_navmsg_bus.h"
55#include "config_vars.h"
59#include "multiplexer.h"
60#include "navutil_base.h"
62#include "route_point.h"
64#include "SoundFactory.h"
67#include "AISTargetAlertDialog.h"
70static const long long lNaN = 0xfff8000000000000;
71#define NAN (*(double *)&lNaN)
80extern double g_CPAMax_NM;
81extern bool g_bCPAWarn;
82extern double g_CPAWarn_NM;
83extern bool g_bTCPA_Max;
84extern double g_TCPA_Max;
85extern bool g_bMarkLost;
86extern double g_MarkLost_Mins;
87extern bool g_bRemoveLost;
88extern double g_RemoveLost_Mins;
89extern double g_AISShowTracks_Mins;
90extern bool g_bHideMoored;
91extern double g_ShowMoored_Kts;
92extern bool g_bAIS_CPA_Alert_Suppress_Moored;
93extern bool g_bAIS_ACK_Timeout;
94extern double g_AckTimeout_Mins;
95extern bool g_bDrawAISSize;
96extern bool g_bAllowShowScaled;
97extern bool g_bShowScaled;
98extern bool g_bInlandEcdis;
99extern int g_WplAction;
100extern bool g_bAIS_CPA_Alert;
101extern bool g_bAIS_CPA_Alert_Audio;
102extern int g_iDistanceFormat;
103extern int g_iSpeedFormat;
105extern ArrayOfMmsiProperties g_MMSI_Props_Array;
106extern Route *pAISMOBRoute;
107extern wxString AISTargetNameFileName;
108extern wxString g_default_wp_icon;
109extern std::vector<Track*> g_TrackList;
113extern wxString g_CmdSoundString;
115bool g_benableAISNameCache;
116bool g_bUseOnlyConfirmedAISName;
117wxString GetShipNameFromFile(
int);
137EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
138EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
141static const
double ms_to_knot_factor = 1.9438444924406;
147static
bool b_firstrx;
148static
int first_rx_ticks;
150static
double arpa_ref_hdg = NAN;
152extern const wxEventType wxEVT_OCPN_DATASTREAM;
153extern
bool g_bquiting;
154extern wxString g_DSC_sound_file;
155extern wxString g_SART_sound_file;
156extern wxString g_AIS_sound_file;
157extern
bool g_bAIS_GCPA_Alert_Audio;
158extern
bool g_bAIS_SART_Alert_Audio;
159extern
bool g_bAIS_DSC_Alert_Audio;
161static inline
double GeodesicRadToDeg(
double rads) {
162 return rads * 180.0 / M_PI;
165static inline double MS2KNOTS(
double ms) {
166 return ms * 1.9438444924406;
170 AIS_Target_Name_Hash *AISTargetNamesC,
171 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
174 : m_signalk_selfid(
""), m_callbacks(callbacks) {
176 AISTargetNamesC =
new AIS_Target_Name_Hash;
177 AISTargetNamesNC =
new AIS_Target_Name_Hash;
179 if (g_benableAISNameCache) {
181 if (infile.Open(AISTargetNameFileName)) {
182 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
183 wxString line = infile.GetFirstLine();
184 while (!infile.Eof()) {
185 if (line.IsSameAs(wxT(
"+++==Confirmed Entry's==+++")))
186 HashFile = AISTargetNamesC;
188 if (line.IsSameAs(wxT(
"+++==Non Confirmed Entry's==+++")))
189 HashFile = AISTargetNamesNC;
191 wxStringTokenizer tokenizer(line, _T(
","));
192 int mmsi = wxAtoi(tokenizer.GetNextToken());
193 wxString name = tokenizer.GetNextToken().Trim();
194 (*HashFile)[mmsi] = name;
197 line = infile.GetNextLine();
203 BuildERIShipTypeHash();
205 g_pais_alert_dialog_active = NULL;
206 m_bAIS_Audio_Alert_On =
false;
210 m_bAIS_AlertPlaying =
false;
212 TimerAIS.SetOwner(
this, TIMER_AIS1);
213 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
215 m_ptentative_dsctarget = NULL;
216 m_dsc_timer.SetOwner(
this, TIMER_DSC);
226AisDecoder::~AisDecoder(
void) {
235 if (outfile.Open(AISTargetNameFileName)) {
237 content = wxT(
"+++==Confirmed Entry's==+++");
238 AIS_Target_Name_Hash::iterator it;
239 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
240 content.append(_T(
"\r\n"));
241 content.append(wxString::Format(wxT(
"%i"), it->first));
242 content.append(_T(
",")).append(it->second);
244 content.append(_T(
"\r\n"));
245 content.append(_T(
"+++==Non Confirmed Entry's==+++"));
246 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
247 content.append(_T(
"\r\n"));
248 content.append(wxString::Format(wxT(
"%i"), it->first));
249 content.append(_T(
",")).append(it->second);
251 outfile.Write(content);
255 AISTargetNamesC->clear();
256 delete AISTargetNamesC;
257 AISTargetNamesNC->clear();
258 delete AISTargetNamesNC;
263 m_AIS_Audio_Alert_Timer.Stop();
268 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
270 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
274void AisDecoder::InitCommListeners(
void) {
277 auto& msgbus = NavMsgBus::GetInstance();
282 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
284 auto ptr = ev.GetSharedPtr();
285 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
286 HandleN0183_AIS( n0183_msg );
291 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
294 auto ptr = ev.GetSharedPtr();
295 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
296 HandleN0183_AIS( n0183_msg );
301 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
303 auto ptr = ev.GetSharedPtr();
304 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
305 HandleN0183_AIS( n0183_msg );
310 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
312 auto ptr = ev.GetSharedPtr();
313 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
314 HandleN0183_AIS( n0183_msg );
319 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
322 auto ptr = ev.GetSharedPtr();
323 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
324 HandleN0183_AIS( n0183_msg );
329 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
331 auto ptr = ev.GetSharedPtr();
332 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
333 HandleN0183_AIS(n0183_msg);
338 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
340 auto ptr = ev.GetSharedPtr();
341 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
342 HandleN0183_AIS( n0183_msg );
347 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
349 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
354 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
355 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
357 HandleN2K_129038(UnpackEvtPointer<Nmea2000Msg>(ev));
362 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
363 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
365 HandleN2K_129039(UnpackEvtPointer<Nmea2000Msg>(ev));
370 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
371 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
373 HandleN2K_129041(UnpackEvtPointer<Nmea2000Msg>(ev));
378 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
379 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
381 HandleN2K_129794(UnpackEvtPointer<Nmea2000Msg>(ev));
386 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
387 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
389 HandleN2K_129809(UnpackEvtPointer<Nmea2000Msg>(ev));
394 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
395 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
397 HandleN2K_129810(UnpackEvtPointer<Nmea2000Msg>(ev));
402 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
403 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
405 HandleN2K_129793(UnpackEvtPointer<Nmea2000Msg>(ev));
411bool AisDecoder::HandleN0183_AIS( std::shared_ptr <const Nmea0183Msg> n0183_msg ){
412 std::string str = n0183_msg->payload;
413 wxString sentence(str.c_str());
414 DecodeN0183(sentence);
419bool AisDecoder::HandleN2K_129038( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
420 std::vector<unsigned char> v = n2k_msg->payload;
423 tN2kAISRepeat Repeat;
434 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
435 tN2kAISTransceiverInformation AISTransceiverInformation;
438 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID,
439 Latitude, Longitude, Accuracy, RAIM, Seconds,
440 COG, SOG, Heading, ROT, NavStat, AISTransceiverInformation)) {
445 long mmsi_long = mmsi;
446 std::shared_ptr<AisTargetData>pTargetData = 0;
447 bool bnewtarget =
false;
449 auto it = AISTargetList.find(mmsi);
450 if (it == AISTargetList.end())
452 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
456 pTargetData = it->second;
459 wxDateTime now = wxDateTime::Now();
463 pTargetData->MMSI = mmsi;
464 pTargetData->MID = MessageID;
465 pTargetData->MMSI = mmsi;
466 pTargetData->Class = AIS_CLASS_A;
468 if( 97 == pTargetData->MMSI / 10000000) {
469 pTargetData->Class = AIS_SART;
471 pTargetData->StaticReportTicks = now.GetTicks();
473 pTargetData->NavStatus = (ais_nav_status)NavStat;
474 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
475 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
476 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
477 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
478 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
480 pTargetData->ROTAIS = ROT;
482 double rot_dir = 1.0;
494 pTargetData->b_OwnShip =
495 AISTransceiverInformation == tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
496 pTargetData->b_active =
true;
497 pTargetData->b_lost =
false;
498 pTargetData->b_positionOnceValid =
true;
499 pTargetData->PositionReportTicks = now.GetTicks();
501 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
511bool AisDecoder::HandleN2K_129039( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
512 std::vector<unsigned char> v = n2k_msg->payload;
522 tN2kAISRepeat Repeat;
532 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
533 tN2kAISTransceiverInformation AISTransceiverInformation;
535 bool DSC, Band, Msg22, State, Display;
538 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID,
539 Latitude, Longitude, Accuracy, RAIM, Seconds, COG,
540 SOG, AISTransceiverInformation, Heading,
541 Unit, Display, DSC, Band, Msg22, Mode, State)) {
546 long mmsi_long = mmsi;
547 std::shared_ptr<AisTargetData> pTargetData = 0;
548 bool bnewtarget =
false;
550 auto it = AISTargetList.find(mmsi);
551 if (it == AISTargetList.end())
553 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
557 pTargetData = it->second;
560 wxDateTime now = wxDateTime::Now();
564 pTargetData->MMSI = mmsi;
565 pTargetData->MID = MessageID;
566 pTargetData->MMSI = mmsi;
567 pTargetData->Class = AIS_CLASS_B;
568 pTargetData->NavStatus = (ais_nav_status)NavStat;
569 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
570 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
571 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
572 if(!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
573 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
575 pTargetData->b_positionOnceValid =
true;
576 pTargetData->b_active =
true;
577 pTargetData->b_lost =
false;
578 pTargetData->PositionReportTicks = now.GetTicks();
579 pTargetData->b_OwnShip =
580 AISTransceiverInformation == tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
582 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
594bool AisDecoder::HandleN2K_129041( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
595 std::vector<unsigned char> v = n2k_msg->payload;
597 tN2kAISAtoNReportData data;
600 struct tN2kAISAtoNReportData {
602 tN2kAISRepeat Repeat;
611 double PositionReferenceStarboard ;
612 double PositionReferenceTrueNorth;
613 tN2kAISAtoNType AtoNType;
614 bool OffPositionIndicator;
615 bool VirtualAtoNFlag;
616 bool AssignedModeFlag;
617 tN2kGNSStype GNSSType;
619 tN2kAISTransceiverInformation AISTransceiverInformation;
620 char AtoNName[34 + 1];
623 if (ParseN2kPGN129041(v, data)){
626 int mmsi = data.UserID;
627 long mmsi_long = mmsi;
628 std::shared_ptr<AisTargetData> pTargetData = 0;
629 bool bnewtarget =
false;
631 auto it = AISTargetList.find(mmsi);
632 if (it == AISTargetList.end())
634 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
638 pTargetData = it->second;
642 pTargetData->MMSI = mmsi;
644 wxDateTime now = wxDateTime::Now();
647 int offpos = data.OffPositionIndicator;
648 int virt = data.VirtualAtoNFlag;
651 pTargetData->NavStatus = ATON_VIRTUAL;
653 pTargetData->NavStatus = ATON_REAL;
655 pTargetData->m_utc_sec = data.Seconds;
657 if (pTargetData->m_utc_sec <= 59 ){
658 pTargetData->NavStatus += 1;
659 if (offpos) pTargetData->NavStatus += 1;
662 data.AtoNName[34] = 0;
663 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
664 pTargetData->b_nameValid =
true;
665 pTargetData->MID = 124;
666 pTargetData->Class = AIS_ATON;
668 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
669 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
670 pTargetData->b_positionDoubtful =
false;
671 pTargetData->b_positionOnceValid =
true;
672 pTargetData->PositionReportTicks = now.GetTicks();
676 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
678 touch_state.Notify();
686bool AisDecoder::HandleN2K_129794( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
687 std::vector<unsigned char> v = n2k_msg->payload;
690 tN2kAISRepeat Repeat;
694 char Name[SHIP_NAME_LEN];
703 char Destination[DESTINATION_LEN];
704 tN2kAISVersion AISversion;
705 tN2kGNSStype GNSStype;
707 tN2kAISTranceiverInfo AISinfo;
710 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID,
711 IMOnumber, Callsign, Name, VesselType, Length,
712 Beam, PosRefStbd, PosRefBow, ETAdate, ETAtime,
713 Draught, Destination, AISversion, GNSStype,
719 long mmsi_long = mmsi;
720 std::shared_ptr<AisTargetData> pTargetData = 0;
721 bool bnewtarget =
false;
723 auto it = AISTargetList.find(mmsi);
724 if (it == AISTargetList.end())
726 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
730 pTargetData = it->second;
734 pTargetData->MMSI = mmsi;
735 Name[
sizeof(Name) - 1] = 0;
736 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
737 pTargetData->b_nameValid =
true;
738 pTargetData->MID = 124;
740 pTargetData->b_OwnShip =
741 AISinfo == tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
743 pTargetData->DimA = PosRefBow;
744 pTargetData->DimB = Length - PosRefBow;
745 pTargetData->DimC = Beam - PosRefStbd;
746 pTargetData->DimD = PosRefStbd;
747 pTargetData->Draft = Draught;
748 pTargetData->IMO = IMOnumber;
749 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
750 pTargetData->ShipType = (
unsigned char)VesselType;
751 Destination[
sizeof(Destination) - 1] = 0;
752 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
754 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
755 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
756 wxDateTime t((time_t)secs);
758 wxDateTime tz = t.ToUTC();
759 pTargetData->ETA_Mo = tz.GetMonth() + 1;
760 pTargetData->ETA_Day = tz.GetDay();
761 pTargetData->ETA_Hr = tz.GetHour();
762 pTargetData->ETA_Min = tz.GetMinute();
766 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
775bool AisDecoder::HandleN2K_129809( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
776 std::vector<unsigned char> v = n2k_msg->payload;
779 tN2kAISRepeat Repeat;
783 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name))
788 long mmsi_long = mmsi;
789 std::shared_ptr<AisTargetData> pTargetData = 0;
790 bool bnewtarget =
false;
792 auto it = AISTargetList.find(mmsi);
793 if (it == AISTargetList.end())
795 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
799 pTargetData = it->second;
803 pTargetData->MMSI = mmsi;
804 Name[
sizeof(Name) - 1] = 0;
805 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
806 pTargetData->b_nameValid =
true;
807 pTargetData->MID = 124;
809 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
820bool AisDecoder::HandleN2K_129810( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
821 std::vector<unsigned char> v = n2k_msg->payload;
824 tN2kAISRepeat Repeat;
833 uint32_t MothershipID;
835 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID,
836 VesselType, Vendor, Callsign, Length, Beam,
837 PosRefStbd, PosRefBow, MothershipID))
842 long mmsi_long = mmsi;
843 std::shared_ptr<AisTargetData> pTargetData = 0;
844 bool bnewtarget =
false;
846 auto it = AISTargetList.find(mmsi);
847 if (it == AISTargetList.end())
849 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
853 pTargetData = it->second;
857 pTargetData->MMSI = mmsi;
858 pTargetData->DimA = PosRefBow;
859 pTargetData->DimB = Length - PosRefBow;
860 pTargetData->DimC = Beam - PosRefStbd;
861 pTargetData->DimD = PosRefStbd;
862 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
863 pTargetData->ShipType = (
unsigned char)VesselType;
865 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
875bool AisDecoder::HandleN2K_129793( std::shared_ptr<const Nmea2000Msg> n2k_msg ){
876 std::vector<unsigned char> v = n2k_msg->payload;
879 tN2kAISRepeat Repeat;
883 unsigned int SecondsSinceMidnight;
884 unsigned int DaysSinceEpoch;
886 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID,
888 SecondsSinceMidnight, DaysSinceEpoch))
890 wxDateTime now = wxDateTime::Now();
896 long mmsi_long = mmsi;
897 std::shared_ptr<AisTargetData> pTargetData = 0;
898 bool bnewtarget =
false;
900 auto it = AISTargetList.find(mmsi);
901 if (it == AISTargetList.end())
903 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
907 pTargetData = it->second;
911 pTargetData->MMSI = mmsi;
912 pTargetData->Class = AIS_BASE;
914 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
915 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
916 pTargetData->b_positionDoubtful =
false;
917 pTargetData->b_positionOnceValid =
true;
918 pTargetData->PositionReportTicks = now.GetTicks();
923 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
940void AisDecoder::BuildERIShipTypeHash(
void) {
941 make_hash_ERI(8000, _(
"Vessel, type unknown"));
942 make_hash_ERI(8150, _(
"Freightbarge"));
943 make_hash_ERI(8160, _(
"Tankbarge"));
944 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
945 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
946 make_hash_ERI(8430, _(
"Pushboat, single"));
947 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
948 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
949 make_hash_ERI(8490, _(
"Bunkership"));
950 make_hash_ERI(8010, _(
"Motor freighter"));
951 make_hash_ERI(8020, _(
"Motor tanker"));
952 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
953 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
954 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
955 make_hash_ERI(8030, _(
"Container vessel"));
956 make_hash_ERI(8040, _(
"Gas tanker"));
957 make_hash_ERI(8050, _(
"Motor freighter, tug"));
958 make_hash_ERI(8060, _(
"Motor tanker, tug"));
959 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
960 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
961 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
962 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
963 make_hash_ERI(8110, _(
"Tug, freighter"));
964 make_hash_ERI(8120, _(
"Tug, tanker"));
965 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
966 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
967 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
968 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
969 make_hash_ERI(8170, _(
"Freightbarge with containers"));
970 make_hash_ERI(8180, _(
"Tankbarge, gas"));
971 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
972 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
973 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
974 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
975 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
976 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
977 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
978 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
979 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
980 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
982 _(
"Pushtow, two barges at least one tanker or gas barge"));
984 _(
"Pushtow, three barges at least one tanker or gas barge"));
986 _(
"Pushtow, four barges at least one tanker or gas barge"));
988 _(
"Pushtow, five barges at least one tanker or gas barge"));
990 _(
"Pushtow, six barges at least one tanker or gas barge"));
992 _(
"Pushtow, seven barges at least one tanker or gas barge"));
994 _(
"Pushtow, eight barges at least one tanker or gas barge"));
996 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
997 make_hash_ERI(8400, _(
"Tug, single"));
998 make_hash_ERI(8410, _(
"Tug, one or more tows"));
999 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
1000 make_hash_ERI(8430, _(
"Pushboat, single"));
1001 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
1002 make_hash_ERI(8441, _(
"Ferry"));
1003 make_hash_ERI(8442, _(
"Red cross ship"));
1004 make_hash_ERI(8443, _(
"Cruise ship"));
1005 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
1006 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
1007 "cable-ship, buoy-ship, dredge"));
1008 make_hash_ERI(8480, _(
"Fishing boat"));
1009 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
1010 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
1011 make_hash_ERI(1510, _(
"Unit carrier maritime"));
1012 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
1013 make_hash_ERI(1530, _(
"Tanker"));
1014 make_hash_ERI(1540, _(
"Liquified gas tanker"));
1015 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
1016 make_hash_ERI(1900, _(
"Fast ship"));
1017 make_hash_ERI(1910, _(
"Hydrofoil"));
1025void AisDecoder::OnEvtAIS(OCPN_DataStreamEvent &event) {
1026 wxString message =
event.ProcessNMEA4Tags();
1029 if (!message.IsEmpty()) {
1030 if (message.Mid(3, 3).IsSameAs(_T(
"VDM")) ||
1031 message.Mid(3, 3).IsSameAs(_T(
"VDO")) ||
1032 message.Mid(1, 5).IsSameAs(_T(
"FRPOS")) ||
1033 message.Mid(1, 2).IsSameAs(_T(
"CD")) ||
1034 message.Mid(3, 3).IsSameAs(_T(
"TLL")) ||
1035 message.Mid(3, 3).IsSameAs(_T(
"TTM")) ||
1036 message.Mid(3, 3).IsSameAs(_T(
"OSD")) ||
1037 (g_bWplUsePosition && message.Mid(3, 3).IsSameAs(_T(
"WPL")))) {
1038 nr = Decode(message);
1039 if (nr == AIS_NoError) {
1040 g_pi_manager->SendAISSentenceToAllPlugIns(message);
1042 gFrame->TouchAISActive();
1051void AisDecoder::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg){
1052 rapidjson::Document root;
1054 root.Parse(sK_msg->raw_message);
1056 if (root.HasParseError())
return;
1058 if (root.HasMember(
"self")) {
1064 if (m_signalk_selfid.IsEmpty()) {
1069 if (root.HasMember(
"context") && root[
"context"].IsString()) {
1070 wxString context = root[
"context"].GetString();
1071 if (context == m_signalk_selfid) {
1073 wxLogMessage(_T(
"** Ignore context own ship.."));
1077 wxString mmsi_string;
1078 if (context.StartsWith(_T(
"vessels.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1079 context.StartsWith(_T(
"atons.urn:mrn:imo:mmsi:"), &mmsi_string) ||
1080 context.StartsWith(_T(
"aircraft.urn:mrn:imo:mmsi:"), &mmsi_string)) {
1083 if (mmsi_string.ToLong(&mmsi)) {
1094 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1095 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
1097 if (props->m_bignore) {
1104 wxJSONWriter writer;
1105 writer.Write(root, dbg);
1107 wxString msg( _T(
"AisDecoder::OnEvtSignalK: ") );
1111 std::shared_ptr<AisTargetData> pTargetData = 0;
1112 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1113 bool bnewtarget =
false;
1114 int last_report_ticks;
1116 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
1119 getMmsiProperties(pTargetData);
1120 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
1121 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin(); itr != root[
"updates"].End(); ++itr) {
1122 handleUpdate(pTargetData, bnewtarget, *itr);
1125 pTargetData->MMSI = mmsi;
1127 if (97 == mmsi / 10000000) {
1128 pTargetData->Class = AIS_SART;
1130 pTargetData->b_OwnShip =
false;
1131 AISTargetList[pTargetData->MMSI] = pTargetData;
1135void AisDecoder::handleUpdate(std::shared_ptr<AisTargetData> pTargetData,
bool bnewtarget,
1136 const rapidjson::Value &update) {
1137 wxString sfixtime =
"";
1139 if (update.HasMember(
"timestamp")) {
1140 sfixtime = update[
"timestamp"].GetString();
1142 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
1143 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin(); itr != update[
"values"].End(); ++itr) {
1144 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
1147 wxDateTime now = wxDateTime::Now();
1148 pTargetData->m_utc_hour = now.ToUTC().GetHour();
1149 pTargetData->m_utc_min = now.ToUTC().GetMinute();
1150 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
1152 pTargetData->b_active =
true;
1153 pTargetData->b_lost =
false;
1155 if (pTargetData->b_positionOnceValid) {
1156 long mmsi_long = pTargetData->MMSI;
1158 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
1159 (
void *)mmsi_long, SELTYPE_AISTARGET);
1160 pSel->SetUserData(pTargetData->MMSI);
1162 UpdateOneCPA(pTargetData.get());
1163 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
1166void AisDecoder::updateItem(std::shared_ptr<AisTargetData> pTargetData,
bool bnewtarget,
1167 const rapidjson::Value &item, wxString &sfixtime)
const {
1168 if (item.HasMember(
"path") && item.HasMember(
"value")) {
1169 const wxString &update_path = item[
"path"].GetString();
1170 if (update_path == _T(
"navigation.position")) {
1171 if (item[
"value"].HasMember(
"latitude") && item[
"value"].HasMember(
"longitude")) {
1172 wxDateTime now = wxDateTime::Now();
1174 double lat = item[
"value"][
"latitude"].GetDouble();
1175 double lon = item[
"value"][
"longitude"].GetDouble();
1176 pTargetData->PositionReportTicks = now.GetTicks();
1177 pTargetData->StaticReportTicks = now.GetTicks();
1178 pTargetData->Lat = lat;
1179 pTargetData->Lon = lon;
1180 pTargetData->b_positionOnceValid =
true;
1181 pTargetData->b_positionDoubtful =
false;
1184 if (item[
"value"].HasMember(
"altitude")) {
1185 pTargetData->altitude = item[
"value"][
"altitude "].GetInt();
1187 }
else if (update_path == _T(
"navigation.speedOverGround")) {
1188 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
1189 }
else if (update_path == _T(
"navigation.courseOverGroundTrue")) {
1190 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1191 }
else if (update_path == _T(
"navigation.headingTrue")) {
1192 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
1193 }
else if (update_path == _T(
"navigation.rateOfTurn")) {
1194 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
1195 }
else if (update_path == _T(
"design.aisShipType")) {
1196 if (item[
"value"].HasMember(
"id")) {
1197 if (!pTargetData->b_isDSCtarget) {
1198 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1201 }
else if (update_path == _T(
"atonType")) {
1202 if (item[
"value"].HasMember(
"id")) {
1203 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
1205 }
else if (update_path == _T(
"virtual")) {
1206 if (item[
"value"].GetBool()) {
1207 pTargetData->NavStatus = ATON_VIRTUAL;
1209 pTargetData->NavStatus = ATON_REAL;
1211 }
else if (update_path == _T(
"offPosition")) {
1212 if (item[
"value"].GetBool()) {
1213 if (ATON_REAL == pTargetData->NavStatus) {
1214 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
1215 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
1216 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
1219 }
else if (update_path == _T(
"design.draft")) {
1220 if (item[
"value"].HasMember(
"maximum")) {
1221 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
1222 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
1224 if (item[
"value"].HasMember(
"current")) {
1225 double draft = item[
"value"][
"current"].GetDouble();
1227 pTargetData->Draft = draft;
1228 pTargetData->Euro_Draft = draft;
1231 }
else if (update_path == _T(
"design.length")) {
1232 if (pTargetData->DimB == 0) {
1233 if (item[
"value"].HasMember(
"overall")) {
1234 if (item[
"value"][
"overall"].IsNumber()) {
1235 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
1236 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
1238 pTargetData->DimB = 0;
1241 }
else if (update_path == _T(
"sensors.ais.class")) {
1242 wxString aisclass = item[
"value"].GetString();
1243 if (aisclass == _T(
"A")) {
1244 if(!pTargetData->b_isDSCtarget)
1245 pTargetData->Class = AIS_CLASS_A;
1246 }
else if (aisclass == _T(
"B")) {
1247 if (!pTargetData->b_isDSCtarget)
1248 pTargetData->Class = AIS_CLASS_B;
1249 pTargetData->NavStatus =
1251 }
else if (aisclass == _T(
"BASE")) {
1252 pTargetData->Class = AIS_BASE;
1253 }
else if (aisclass == _T(
"ATON")) {
1254 pTargetData->Class = AIS_ATON;
1256 }
else if (update_path == _T(
"sensors.ais.fromBow")) {
1257 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
1258 int length = pTargetData->DimA;
1259 if (item[
"value"].IsNumber()) {
1260 pTargetData->DimA = item[
"value"].GetDouble();
1261 pTargetData->DimB = length - item[
"value"].GetDouble();
1264 }
else if (update_path == _T(
"design.beam")) {
1265 if (pTargetData->DimD == 0) {
1266 if (item[
"value"].IsNumber()) {
1267 pTargetData->Euro_Beam = item[
"value"].GetDouble();
1268 pTargetData->DimC = item[
"value"].GetDouble();
1270 pTargetData->DimD = 0;
1272 }
else if (update_path == _T(
"sensors.ais.fromCenter")) {
1273 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
1274 int beam = pTargetData->DimC;
1275 int center = beam / 2;
1276 if (item[
"value"].IsNumber()) {
1278 pTargetData->DimC = center + item[
"value"].GetDouble();
1279 pTargetData->DimD = beam - pTargetData->DimC;
1282 }
else if (update_path == _T(
"navigation.state")) {
1283 wxString state = item[
"value"].GetString();
1284 if (state == _T(
"motoring")) {
1285 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
1286 }
else if (state == _T(
"anchored")) {
1287 pTargetData->NavStatus = AT_ANCHOR;
1288 }
else if (state == _T(
"not under command")) {
1289 pTargetData->NavStatus = NOT_UNDER_COMMAND;
1290 }
else if (state == _T(
"restricted manouverability")) {
1291 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
1292 }
else if (state == _T(
"constrained by draft")) {
1293 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
1294 }
else if (state == _T(
"moored")) {
1295 pTargetData->NavStatus = MOORED;
1296 }
else if (state == _T(
"aground")) {
1297 pTargetData->NavStatus = AGROUND;
1298 }
else if (state == _T(
"fishing")) {
1299 pTargetData->NavStatus = FISHING;
1300 }
else if (state == _T(
"sailing")) {
1301 pTargetData->NavStatus = UNDERWAY_SAILING;
1302 }
else if (state == _T(
"hazardous material high speed")) {
1303 pTargetData->NavStatus = HSC;
1304 }
else if (state == _T(
"hazardous material wing in ground")) {
1305 pTargetData->NavStatus = WIG;
1306 }
else if (state == _T(
"ais-sart")) {
1307 pTargetData->NavStatus = RESERVED_14;
1309 pTargetData->NavStatus = UNDEFINED;
1311 }
else if (update_path == _T(
"navigation.destination.commonName")) {
1312 const wxString &destination = item[
"value"].GetString();
1313 pTargetData->Destination[0] =
'\0';
1314 strncpy(pTargetData->Destination, destination.c_str(),
1315 DESTINATION_LEN - 1);
1316 }
else if (update_path == _T(
"navigation.specialManeuver")) {
1317 if (strcmp(
"not available", item[
"value"].GetString()) != 0 && pTargetData->IMO < 1) {
1318 const wxString &bluesign = item[
"value"].GetString();
1319 if (_T(
"not engaged") == bluesign) {
1320 pTargetData->blue_paddle = 1;
1322 if (_T(
"engaged") == bluesign) {
1323 pTargetData->blue_paddle = 2;
1325 pTargetData->b_blue_paddle =
1326 pTargetData->blue_paddle == 2 ? true :
false;
1328 }
else if (update_path == _T(
"sensors.ais.designatedAreaCode")) {
1329 if (item[
"value"].GetInt() == 200) {
1330 pTargetData->b_hasInlandDac =
true;
1332 }
else if (update_path == _T(
"sensors.ais.functionalId")) {
1333 if (item[
"value"].GetInt() ==
1335 pTargetData->b_hasInlandDac) {
1336 pTargetData->b_isEuroInland =
true;
1338 }
else if (update_path == _T(
"")) {
1339 if (item[
"value"].HasMember(
"name")) {
1340 const wxString &name = item[
"value"][
"name"].GetString();
1341 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
1342 pTargetData->b_nameValid =
true;
1343 pTargetData->MID = 123;
1344 }
else if (item[
"value"].HasMember(
"registrations")) {
1345 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
1346 pTargetData->IMO = wxAtoi(imo.Right(7));
1347 }
else if (item[
"value"].HasMember(
"communication")) {
1348 const wxString &callsign =
1349 item[
"value"][
"communication"][
"callsignVhf"].GetString();
1350 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
1352 if (item[
"value"].HasMember(
"mmsi")) {
1354 wxString tmp = item[
"value"][
"mmsi"].GetString();
1355 if (tmp.ToLong(&mmsi)) {
1356 pTargetData->MMSI = mmsi;
1358 if (97 == mmsi / 10000000) {
1359 pTargetData->Class = AIS_SART;
1361 if (111 == mmsi / 1000000) {
1362 pTargetData->b_SarAircraftPosnReport =
true;
1365 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
1370 wxLogMessage(wxString::Format(
1371 _T(
"** AisDecoder::updateItem: unhandled path %s"), update_path));
1373 rapidjson::StringBuffer buffer;
1374 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1375 item.Accept(writer);
1376 wxString msg(_T(
"update: "));
1377 msg.append(buffer.GetString());
1387AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
1389 wxString *accumulator) {
1391 if (str.Len() > 100)
return AIS_NMEAVDX_TOO_LONG;
1393 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
1395 if (!pos)
return AIS_GENERIC_ERROR;
1397 if (!accumulator)
return AIS_GENERIC_ERROR;
1400 if (!str.Mid(1, 5).IsSameAs(_T(
"AIVDO")))
return AIS_GENERIC_ERROR;
1403 wxStringTokenizer tkz(str, _T(
","));
1406 token = tkz.GetNextToken();
1408 token = tkz.GetNextToken();
1409 int nsentences = atoi(token.mb_str());
1411 token = tkz.GetNextToken();
1412 int isentence = atoi(token.mb_str());
1414 token = tkz.GetNextToken();
1415 token = tkz.GetNextToken();
1417 wxString string_to_parse;
1418 string_to_parse.Clear();
1431 if ((1 == nsentences) && (1 == isentence)) {
1432 string_to_parse = tkz.GetNextToken();
1435 else if (nsentences > 1) {
1436 if (1 == isentence) {
1437 *accumulator = tkz.GetNextToken();
1441 accumulator->Append(tkz.GetNextToken());
1444 if (isentence == nsentences) {
1445 string_to_parse = *accumulator;
1449 if (string_to_parse.IsEmpty() &&
1451 return AIS_INCOMPLETE_MULTIPART;
1460 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1462 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
1464 if (bdecode_result) {
1465 switch (TargetData->MID) {
1470 if (!TargetData->b_positionDoubtful) {
1471 pos->kLat = TargetData->Lat;
1472 pos->kLon = TargetData->Lon;
1478 if (TargetData->COG == 360.0)
1481 pos->kCog = TargetData->COG;
1483 if (TargetData->SOG > 102.2)
1486 pos->kSog = TargetData->SOG;
1488 if ((
int)TargetData->HDG == 511)
1491 pos->kHdt = TargetData->HDG;
1499 return AIS_GENERIC_ERROR;
1504 return AIS_GENERIC_ERROR;
1512AisError AisDecoder::DecodeN0183(
const wxString &str) {
1513 AisError ret = AIS_GENERIC_ERROR;
1514 wxString string_to_parse;
1516 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
1517 double gpsg_cog, gpsg_sog, gpsg_utc_time;
1518 int gpsg_utc_hour = 0;
1519 int gpsg_utc_min = 0;
1520 int gpsg_utc_sec = 0;
1521 char gpsg_name_str[21];
1524 bool bdecode_result =
false;
1529 int follower_mmsi = 0;
1532 long arpa_tgt_num = 0;
1533 double arpa_sog = 0.;
1534 double arpa_cog = 0.;
1535 double arpa_lat = 0.;
1536 double arpa_lon = 0.;
1537 double arpa_dist = 0.;
1538 double arpa_brg = 0.;
1539 wxString arpa_brgunit;
1540 wxString arpa_status;
1541 wxString arpa_distunit;
1542 wxString arpa_cogunit;
1543 wxString arpa_reftarget;
1544 double arpa_mins, arpa_degs;
1545 double arpa_utc_time;
1546 int arpa_utc_hour = 0;
1547 int arpa_utc_min = 0;
1548 int arpa_utc_sec = 0;
1549 char arpa_name_str[21];
1550 bool arpa_lost =
true;
1551 bool arpa_nottracked =
false;
1553 double aprs_lat = 0.;
1554 double aprs_lon = 0.;
1555 char aprs_name_str[21];
1556 double aprs_mins, aprs_degs;
1558 std::shared_ptr<AisTargetData> pTargetData = 0;
1559 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
1560 bool bnewtarget =
false;
1561 int last_report_ticks;
1565 if (str.Len() > 100)
return AIS_NMEAVDX_TOO_LONG;
1567 if (!NMEACheckSumOK(str)) {
1568 return AIS_NMEAVDX_CHECKSUM_BAD;
1570 if (str.Mid(1, 2).IsSameAs(_T(
"CD"))) {
1573 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
1577 wxString string(str);
1578 wxStringTokenizer tkz(
string, _T(
",*"));
1581 token = tkz.GetNextToken();
1582 token = tkz.GetNextToken();
1583 token.ToLong(&arpa_tgt_num);
1584 token = tkz.GetNextToken();
1585 token.ToDouble(&arpa_dist);
1586 token = tkz.GetNextToken();
1587 token.ToDouble(&arpa_brg);
1588 arpa_brgunit = tkz.GetNextToken();
1589 if (arpa_brgunit == _T(
"R")) {
1590 if (std::isnan(arpa_ref_hdg)) {
1591 if (!std::isnan(gHdt))
1596 arpa_brg += arpa_ref_hdg;
1597 if (arpa_brg >= 360.) arpa_brg -= 360.;
1599 token = tkz.GetNextToken();
1600 token.ToDouble(&arpa_sog);
1601 token = tkz.GetNextToken();
1602 token.ToDouble(&arpa_cog);
1603 arpa_cogunit = tkz.GetNextToken();
1604 if (arpa_cogunit == _T(
"R")) {
1605 if (std::isnan(arpa_ref_hdg)) {
1606 if (!std::isnan(gHdt))
1611 arpa_cog += arpa_ref_hdg;
1612 if (arpa_cog >= 360.) arpa_cog -= 360.;
1614 token = tkz.GetNextToken();
1615 token = tkz.GetNextToken();
1617 arpa_distunit = tkz.GetNextToken();
1618 token = tkz.GetNextToken();
1619 if (token == wxEmptyString)
1620 token = wxString::Format(_T(
"ARPA %ld"), arpa_tgt_num);
1621 int len = wxMin(token.Length(), 20);
1622 strncpy(arpa_name_str, token.mb_str(), len);
1623 arpa_name_str[len] = 0;
1624 arpa_status = tkz.GetNextToken();
1625 if (arpa_status != _T(
"L" )) {
1627 }
else if (arpa_status != wxEmptyString)
1628 arpa_nottracked =
true;
1629 arpa_reftarget = tkz.GetNextToken();
1630 if (tkz.HasMoreTokens()) {
1631 token = tkz.GetNextToken();
1632 token.ToDouble(&arpa_utc_time);
1633 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1634 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1636 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1638 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
1639 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
1640 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
1643 if (arpa_distunit == _T(
"K")) {
1644 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_KM, g_iDistanceFormat);
1645 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_KMH, g_iSpeedFormat);
1646 }
else if (arpa_distunit == _T(
"S")) {
1647 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_MI, g_iDistanceFormat);
1648 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_MPH, g_iSpeedFormat);
1651 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
1655 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
1658 wxString aprs_tll_str;
1659 wxString string(str);
1660 wxStringTokenizer tkz(
string, _T(
",*"));
1663 aprs_tll_str = tkz.GetNextToken();
1664 token = tkz.GetNextToken();
1665 token.ToLong(&arpa_tgt_num);
1666 token = tkz.GetNextToken();
1667 token.ToDouble(&arpa_lat);
1668 arpa_degs = (int)(arpa_lat / 100.0);
1669 arpa_mins = arpa_lat - arpa_degs * 100.0;
1670 arpa_lat = arpa_degs + arpa_mins / 60.0;
1671 token = tkz.GetNextToken();
1672 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1673 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1674 arpa_lat = 0. - arpa_lat;
1675 token = tkz.GetNextToken();
1676 token.ToDouble(&arpa_lon);
1677 arpa_degs = (int)(arpa_lon / 100.0);
1678 arpa_mins = arpa_lon - arpa_degs * 100.0;
1679 arpa_lon = arpa_degs + arpa_mins / 60.0;
1680 token = tkz.GetNextToken();
1681 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1682 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1683 arpa_lon = 0. - arpa_lon;
1684 token = tkz.GetNextToken();
1685 if (token == wxEmptyString)
1686 token = wxString::Format(_T(
"ARPA %d"), arpa_tgt_num);
1687 int len = wxMin(token.Length(), 20);
1688 strncpy(arpa_name_str, token.mb_str(), len);
1689 arpa_name_str[len] = 0;
1690 token = tkz.GetNextToken();
1691 token.ToDouble(&arpa_utc_time);
1692 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
1693 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
1695 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
1696 arpa_status = tkz.GetNextToken();
1699 if (arpa_status != _T(
"L"))
1701 else if (arpa_status != wxEmptyString)
1702 arpa_nottracked =
true;
1703 arpa_reftarget = tkz.GetNextToken();
1710 }
else if (str.Mid(3, 3).IsSameAs(_T(
"OSD"))) {
1712 wxString string(str);
1713 wxStringTokenizer tkz(
string, _T(
",*"));
1716 token = tkz.GetNextToken();
1717 token = tkz.GetNextToken();
1718 token.ToDouble(&arpa_ref_hdg);
1728 }
else if (str.Mid(3, 3).IsSameAs(_T(
"WPL"))) {
1730 wxString string(str);
1731 wxStringTokenizer tkz(
string, _T(
",*"));
1734 token = tkz.GetNextToken();
1735 token = tkz.GetNextToken();
1736 token.ToDouble(&aprs_lat);
1737 aprs_degs = (int)(aprs_lat / 100.0);
1738 aprs_mins = aprs_lat - aprs_degs * 100.0;
1739 aprs_lat = aprs_degs + aprs_mins / 60.0;
1740 token = tkz.GetNextToken();
1741 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1742 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1743 aprs_lat = 0. - aprs_lat;
1744 token = tkz.GetNextToken();
1745 token.ToDouble(&aprs_lon);
1746 aprs_degs = (int)(aprs_lon / 100.0);
1747 aprs_mins = aprs_lon - aprs_degs * 100.0;
1748 aprs_lon = aprs_degs + aprs_mins / 60.0;
1749 token = tkz.GetNextToken();
1750 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1751 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1752 aprs_lon = 0. - aprs_lon;
1753 token = tkz.GetNextToken();
1754 int len = wxMin(token.Length(), 20);
1755 strncpy(aprs_name_str, token.mb_str(), len + 1);
1756 if (0 == g_WplAction) {
1758 aprs_name_str[len] = 0;
1759 for (i = 0; i < len; i++) {
1761 hash += (int)(aprs_name_str[i]);
1762 while (hash >= 100000) hash = hash / 100000;
1764 mmsi = aprs_mmsi = 199300000 + hash;
1768 }
else if (1 == g_WplAction) {
1778 }
else if (str.Mid(1, 5).IsSameAs(_T(
"FRPOS"))) {
1782 wxString string(str);
1783 wxStringTokenizer tkz(
string, _T(
",*"));
1786 token = tkz.GetNextToken();
1788 token = tkz.GetNextToken();
1789 token.ToDouble(&gpsg_lat);
1790 gpsg_degs = (int)(gpsg_lat / 100.0);
1791 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
1792 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
1794 token = tkz.GetNextToken();
1795 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
1796 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
1797 gpsg_lat = 0. - gpsg_lat;
1799 token = tkz.GetNextToken();
1800 token.ToDouble(&gpsg_lon);
1801 gpsg_degs = (int)(gpsg_lon / 100.0);
1802 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
1803 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
1805 token = tkz.GetNextToken();
1806 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
1807 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
1808 gpsg_lon = 0. - gpsg_lon;
1810 token = tkz.GetNextToken();
1813 token = tkz.GetNextToken();
1814 token.ToDouble(&gpsg_sog);
1816 token = tkz.GetNextToken();
1817 token.ToDouble(&gpsg_cog);
1819 token = tkz.GetNextToken();
1822 token = tkz.GetNextToken();
1823 token.ToDouble(&gpsg_utc_time);
1824 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
1825 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
1827 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
1831 token = tkz.GetNextToken();
1832 int i, len, hash = 0;
1833 len = wxMin(wxStrlen(token), 20);
1834 strncpy(gpsg_name_str, token.mb_str(), len);
1835 gpsg_name_str[len] = 0;
1836 for (i = 0; i < len; i++) {
1838 hash += (int)(token[i]);
1839 while (hash >= 100000) hash = hash / 100000;
1842 gpsg_mmsi = 199000000 + hash;
1844 }
else if (!str.Mid(3, 2).IsSameAs(_T(
"VD"))) {
1845 return AIS_NMEAVDX_BAD;
1851 wxString string(str);
1852 wxStringTokenizer tkz(
string, _T(
","));
1855 token = tkz.GetNextToken();
1857 token = tkz.GetNextToken();
1858 nsentences = atoi(token.mb_str());
1860 token = tkz.GetNextToken();
1861 isentence = atoi(token.mb_str());
1863 token = tkz.GetNextToken();
1864 long lsequence_id = 0;
1865 token.ToLong(&lsequence_id);
1867 token = tkz.GetNextToken();
1869 token.ToLong(&lchannel);
1872 string_to_parse.Clear();
1876 if ((1 == nsentences) && (1 == isentence)) {
1877 string_to_parse = tkz.GetNextToken();
1880 else if (nsentences > 1) {
1881 if (1 == isentence) {
1882 sentence_accumulator = tkz.GetNextToken();
1886 sentence_accumulator += tkz.GetNextToken();
1889 if (isentence == nsentences) {
1890 string_to_parse = sentence_accumulator;
1894 if (mmsi || (!string_to_parse.IsEmpty() &&
1895 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
1897 wxCharBuffer abuf = string_to_parse.ToUTF8();
1899 return AIS_GENERIC_ERROR;
1904 if (!mmsi) mmsi = strbit.GetInt(9, 30);
1905 long mmsi_long = mmsi;
1907 auto it = AISTargetList.find(mmsi);
1908 if (it == AISTargetList.end())
1910 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1914 pTargetData = it->second;
1915 pStaleTarget = pTargetData;
1917 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1919 if (mmsi == props->MMSI) {
1921 if (props->m_bFollower) follower_mmsi = mmsi;
1924 if (TRACKTYPE_NEVER == props->TrackType) {
1925 pTargetData->b_show_track =
false;
1927 else if (TRACKTYPE_ALWAYS == props->TrackType) {
1928 pTargetData->b_show_track =
true;
1933 if (props->m_bignore)
return AIS_NoError;
1936 else if (props->m_bVDM) {
1938 if (str.Mid(3, 9).IsSameAs(wxT(
"VDM,1,1,,"))) {
1939 int message_ID = strbit.GetInt(1, 6);
1942 if ((message_ID <= 3) || (message_ID == 18)) {
1944 pTargetData->b_OwnShip =
true;
1946 wxString aivdostr = str;
1947 aivdostr.replace(1, 5,
"AIVDO");
1948 unsigned char calculated_checksum = 0;
1949 wxString::iterator i;
1950 for (i = aivdostr.begin() + 1; i != aivdostr.end() && *i !=
'*';
1952 calculated_checksum ^=
static_cast<unsigned char>(*i);
1955 if (i <= aivdostr.end() - 3)
1958 wxString::Format(_(
"%02X"), calculated_checksum));
1960 gps_watchdog_timeout_ticks =
1979 wxDateTime now = wxDateTime::Now();
1983 last_report_ticks = pStaleTarget->PositionReportTicks;
1985 last_report_ticks = now.GetTicks();
1989 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
1993 pTargetData->PositionReportTicks = now.GetTicks();
1994 pTargetData->StaticReportTicks = now.GetTicks();
1995 pTargetData->m_utc_hour = gpsg_utc_hour;
1996 pTargetData->m_utc_min = gpsg_utc_min;
1997 pTargetData->m_utc_sec = gpsg_utc_sec;
1998 pTargetData->m_date_string = gpsg_date;
1999 pTargetData->MMSI = gpsg_mmsi;
2000 pTargetData->NavStatus = 0;
2001 pTargetData->Lat = gpsg_lat;
2002 pTargetData->Lon = gpsg_lon;
2003 pTargetData->b_positionOnceValid =
true;
2004 pTargetData->COG = gpsg_cog;
2005 pTargetData->SOG = gpsg_sog;
2006 pTargetData->ShipType = 52;
2007 pTargetData->Class = AIS_GPSG_BUDDY;
2008 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
2009 pTargetData->b_nameValid =
true;
2010 pTargetData->b_active =
true;
2011 pTargetData->b_lost =
false;
2013 bdecode_result =
true;
2014 }
else if (arpa_mmsi) {
2015 pTargetData->m_utc_hour = arpa_utc_hour;
2016 pTargetData->m_utc_min = arpa_utc_min;
2017 pTargetData->m_utc_sec = arpa_utc_sec;
2018 pTargetData->MMSI = arpa_mmsi;
2019 pTargetData->NavStatus = 15;
2020 if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
2023 (now.GetTicks() - pTargetData->PositionReportTicks);
2024 if (age_of_last > 0) {
2025 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
2026 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
2027 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2030 pTargetData->Lat = arpa_lat;
2031 pTargetData->Lon = arpa_lon;
2032 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
2033 if (arpa_dist != 0.)
2034 ll_gc_ll(gLat, gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
2038 pTargetData->COG = arpa_cog;
2039 pTargetData->SOG = arpa_sog;
2041 pTargetData->PositionReportTicks = now.GetTicks();
2042 pTargetData->StaticReportTicks = now.GetTicks();
2043 pTargetData->b_positionOnceValid =
true;
2044 pTargetData->ShipType = 55;
2045 pTargetData->Class = AIS_ARPA;
2047 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
2048 if (arpa_status != _T(
"Q"))
2049 pTargetData->b_nameValid =
true;
2051 pTargetData->b_nameValid =
false;
2052 pTargetData->b_active = !arpa_lost;
2053 pTargetData->b_lost = arpa_nottracked;
2055 bdecode_result =
true;
2056 }
else if (aprs_mmsi) {
2057 pTargetData->m_utc_hour = now.GetHour();
2058 pTargetData->m_utc_min = now.GetMinute();
2059 pTargetData->m_utc_sec = now.GetSecond();
2060 pTargetData->MMSI = aprs_mmsi;
2061 pTargetData->NavStatus = 15;
2063 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
2064 if (age_of_last > 0) {
2065 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
2066 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
2067 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
2070 pTargetData->PositionReportTicks = now.GetTicks();
2071 pTargetData->StaticReportTicks = now.GetTicks();
2072 pTargetData->Lat = aprs_lat;
2073 pTargetData->Lon = aprs_lon;
2074 pTargetData->b_positionOnceValid =
true;
2075 pTargetData->ShipType = 56;
2076 pTargetData->Class = AIS_APRS;
2077 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
2078 pTargetData->b_nameValid =
true;
2079 pTargetData->b_active =
true;
2080 pTargetData->b_lost =
false;
2082 bdecode_result =
true;
2086 Parse_VDXBitstring(&strbit, pTargetData);
2090 if (follower_mmsi) pTargetData->b_isFollower =
true;
2093 pTargetData->RecentPeriod =
2094 pTargetData->PositionReportTicks - last_report_ticks;
2104 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
2109 if ((n_msgs % 10000) == 0)
2110 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
2111 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
2117void AisDecoder::CommitAISTarget(std::shared_ptr<AisTargetData> pTargetData,
2118 const wxString &str,
2122 m_pLatestTargetData = pTargetData;
2124 if (!str.IsEmpty()) {
2125 if (str.Mid(3, 3).IsSameAs(_T(
"VDO")))
2126 pTargetData->b_OwnShip =
true;
2128 pTargetData->b_OwnShip =
false;
2131 if (!pTargetData->b_OwnShip) {
2133 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
2135 pTargetData->b_PersistTrack =
false;
2137 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2138 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2140 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2147 pTargetData->b_NoTrack =
false;
2152 if (message_valid) {
2154 if (pTargetData->MMSI) {
2155 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC, pTargetData->MMSI);
2157 AISTargetList[pTargetData->MMSI] =
2160 if (!pTargetData->area_notices.empty()) {
2161 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
2162 if (it == AIS_AreaNotice_Sources.end())
2163 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
2168 if (!pTargetData->b_OwnShip) {
2169 if (pTargetData->b_positionOnceValid) {
2170 long mmsi_long = pTargetData->MMSI;
2171 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2172 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2174 pSel->SetUserData(pTargetData->MMSI);
2178 UpdateOneCPA(pTargetData.get());
2181 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2194 if (!pTargetData->b_OwnShip) {
2195 if (pTargetData->b_positionOnceValid) {
2196 long mmsi_long = pTargetData->MMSI;
2197 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
2198 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
2200 pSel->SetUserData(pTargetData->MMSI);
2210void AisDecoder::getAISTarget(
long mmsi, std::shared_ptr<AisTargetData> &pTargetData,
2211 std::shared_ptr<AisTargetData> &pStaleTarget,
bool &bnewtarget,
2212 int &last_report_ticks, wxDateTime &now) {
2213 now = wxDateTime::Now();
2214 auto it = AISTargetList.find(mmsi);
2215 if (it == AISTargetList.end())
2217 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2221 pTargetData = it->second;
2222 pStaleTarget = pTargetData;
2229 last_report_ticks = pStaleTarget->PositionReportTicks;
2231 last_report_ticks = now.GetTicks();
2235 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
2238void AisDecoder::getMmsiProperties(std::shared_ptr<AisTargetData> &pTargetData) {
2239 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
2240 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
2242 pTargetData->b_isFollower = props->m_bFollower;
2243 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
2244 if (TRACKTYPE_NEVER == props->TrackType) {
2245 pTargetData->b_show_track =
false;
2247 else if (TRACKTYPE_ALWAYS == props->TrackType) {
2248 pTargetData->b_show_track =
true;
2255std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
bool b_take_dsc) {
2256 double dsc_lat = 0.;
2257 double dsc_lon = 0.;
2258 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
2260 double dse_lat = 0.;
2261 double dse_lon = 0.;
2262 long dsc_fmt, dsc_quadrant,dsc_cat, dsc_nature;
2265 int dsc_tx_mmsi = 0;
2267 double dse_cog = 0.;
2268 double dse_sog = 0.;
2269 wxString dse_shipName = wxEmptyString;
2274 std::shared_ptr<AisTargetData>pTargetData = NULL;
2278 wxString string(str);
2279 wxStringTokenizer tkz(
string, _T(
",*"));
2282 token = tkz.GetNextToken();
2284 if (str.Mid(3, 3).IsSameAs(_T(
"DSC"))) {
2285 m_dsc_last_string = str;
2287 token = tkz.GetNextToken();
2288 token.ToLong(&dsc_fmt);
2290 token = tkz.GetNextToken();
2292 if (dsc_fmt == 12 || dsc_fmt == 16) {
2293 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2296 token.ToDouble(&dsc_addr);
2297 dsc_mmsi = 0 - (int)( dsc_addr / 10 );
2300 token = tkz.GetNextToken();
2301 token.ToLong(&dsc_cat);
2303 token = tkz.GetNextToken();
2304 if (!token.IsSameAs(wxEmptyString)) {
2305 token.ToLong(&dsc_nature);
2307 else dsc_nature = 99;
2309 token = tkz.GetNextToken();
2311 token = tkz.GetNextToken();
2312 token.ToDouble(&dsc_tmp);
2314 token = tkz.GetNextToken();
2315 token = tkz.GetNextToken();
2316 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(wxEmptyString) ) {
2318 dsc_tx_mmsi = dsc_mmsi;
2319 dsc_mmsi = wxAtoi(token.Mid(0, 9));
2321 token = tkz.GetNextToken();
2322 if (dsc_fmt == 16 && dsc_cat == 12) {
2323 if (!token.IsSameAs(wxEmptyString)) {
2324 token.ToLong(&dsc_nature);
2326 else dsc_nature = 99;
2328 token = tkz.GetNextToken();
2329 token = tkz.GetNextToken();
2331 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
2333 if (dsc_quadrant > 3)
2336 dsc_lat = (int)(dsc_tmp / 100000.0);
2337 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
2338 dsc_lat = dsc_lat - dsc_quadrant * 10000;
2339 dsc_degs = (int)(dsc_lat / 100.0);
2340 dsc_mins = dsc_lat - dsc_degs * 100.0;
2341 dsc_lat = dsc_degs + dsc_mins / 60.0;
2343 dsc_degs = (int)(dsc_lon / 100.0);
2344 dsc_mins = dsc_lon - dsc_degs * 100.0;
2345 dsc_lon = dsc_degs + dsc_mins / 60.0;
2346 switch (dsc_quadrant) {
2362 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
2364 }
else if (str.Mid(3, 3).IsSameAs(_T(
"DSE"))) {
2365 token = tkz.GetNextToken();
2366 token = tkz.GetNextToken();
2367 token = tkz.GetNextToken();
2368 token = tkz.GetNextToken();
2369 dse_mmsi = wxAtoi(token.Mid(0, 9));
2374 token = tkz.GetNextToken();
2377 token.ToDouble(&dse_tmp);
2378 dse_lat = (int)(dse_tmp / 10000.0);
2379 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2380 dse_lat = dse_lat / 600000.0;
2381 dse_lon = dse_lon / 600000.0;
2384 while (tkz.HasMoreTokens()) {
2385 dseSymbol = tkz.GetNextToken();
2386 token = tkz.GetNextToken();
2387 if (dseSymbol.IsSameAs(_T(
"00"))) {
2388 token.ToDouble(&dse_tmp);
2389 dse_lat = (int)(dse_tmp / 10000.0);
2390 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
2391 dse_lat = dse_lat / 600000.0;
2392 dse_lon = dse_lon / 600000.0;
2394 else if (dseSymbol.IsSameAs(_T(
"01"))) {
2396 else if (dseSymbol.IsSameAs(_T(
"02"))) {
2397 token.ToDouble(&dse_tmp);
2398 dse_sog = dse_tmp / 10.0;
2400 else if (dseSymbol.IsSameAs(_T(
"03"))) {
2401 token.ToDouble(&dse_tmp);
2402 dse_cog = dse_tmp / 10.0;
2404 else if (dseSymbol.IsSameAs(_T(
"04"))) {
2405 dse_shipName = DecodeDSEExpansionCharacters(token);
2407 else if (dseSymbol.IsSameAs(_T(
"05"))) {
2409 else if (dseSymbol.IsSameAs(_T(
"06"))) {
2412 mmsi = abs((
int)dse_mmsi);
2416 wxDateTime now = wxDateTime::Now();
2418 int last_report_ticks = now.GetTicks();
2421 auto it = AISTargetList.find(mmsi);
2422 std::shared_ptr<AisTargetData> pStaleTarget = NULL;
2423 if (it == AISTargetList.end()) {
2425 pStaleTarget = it->second;
2426 last_report_ticks = pStaleTarget->PositionReportTicks;
2432 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
2434 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
2435 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
2437 m_ptentative_dsctarget->MMSI = mmsi;
2438 m_ptentative_dsctarget->NavStatus = 99;
2439 m_ptentative_dsctarget->Lat = dsc_lat;
2440 m_ptentative_dsctarget->Lon = dsc_lon;
2441 m_ptentative_dsctarget->b_positionOnceValid =
true;
2442 m_ptentative_dsctarget->COG = 0;
2443 m_ptentative_dsctarget->SOG = 0;
2444 m_ptentative_dsctarget->ShipType = dsc_fmt;
2445 m_ptentative_dsctarget->Class = AIS_DSC;
2446 m_ptentative_dsctarget->b_isDSCtarget =
true;
2447 m_ptentative_dsctarget->b_nameValid =
true;
2448 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
2449 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %d",
2451 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
2452 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
2454 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %d",
2458 m_ptentative_dsctarget->b_active =
true;
2459 m_ptentative_dsctarget->b_lost =
false;
2460 m_ptentative_dsctarget->RecentPeriod =
2461 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2464 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
2469 if (dse_mmsi || b_take_dsc) {
2470 if (m_ptentative_dsctarget) {
2476 m_ptentative_dsctarget->Lat =
2477 m_ptentative_dsctarget->Lat +
2478 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
2479 m_ptentative_dsctarget->Lon =
2480 m_ptentative_dsctarget->Lon +
2481 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
2482 if (dse_shipName.length() > 0) {
2483 memset(m_ptentative_dsctarget->ShipName,
'\0',SHIP_NAME_LEN);
2484 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
2485 "%s", dse_shipName.ToAscii().data());
2487 m_ptentative_dsctarget->COG = dse_cog;
2488 m_ptentative_dsctarget->SOG = dse_sog;
2492 m_ptentative_dsctarget->RecentPeriod =
2493 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
2498 auto it = AISTargetList.find(mmsi);
2499 if (it == AISTargetList.end()) {
2500 pTargetData = m_ptentative_dsctarget;
2502 pTargetData = it->second;
2503 std::vector<AISTargetTrackPoint> ptrack =
2504 std::move(pTargetData->m_ptrack);
2505 pTargetData->CloneFrom(
2506 m_ptentative_dsctarget.get());
2508 pTargetData->m_ptrack =
2515 m_ptentative_dsctarget = NULL;
2517 m_pLatestTargetData = pTargetData;
2519 AISTargetList[pTargetData->MMSI] =
2522 long mmsi_long = pTargetData->MMSI;
2526 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
2529 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2530 (
void *)mmsi_long, SELTYPE_AISTARGET);
2531 pSel->SetUserData(pTargetData->MMSI);
2534 UpdateOneCPA(pTargetData.get());
2537 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2545wxString AisDecoder::DecodeDSEExpansionCharacters(wxString dseData) {
2547 char lookupTable[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
' ',
2548 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
2549 'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
2550 'Y',
'Z',
'.',
',',
'-',
'/',
' ' };
2552 for (
size_t i = 0; i < dseData.length(); i += 2) {
2553 result.append(1, lookupTable[strtol(dseData.Mid(i, 2).data(), NULL, 10)]);
2562 std::shared_ptr<AisTargetData> ptd) {
2563 bool parse_result =
false;
2564 bool b_posn_report =
false;
2566 wxDateTime now = wxDateTime::Now();
2568 int message_ID = bstr->
GetInt(1, 6);
2569 ptd->MID = message_ID;
2573 switch (message_ID) {
2579 ptd->NavStatus = bstr->
GetInt(39, 4);
2580 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
2582 int lon = bstr->
GetInt(62, 28);
2583 if (lon & 0x08000000)
2585 double lon_tentative = lon / 600000.;
2587 int lat = bstr->
GetInt(90, 27);
2588 if (lat & 0x04000000)
2590 double lat_tentative = lat / 600000.;
2592 if ((lon_tentative <= 180.) &&
2596 ptd->Lon = lon_tentative;
2597 ptd->Lat = lat_tentative;
2598 ptd->b_positionDoubtful =
false;
2599 ptd->b_positionOnceValid =
true;
2600 ptd->PositionReportTicks = now.GetTicks();
2602 ptd->b_positionDoubtful =
true;
2605 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
2606 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
2608 ptd->ROTAIS = bstr->
GetInt(43, 8);
2609 double rot_dir = 1.0;
2611 if (ptd->ROTAIS == 128)
2613 else if ((ptd->ROTAIS & 0x80) == 0x80) {
2614 ptd->ROTAIS = ptd->ROTAIS - 256;
2618 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
2621 ptd->m_utc_sec = bstr->
GetInt(138, 6);
2623 if ((1 == message_ID) ||
2626 ptd->SyncState = bstr->
GetInt(151, 2);
2627 ptd->SlotTO = bstr->
GetInt(153, 2);
2628 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
2630 ptd->m_utc_hour = bstr->
GetInt(155, 5);
2632 ptd->m_utc_min = bstr->
GetInt(160, 7);
2634 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
2635 (ptd->m_utc_sec < 60)) {
2636 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
2637 rx_ticks = rx_time.GetTicks();
2639 first_rx_ticks = rx_ticks;
2647 ptd->blue_paddle = bstr->
GetInt(144, 2);
2648 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
2650 if (!ptd->b_isDSCtarget)
2651 ptd->Class = AIS_CLASS_A;
2654 int mmsi_start = ptd->MMSI / 10000000;
2656 if (mmsi_start == 97) {
2657 ptd->Class = AIS_SART;
2658 ptd->StaticReportTicks =
2672 parse_result =
true;
2673 b_posn_report =
true;
2682 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2684 int lon = bstr->
GetInt(58, 28);
2685 if (lon & 0x08000000)
2687 double lon_tentative = lon / 600000.;
2689 int lat = bstr->
GetInt(86, 27);
2690 if (lat & 0x04000000)
2692 double lat_tentative = lat / 600000.;
2694 if ((lon_tentative <= 180.) &&
2698 ptd->Lon = lon_tentative;
2699 ptd->Lat = lat_tentative;
2700 ptd->b_positionDoubtful =
false;
2701 ptd->b_positionOnceValid =
true;
2702 ptd->PositionReportTicks = now.GetTicks();
2704 ptd->b_positionDoubtful =
true;
2706 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2707 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2709 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2711 if (!ptd->b_isDSCtarget)
2712 ptd->Class = AIS_CLASS_B;
2714 parse_result =
true;
2715 b_posn_report =
true;
2723 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
2724 int lon = bstr->
GetInt(58, 28);
2725 if (lon & 0x08000000)
2727 double lon_tentative = lon / 600000.;
2729 int lat = bstr->
GetInt(86, 27);
2730 if (lat & 0x04000000)
2732 double lat_tentative = lat / 600000.;
2734 if ((lon_tentative <= 180.) &&
2738 ptd->Lon = lon_tentative;
2739 ptd->Lat = lat_tentative;
2740 ptd->b_positionDoubtful =
false;
2741 ptd->b_positionOnceValid =
true;
2742 ptd->PositionReportTicks = now.GetTicks();
2744 ptd->b_positionDoubtful =
true;
2746 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
2747 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
2748 ptd->m_utc_sec = bstr->
GetInt(134, 6);
2750 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
2751 ptd->b_nameValid =
true;
2752 if (!ptd->b_isDSCtarget) {
2753 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
2755 ptd->DimA = bstr->
GetInt(272, 9);
2756 ptd->DimB = bstr->
GetInt(281, 9);
2757 ptd->DimC = bstr->
GetInt(290, 6);
2758 ptd->DimD = bstr->
GetInt(296, 6);
2760 if (!ptd->b_isDSCtarget)
2761 ptd->Class = AIS_CLASS_B;
2762 parse_result =
true;
2763 b_posn_report =
true;
2776 int bitCorrection = 10;
2777 int resolution = 10;
2780 double lon_tentative = 181.;
2781 double lat_tentative = 91.;
2784 printf(
"AIS Message 27 - received:\r\n");
2785 printf(
"MMSI : %i\r\n", ptd->MMSI);
2791 if (!ptd->b_isDSCtarget)
2792 ptd->Class = AIS_CLASS_A;
2794 ptd->NavStatus = bstr->
GetInt(39, 4);
2796 int lon = bstr->
GetInt(45, 18);
2797 int lat = bstr->
GetInt(63, 17);
2799 lat_tentative = lat;
2800 lon_tentative = lon;
2803 if (lat >= (0x4000000 >> bitCorrection)) {
2804 lat_tentative = (0x8000000 >> bitCorrection) - lat;
2805 lat_tentative *= -1;
2809 if (lon >= (0x8000000 >> bitCorrection)) {
2810 lon_tentative = (0x10000000 >> bitCorrection) - lon;
2811 lon_tentative *= -1;
2815 lat_tentative = lat_tentative / resolution / 60.0;
2816 lon_tentative = lon_tentative / resolution / 60.0;
2819 printf(
"Latitude : %f\r\n", lat_tentative);
2820 printf(
"Longitude : %f\r\n", lon_tentative);
2824 int positionLatency = bstr->
GetInt(95, 1);
2826 if ((lon_tentative <= 180.) &&
2830 ptd->Lon = lon_tentative;
2831 ptd->Lat = lat_tentative;
2832 ptd->b_positionDoubtful =
false;
2833 ptd->b_positionOnceValid =
true;
2834 if (positionLatency == 0) {
2837 printf(
"Low latency position report.\r\n");
2839 ptd->PositionReportTicks = now.GetTicks();
2842 ptd->b_positionDoubtful =
true;
2844 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
2845 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
2847 b_posn_report =
true;
2848 parse_result =
true;
2854 if (!ptd->b_isDSCtarget)
2855 ptd->Class = AIS_CLASS_A;
2861 int AIS_version_indicator = bstr->
GetInt(39, 2);
2862 if (AIS_version_indicator < 4) {
2863 ptd->IMO = bstr->
GetInt(41, 30);
2865 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
2866 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
2867 ptd->b_nameValid =
true;
2868 if (!ptd->b_isDSCtarget) {
2869 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
2872 ptd->DimA = bstr->
GetInt(241, 9);
2873 ptd->DimB = bstr->
GetInt(250, 9);
2874 ptd->DimC = bstr->
GetInt(259, 6);
2875 ptd->DimD = bstr->
GetInt(265, 6);
2877 ptd->ETA_Mo = bstr->
GetInt(275, 4);
2878 ptd->ETA_Day = bstr->
GetInt(279, 5);
2879 ptd->ETA_Hr = bstr->
GetInt(284, 5);
2880 ptd->ETA_Min = bstr->
GetInt(289, 6);
2882 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
2884 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
2886 ptd->StaticReportTicks = now.GetTicks();
2888 parse_result =
true;
2895 int part_number = bstr->
GetInt(39, 2);
2896 if (0 == part_number) {
2897 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
2898 ptd->b_nameValid =
true;
2899 parse_result =
true;
2901 }
else if (1 == part_number) {
2902 if (!ptd->b_isDSCtarget) {
2903 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
2905 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
2907 ptd->DimA = bstr->
GetInt(133, 9);
2908 ptd->DimB = bstr->
GetInt(142, 9);
2909 ptd->DimC = bstr->
GetInt(151, 6);
2910 ptd->DimD = bstr->
GetInt(157, 6);
2911 parse_result =
true;
2917 ptd->Class = AIS_BASE;
2919 ptd->m_utc_hour = bstr->
GetInt(62, 5);
2920 ptd->m_utc_min = bstr->
GetInt(67, 6);
2921 ptd->m_utc_sec = bstr->
GetInt(73, 6);
2923 int lon = bstr->
GetInt(80, 28);
2924 if (lon & 0x08000000)
2926 double lon_tentative = lon / 600000.;
2928 int lat = bstr->
GetInt(108, 27);
2929 if (lat & 0x04000000)
2931 double lat_tentative = lat / 600000.;
2933 if ((lon_tentative <= 180.) &&
2937 ptd->Lon = lon_tentative;
2938 ptd->Lat = lat_tentative;
2939 ptd->b_positionDoubtful =
false;
2940 ptd->b_positionOnceValid =
true;
2941 ptd->PositionReportTicks = now.GetTicks();
2943 ptd->b_positionDoubtful =
true;
2949 parse_result =
true;
2950 b_posn_report =
true;
2956 ptd->SOG = bstr->
GetInt(51, 10);
2958 int lon = bstr->
GetInt(62, 28);
2959 if (lon & 0x08000000)
2961 double lon_tentative = lon / 600000.;
2963 int lat = bstr->
GetInt(90, 27);
2964 if (lat & 0x04000000)
2966 double lat_tentative = lat / 600000.;
2968 if ((lon_tentative <= 180.) &&
2972 ptd->Lon = lon_tentative;
2973 ptd->Lat = lat_tentative;
2974 ptd->b_positionDoubtful =
false;
2975 ptd->b_positionOnceValid =
true;
2976 ptd->PositionReportTicks = now.GetTicks();
2978 ptd->b_positionDoubtful =
true;
2981 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
2983 int alt_tent = bstr->
GetInt(39, 12);
2984 ptd->altitude = alt_tent;
2986 ptd->b_SarAircraftPosnReport =
true;
2988 parse_result =
true;
2989 b_posn_report =
true;
2995 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
3001 ptd->DimA = bstr->
GetInt(220, 9);
3002 ptd->DimB = bstr->
GetInt(229, 9);
3003 ptd->DimC = bstr->
GetInt(238, 6);
3004 ptd->DimD = bstr->
GetInt(244, 6);
3007 ptd->m_utc_sec = bstr->
GetInt(254, 6);
3009 int offpos = bstr->
GetInt(260, 1);
3010 int virt = bstr->
GetInt(270, 1);
3013 ptd->NavStatus = ATON_VIRTUAL;
3015 ptd->NavStatus = ATON_REAL;
3016 if (ptd->m_utc_sec <= 59 ) {
3017 ptd->NavStatus += 1;
3018 if (offpos) ptd->NavStatus += 1;
3021 bstr->GetStr( 44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
3024 if (bstr->GetBitCount() > 276) {
3025 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
3026 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
3027 ptd->ShipNameExtension[14] = 0;
3029 ptd->ShipNameExtension[0] = 0;
3032 ptd->b_nameValid =
true;
3034 parse_result =
true;
3036 ptd->Class = AIS_ATON;
3038 int lon = bstr->
GetInt(165, 28);
3040 if (lon & 0x08000000)
3042 double lon_tentative = lon / 600000.;
3044 int lat = bstr->
GetInt(193, 27);
3046 if (lat & 0x04000000)
3048 double lat_tentative = lat / 600000.;
3050 if ((lon_tentative <= 180.) &&
3054 ptd->Lon = lon_tentative;
3055 ptd->Lat = lat_tentative;
3056 ptd->b_positionDoubtful =
false;
3057 ptd->b_positionOnceValid =
true;
3058 ptd->PositionReportTicks = now.GetTicks();
3060 ptd->b_positionDoubtful =
true;
3062 b_posn_report =
true;
3067 int dac = bstr->
GetInt(41, 10);
3068 int fi = bstr->
GetInt(51, 6);
3073 ptd->b_isEuroInland =
true;
3075 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
3076 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
3077 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
3078 ptd->UN_shiptype = bstr->
GetInt(128, 14);
3079 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
3080 parse_result =
true;
3087 if (bstr->GetBitCount() >= 111) {
3089 an.link_id = bstr->
GetInt(57, 10);
3090 an.notice_type = bstr->
GetInt(67, 7);
3091 an.month = bstr->
GetInt(74, 4);
3092 an.day = bstr->
GetInt(78, 5);
3093 an.hour = bstr->
GetInt(83, 5);
3094 an.minute = bstr->
GetInt(88, 6);
3095 an.duration_minutes = bstr->
GetInt(94, 18);
3097 wxDateTime now = wxDateTime::Now();
3100 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3101 now.GetYear(), an.hour, an.minute);
3106 if (an.start_time > now + wxTimeSpan::Hours(48))
3107 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3108 now.GetYear() - 1, an.hour, an.minute);
3111 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3116 if (an.expiry_time < now - wxTimeSpan::Hours(24)) {
3117 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
3118 now.GetYear() + 1, an.hour, an.minute);
3120 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
3123 int subarea_count = (bstr->GetBitCount() - 111) / 87;
3124 for (
int i = 0; i < subarea_count; ++i) {
3125 int base = 111 + i * 87;
3127 sa.shape = bstr->
GetInt(base + 1, 3);
3128 int scale_factor = 1;
3129 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
3132 bstr->GetStr(base + 4, 84, t, 14);
3133 sa.text = wxString(t, wxConvUTF8);
3135 int scale_multipliers[4] = {1, 10, 100, 1000};
3136 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
3138 case AIS8_001_22_SHAPE_CIRCLE:
3139 case AIS8_001_22_SHAPE_SECTOR:
3140 sa.radius_m = bstr->
GetInt(base + 58, 12) * scale_factor;
3142 case AIS8_001_22_SHAPE_RECT:
3143 sa.longitude = bstr->
GetInt(base + 6, 25,
true) / 60000.0;
3144 sa.latitude = bstr->
GetInt(base + 31, 24,
true) / 60000.0;
3146 case AIS8_001_22_SHAPE_POLYLINE:
3147 case AIS8_001_22_SHAPE_POLYGON:
3148 for (
int i = 0; i < 4; ++i) {
3149 sa.angles[i] = bstr->
GetInt(base + 6 + i * 20, 10) * 0.5;
3151 bstr->
GetInt(base + 16 + i * 20, 10) * scale_factor;
3154 if (sa.shape == AIS8_001_22_SHAPE_RECT) {
3155 sa.e_dim_m = bstr->
GetInt(base + 58, 8) * scale_factor;
3156 sa.n_dim_m = bstr->
GetInt(base + 66, 8) * scale_factor;
3157 sa.orient_deg = bstr->
GetInt(base + 74, 9);
3159 if (sa.shape == AIS8_001_22_SHAPE_SECTOR) {
3160 sa.left_bound_deg = bstr->
GetInt(70, 9);
3161 sa.right_bound_deg = bstr->
GetInt(79, 9);
3164 an.sub_areas.push_back(sa);
3166 ptd->area_notices[an.link_id] = an;
3167 parse_result =
true;
3176 char msg_14_text[968];
3177 if (bstr->GetBitCount() > 40) {
3178 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
3179 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
3181 nd = wxMin(nd, 967);
3182 msg_14_text[nd] = 0;
3183 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
3185 parse_result =
true;
3203 if (b_posn_report) ptd->b_lost =
false;
3205 if (
true == parse_result) {
3207 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
3208 ptd->b_active =
true;
3211 return parse_result;
3214bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3215 unsigned char checksum_value = 0;
3216 int sentence_hex_sum;
3218 wxCharBuffer buf = str_in.ToUTF8();
3219 if (!buf.data())
return false;
3221 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3222 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3223 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3225 int string_length = strlen(str_ascii);
3227 int payload_length = 0;
3228 while ((payload_length < string_length) &&
3229 (str_ascii[payload_length] !=
'*'))
3232 if (payload_length == string_length)
3237 while (index < payload_length) {
3238 checksum_value ^= str_ascii[index];
3242 if (string_length > 4) {
3244 scanstr[0] = str_ascii[payload_length + 1];
3245 scanstr[1] = str_ascii[payload_length + 2];
3247 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3249 if (sentence_hex_sum == checksum_value)
return true;
3255void AisDecoder::UpdateAllCPA(
void) {
3257 for (
const auto &it : GetTargetList()) {
3258 std::shared_ptr<AisTargetData> td = it.second;
3260 if (NULL != td) UpdateOneCPA(td.get());
3264void AisDecoder::UpdateAllTracks(
void) {
3266 for (
const auto &it : GetTargetList()) {
3267 std::shared_ptr<AisTargetData> td = it.second;
3269 if (NULL != td) UpdateOneTrack(td.get());
3274 if (!ptarget->b_positionOnceValid)
return;
3276 if (ptarget->m_ptrack.size() > 0) {
3278 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3279 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3282 ptarget->m_ptrack.pop_back();
3283 ptarget->b_positionDoubtful =
true;
3290 ptrackpoint.m_lat = ptarget->Lat;
3291 ptrackpoint.m_lon = ptarget->Lon;
3292 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3294 ptarget->m_ptrack.push_back(ptrackpoint);
3296 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3298 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3300 t->SetName(wxString::Format(_T(
"AIS %s (%u) %s %s"),
3301 ptarget->GetFullName().c_str(), ptarget->MMSI,
3302 wxDateTime::Now().FormatISODate().c_str(),
3303 wxDateTime::Now().FormatISOTime().c_str()));
3304 g_TrackList.push_back(t);
3306 m_persistent_tracks[ptarget->MMSI] = t;
3308 t = m_persistent_tracks[ptarget->MMSI];
3311 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3313 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3330 wxDateTime::Now().GetTicks() - (time_t)( g_AISShowTracks_Mins * 60 );
3332 ptarget->m_ptrack.erase(
3333 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3335 return track.m_time < test_time;
3337 ptarget->m_ptrack.end());
3341void AisDecoder::DeletePersistentTrack(
Track *track) {
3342 for (std::map<int, Track *>::iterator iterator = m_persistent_tracks.begin();
3343 iterator != m_persistent_tracks.end(); iterator++) {
3344 if (iterator->second == track) {
3345 int mmsi = iterator->first;
3346 m_persistent_tracks.erase(iterator);
3348 if (0 == m_persistent_tracks.count(mmsi)) {
3349 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3350 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
3352 if (props->m_bPersistentTrack) {
3356 std::shared_ptr<AisTargetData> td = Get_Target_Data_From_MMSI(mmsi);
3358 props->m_bPersistentTrack =
false;
3359 td->b_mPropPersistTrack =
false;
3361 if (!m_callbacks.confirm_stop_track()) {
3362 props->m_bPersistentTrack =
true;
3374void AisDecoder::UpdateAllAlarms(
void) {
3375 m_bGeneralAlert =
false;
3378 for (
const auto &it : GetTargetList()) {
3379 std::shared_ptr <AisTargetData> td = it.second;
3383 if (!m_bGeneralAlert) {
3385 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3386 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3387 m_bGeneralAlert =
true;
3390 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3391 m_bGeneralAlert =
false;
3394 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3395 m_bGeneralAlert =
false;
3398 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3401 if (td->Class == AIS_SART && td->NavStatus == 14)
3402 m_bGeneralAlert =
true;
3405 if (( td->Class == AIS_DSC ) && ( ( td->ShipType == 12 ) || ( td->ShipType == 16 ) ))
3406 m_bGeneralAlert =
true;
3409 ais_alert_type this_alarm = AIS_NO_ALERT;
3412 if (td->Class == AIS_SART && td->NavStatus == 14)
3413 this_alarm = AIS_ALERT_SET;
3416 if ((td->Class == AIS_DSC) && (( td->ShipType == 12 ) || ( td->ShipType == 16 )) )
3417 this_alarm = AIS_ALERT_SET;
3419 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3420 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3423 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3424 td->n_alert_state = AIS_NO_ALERT;
3430 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3431 (td->SOG <= g_ShowMoored_Kts)) {
3432 td->n_alert_state = AIS_NO_ALERT;
3438 if (td->Range_NM > g_CPAMax_NM) {
3439 td->n_alert_state = AIS_NO_ALERT;
3444 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3445 (td->Class != AIS_ATON) && (td->Class != AIS_BASE)) {
3447 if (td->TCPA < g_TCPA_Max) {
3448 if (td->b_isFollower)
3449 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3451 this_alarm = AIS_ALERT_SET;
3454 if (td->b_isFollower)
3455 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3457 this_alarm = AIS_ALERT_SET;
3465 if (g_bAIS_ACK_Timeout || (td->Class == AIS_SART) ||
3466 ((td->Class == AIS_DSC) && ((td->ShipType == 12) || (td->ShipType == 16)) )) {
3467 if (td->b_in_ack_timeout) {
3468 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3469 if (delta.GetMinutes() > g_AckTimeout_Mins)
3470 td->b_in_ack_timeout =
false;
3476 if (td->b_in_ack_timeout) {
3477 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
3481 td->n_alert_state = this_alarm;
3487 ptarget->Range_NM = -1.;
3496 DistanceBearingMercator(ptarget->Lat, ptarget->Lon, gLat, gLon, &brg, &dist);
3497 ptarget->Range_NM = dist;
3500 if (dist <= 1e-5) ptarget->Brg = -1.0;
3502 if (!ptarget->b_positionOnceValid || !bGPSValid) {
3503 ptarget->bCPA_Valid =
false;
3512 if (ptarget->b_OwnShip) {
3514 ptarget->TCPA = -100;
3515 ptarget->bCPA_Valid =
false;
3519 double cpa_calc_ownship_cog = gCog;
3520 double cpa_calc_target_cog = ptarget->COG;
3523 if (std::isnan(gSog) || (gSog > 102.2)) {
3524 ptarget->bCPA_Valid =
false;
3529 if (std::isnan(gCog) || gCog == 360.0) {
3531 cpa_calc_ownship_cog =
3535 ptarget->bCPA_Valid =
false;
3541 if (ptarget->COG == 360.0) {
3542 if (ptarget->SOG > 102.2) {
3543 ptarget->bCPA_Valid =
false;
3545 }
else if (ptarget->SOG < .01)
3546 cpa_calc_target_cog =
3550 ptarget->bCPA_Valid =
false;
3556 double v0 = gSog * 1852.;
3557 double v1 = ptarget->SOG * 1852.;
3559 if ((v0 < 1e-6) && (v1 < 1e-6)) {
3563 ptarget->bCPA_Valid =
false;
3570 double east1 = (ptarget->Lon - gLon) * 60 * 1852;
3571 double north1 = (ptarget->Lat - gLat) * 60 * 1852;
3573 double east = east1 * (cos(gLat * PI / 180.));
3575 double north = north1;
3578 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
3579 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
3580 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
3581 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
3584 double fc = (v0 * cosa) - (v1 * cosb);
3585 double fs = (v0 * sina) - (v1 * sinb);
3587 double d = (fc * fc) + (fs * fs);
3595 tcpa = ((fc * east) + (fs * north)) / d;
3598 ptarget->TCPA = tcpa * 60.;
3603 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
3605 ll_gc_ll(gLat, gLon, cpa_calc_ownship_cog, gSog * tcpa, &OwnshipLatCPA,
3607 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
3608 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
3611 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
3614 ptarget->bCPA_Valid =
true;
3616 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
3620void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
3623 if (m_ptentative_dsctarget) {
3624 ProcessDSx(m_dsc_last_string,
true);
3628void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
3633 wxDateTime now = wxDateTime::Now();
3636 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets = GetTargetList();
3638 auto it = current_targets.begin();
3639 std::vector<int> remove_array;
3641 while (it != current_targets.end()) {
3642 if (it->second == NULL)
3644 current_targets.erase(it);
3648 auto xtd = it->second;
3650 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
3651 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
3663 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
3665 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
3666 double iECD_LostTimeOut = 0.0;
3670 if (xtd->Class == AIS_CLASS_B) {
3671 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR))
3672 iECD_LostTimeOut = 18 * 60;
3674 iECD_LostTimeOut = 180;
3676 if (xtd->Class == AIS_CLASS_A) {
3677 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
3679 iECD_LostTimeOut = 18 * 60;
3681 iECD_LostTimeOut = 60;
3683 iECD_LostTimeOut = 60;
3686 if ((target_posn_age > iECD_LostTimeOut) && (xtd->Class != AIS_GPSG_BUDDY))
3687 xtd->b_active =
false;
3689 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
3690 }
else if (g_bMarkLost) {
3691 if ((target_posn_age > g_MarkLost_Mins * 60) &&
3692 (xtd->Class != AIS_GPSG_BUDDY))
3693 xtd->b_active =
false;
3696 if (xtd->Class == AIS_SART) removelost_Mins = 18.0;
3700 if (g_bRemoveLost || g_bInlandEcdis) {
3702 (xtd->Class == AIS_ARPA &&
3704 if (((target_posn_age > removelost_Mins * 60) &&
3705 (xtd->Class != AIS_GPSG_BUDDY)) ||
3710 xtd->b_positionOnceValid =
false;
3718 long mmsi_long = xtd->MMSI;
3719 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3724 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
3725 xtd->b_removed =
true;
3727 remove_array.push_back(xtd->MMSI);
3734 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3736 if (xtd->MMSI == props->MMSI) {
3737 if (props->m_bignore) {
3738 remove_array.push_back(xtd->MMSI);
3739 xtd->b_removed =
true;
3750 for (
unsigned int i = 0; i < remove_array.size(); i++) {
3751 auto itd = current_targets.find(remove_array[i]);
3752 if (itd != current_targets.end()) {
3753 auto td = itd->second;
3754 current_targets.erase(itd);
3763 m_bSuppressed =
false;
3764 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
3765 (g_bShowScaled && g_bAllowShowScaled))
3766 m_bSuppressed =
true;
3768 m_bAIS_Audio_Alert_On =
false;
3777 std::shared_ptr<AisTargetData> palert_target = NULL;
3778 int audioType = AISAUDIO_NONE;
3780 if (NULL == g_pais_alert_dialog_active) {
3781 pAISMOBRoute = NULL;
3782 double tcpa_min = 1e6;
3783 double sart_range = 1e6;
3784 std::shared_ptr<AisTargetData> palert_target_cpa = NULL;
3785 std::shared_ptr<AisTargetData> palert_target_sart = NULL;
3786 std::shared_ptr<AisTargetData> palert_target_dsc = NULL;
3788 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
3789 auto td = it->second;
3791 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3792 if (g_bAIS_CPA_Alert && td->b_active) {
3793 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
3794 if (td->TCPA < tcpa_min) {
3795 tcpa_min = td->TCPA;
3796 palert_target_cpa = td;
3800 }
else if ((td->Class == AIS_DSC) && ((td->ShipType == 12) || (td->ShipType == 16)) ) {
3802 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
3803 palert_target_dsc = td;
3806 td->b_isDSCtarget =
false;
3811 else if (td->Class == AIS_SART) {
3813 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
3814 if (td->Range_NM < sart_range) {
3815 tcpa_min = sart_range;
3816 palert_target_sart = td;
3826 palert_target = palert_target_cpa;
3827 if (palert_target) audioType = AISAUDIO_CPA;
3829 if (palert_target_sart) {
3830 palert_target = palert_target_sart;
3831 audioType = AISAUDIO_SART;
3834 if (palert_target_dsc) {
3835 palert_target = palert_target_dsc;
3836 audioType = AISAUDIO_DSC;
3840 palert_target = Get_Target_Data_From_MMSI(
3841 g_pais_alert_dialog_active->Get_Dialog_MMSI());
3847 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
3850std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
int mmsi) {
3851 if (AISTargetList.find(mmsi) == AISTargetList.end())
3854 return AISTargetList[mmsi];
3857ArrayOfMmsiProperties g_MMSI_Props_Array;
3861MmsiProperties::MmsiProperties(wxString &spec) {
3863 wxStringTokenizer tkz(spec, _T(
";"));
3866 s = tkz.GetNextToken();
3871 s = tkz.GetNextToken();
3873 if (s.Upper() == _T(
"ALWAYS"))
3874 TrackType = TRACKTYPE_ALWAYS;
3875 else if (s.Upper() == _T(
"NEVER"))
3876 TrackType = TRACKTYPE_NEVER;
3879 s = tkz.GetNextToken();
3881 if (s.Upper() == _T(
"IGNORE")) m_bignore =
true;
3884 s = tkz.GetNextToken();
3886 if (s.Upper() == _T(
"MOB")) m_bMOB =
true;
3889 s = tkz.GetNextToken();
3891 if (s.Upper() == _T(
"VDM")) m_bVDM =
true;
3894 s = tkz.GetNextToken();
3896 if (s.Upper() == _T(
"FOLLOWER")) m_bFollower =
true;
3899 s = tkz.GetNextToken();
3901 if (s.Upper() == _T(
"PERSIST")) m_bPersistentTrack =
true;
3904 s = tkz.GetNextToken();
3906 m_ShipName = s.Upper();
3910MmsiProperties::~MmsiProperties() {}
3912void MmsiProperties::Init(
void) {
3914 TrackType = TRACKTYPE_DEFAULT;
3918 m_bFollower =
false;
3919 m_bPersistentTrack =
false;
3920 m_ShipName = wxEmptyString;
3923wxString MmsiProperties::Serialize(
void) {
3927 sMMSI.Printf(_T(
"%d"), MMSI);
3931 if (TRACKTYPE_ALWAYS == TrackType)
3932 sMMSI << _T(
"always");
3933 else if (TRACKTYPE_NEVER == TrackType)
3934 sMMSI << _T(
"never");
3939 sMMSI << _T(
"ignore");
3954 sMMSI << _T(
"Follower");
3958 if (m_bPersistentTrack) {
3959 sMMSI << _T(
"PERSIST");
3963 if (m_ShipName == wxEmptyString) {
3964 m_ShipName = GetShipNameFromFile(MMSI);
3966 sMMSI << m_ShipName;
3971 AIS_Target_Name_Hash *AISTargetNamesC,
3972 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
3973 if (g_benableAISNameCache) {
3974 wxString ship_name = wxEmptyString;
3977 if (!pTargetData->b_nameValid) {
3978 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
3979 if (it != AISTargetNamesC->end()) {
3980 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
3981 strncpy(pTargetData->ShipName, ship_name.mb_str(),
3982 ship_name.length() + 1);
3983 pTargetData->b_nameValid =
true;
3984 pTargetData->b_nameFromCache =
true;
3985 }
else if (!g_bUseOnlyConfirmedAISName) {
3986 it = AISTargetNamesNC->find(mmsi);
3987 if (it != AISTargetNamesNC->end()) {
3988 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
3989 strncpy(pTargetData->ShipName, ship_name.mb_str(),
3990 ship_name.length() + 1);
3991 pTargetData->b_nameValid =
true;
3992 pTargetData->b_nameFromCache =
true;
3997 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
3998 (pTargetData->MID == 19) ||
3999 (pTargetData->MID == 123) ||
4000 (pTargetData->MID == 124) ) {
4002 pTargetData->b_nameFromCache =
false;
4003 ship_name = trimAISField(pTargetData->ShipName);
4004 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4005 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4007 AISTargetNamesC->end()) {
4008 if ((*AISTargetNamesC)[mmsi] ==
4010 if (itNC != AISTargetNamesNC->end()) {
4012 AISTargetNamesNC->erase(itNC);
4015 if (itNC != AISTargetNamesNC->end()) {
4017 if ((*AISTargetNamesNC)[mmsi] ==
4020 (*AISTargetNamesC)[mmsi] = ship_name;
4022 AISTargetNamesNC->erase(itNC);
4025 (*AISTargetNamesNC)[mmsi] = ship_name;
4027 if (g_bUseOnlyConfirmedAISName)
4028 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4029 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4031 (*AISTargetNamesNC)[mmsi] = ship_name;
4036 AISTargetNamesNC->end()) {
4037 if ((*AISTargetNamesNC)[mmsi] ==
4040 (*AISTargetNamesC)[mmsi] = ship_name;
4042 AISTargetNamesNC->erase(itNC);
4044 (*AISTargetNamesNC)[mmsi] = ship_name;
4047 (*AISTargetNamesNC)[mmsi] = ship_name;
4049 if (g_bUseOnlyConfirmedAISName) {
4050 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4051 strncpy(pTargetData->ShipName,
"Unknown ", SHIP_NAME_LEN - 1);
4058wxString GetShipNameFromFile(
int nmmsi) {
4059 wxString name = wxEmptyString;
4060 if (g_benableAISNameCache) {
4061 std::ifstream infile(AISTargetNameFileName.mb_str());
4064 while (getline(infile, line)) {
4065 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()), _T(
","));
4066 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4067 name = tokenizer.GetNextToken().Trim();
4070 tokenizer.GetNextToken();
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.
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, raw SignalK message.