41#include <wx/datetime.h>
45#include <wx/textfile.h>
47#include <wx/tokenzr.h>
48#include <wx/filename.h>
52#include "rapidjson/document.h"
53#include "rapidjson/writer.h"
54#include "rapidjson/stringbuffer.h"
62#include "model/geodesic.h"
74static const long long lNaN = 0xfff8000000000000;
75#define NAN (*(double *)&lNaN)
78wxEvtHandler *g_pais_alert_dialog_active;
82wxString GetShipNameFromFile(
int);
106EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
107EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
110static constexpr
double ms_to_knot_factor = 1.9438444924406;
116static
bool b_firstrx;
117static
int first_rx_ticks;
119static
double arpa_ref_hdg = NAN;
121static inline
double GeodesicRadToDeg(
double rads) {
122 return rads * 180.0 / M_PI;
125static inline double MS2KNOTS(
double ms) {
return ms * 1.9438444924406; }
127static bool isBuoyMmsi(
unsigned msi) {
132 int mid = msi / 1000000;
133 if ((mid > 200 && mid < 880) || mid >= 970) {
142int AisMeteoNewMmsi(
int,
int,
int,
int,
int);
146 AIS_Target_Name_Hash *AISTargetNamesC,
147 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
148static void BuildERIShipTypeHash() {
149 make_hash_ERI(8000, _(
"Vessel, type unknown"));
150 make_hash_ERI(8150, _(
"Freightbarge"));
151 make_hash_ERI(8160, _(
"Tankbarge"));
152 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
153 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
154 make_hash_ERI(8430, _(
"Pushboat, single"));
155 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
156 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
157 make_hash_ERI(8490, _(
"Bunkership"));
158 make_hash_ERI(8010, _(
"Motor freighter"));
159 make_hash_ERI(8020, _(
"Motor tanker"));
160 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
161 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
162 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
163 make_hash_ERI(8030, _(
"Container vessel"));
164 make_hash_ERI(8040, _(
"Gas tanker"));
165 make_hash_ERI(8050, _(
"Motor freighter, tug"));
166 make_hash_ERI(8060, _(
"Motor tanker, tug"));
167 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
168 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
169 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
170 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
171 make_hash_ERI(8110, _(
"Tug, freighter"));
172 make_hash_ERI(8120, _(
"Tug, tanker"));
173 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
174 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
175 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
176 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
177 make_hash_ERI(8170, _(
"Freightbarge with containers"));
178 make_hash_ERI(8180, _(
"Tankbarge, gas"));
179 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
180 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
181 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
182 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
183 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
184 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
185 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
186 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
187 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
188 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
190 _(
"Pushtow, two barges at least one tanker or gas barge"));
192 _(
"Pushtow, three barges at least one tanker or gas barge"));
194 _(
"Pushtow, four barges at least one tanker or gas barge"));
196 _(
"Pushtow, five barges at least one tanker or gas barge"));
198 _(
"Pushtow, six barges at least one tanker or gas barge"));
200 _(
"Pushtow, seven barges at least one tanker or gas barge"));
202 _(
"Pushtow, eight barges at least one tanker or gas barge"));
204 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
205 make_hash_ERI(8400, _(
"Tug, single"));
206 make_hash_ERI(8410, _(
"Tug, one or more tows"));
207 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
208 make_hash_ERI(8430, _(
"Pushboat, single"));
209 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
210 make_hash_ERI(8441, _(
"Ferry"));
211 make_hash_ERI(8442, _(
"Red cross ship"));
212 make_hash_ERI(8443, _(
"Cruise ship"));
213 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
214 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
215 "cable-ship, buoy-ship, dredge"));
216 make_hash_ERI(8480, _(
"Fishing boat"));
217 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
218 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
219 make_hash_ERI(1510, _(
"Unit carrier maritime"));
220 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
221 make_hash_ERI(1530, _(
"Tanker"));
222 make_hash_ERI(1540, _(
"Liquified gas tanker"));
223 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
224 make_hash_ERI(1900, _(
"Fast ship"));
225 make_hash_ERI(1910, _(
"Hydrofoil"));
232static wxString DecodeDSEExpansionCharacters(
const wxString &dseData) {
233 static constexpr std::string_view lookupTable =
234 "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-/ ";
236 bool invalid =
false;
238 for (
size_t i = 0; i < dseData.length(); i += 2) {
239 wxString pair = dseData.Mid(i, 2);
240 if (pair.length() != 2 || !wxIsdigit(pair[0]) || !wxIsdigit(pair[1])) {
244 size_t code = (pair[0] -
'0') * 10 + (pair[1] -
'0');
245 if (code >= lookupTable.size()) {
249 result.append(1, lookupTable[code]);
253 "DSE station information with invalid expansion data ignored "
260static void getMmsiProperties(std::shared_ptr<AisTargetData> &pTargetData) {
264 pTargetData->b_isFollower = props->m_bFollower;
265 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
266 if (TRACKTYPE_NEVER == props->TrackType) {
267 pTargetData->b_show_track =
false;
268 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
269 pTargetData->b_show_track =
true;
280 const std::shared_ptr<AisTargetData> &ptd) {
281 bool parse_result =
false;
282 bool b_posn_report =
false;
284 wxDateTime now = wxDateTime::Now();
286 int message_ID = bstr->
GetInt(1, 6);
287 ptd->MID = message_ID;
290 int met_mmsi = ptd->MMSI;
293 ptd->MMSI = bstr->
GetInt(9, 30);
295 switch (message_ID) {
301 ptd->NavStatus = bstr->
GetInt(39, 4);
302 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
304 int lon = bstr->
GetInt(62, 28);
305 if (lon & 0x08000000)
307 double lon_tentative = lon / 600000.;
309 int lat = bstr->
GetInt(90, 27);
310 if (lat & 0x04000000)
312 double lat_tentative = lat / 600000.;
314 if ((lon_tentative <= 180.) &&
318 ptd->Lon = lon_tentative;
319 ptd->Lat = lat_tentative;
320 ptd->b_positionDoubtful =
false;
321 ptd->b_positionOnceValid =
true;
322 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
323 ptd->PositionReportTicks = now.GetTicks();
325 ptd->b_positionDoubtful =
true;
328 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
329 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
331 ptd->ROTAIS = bstr->
GetInt(43, 8);
332 double rot_dir = 1.0;
334 if (ptd->ROTAIS == 128)
336 else if ((ptd->ROTAIS & 0x80) == 0x80) {
337 ptd->ROTAIS = ptd->ROTAIS - 256;
341 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
344 ptd->m_utc_sec = bstr->
GetInt(138, 6);
346 if ((1 == message_ID) ||
349 ptd->SyncState = bstr->
GetInt(151, 2);
350 ptd->SlotTO = bstr->
GetInt(153, 2);
351 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
353 ptd->m_utc_hour = bstr->
GetInt(155, 5);
355 ptd->m_utc_min = bstr->
GetInt(160, 7);
357 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
358 (ptd->m_utc_sec < 60)) {
359 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
360 rx_ticks = rx_time.GetTicks();
362 first_rx_ticks = rx_ticks;
370 ptd->blue_paddle = bstr->
GetInt(144, 2);
371 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
373 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
376 int mmsi_start = ptd->MMSI / 10000000;
378 if (mmsi_start == 97) {
379 ptd->Class = AIS_SART;
380 ptd->StaticReportTicks =
395 b_posn_report =
true;
396 ptd->b_VDM_secure_Pos =
true;
405 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
407 int lon = bstr->
GetInt(58, 28);
408 if (lon & 0x08000000)
410 double lon_tentative = lon / 600000.;
412 int lat = bstr->
GetInt(86, 27);
413 if (lat & 0x04000000)
415 double lat_tentative = lat / 600000.;
417 if ((lon_tentative <= 180.) &&
421 ptd->Lon = lon_tentative;
422 ptd->Lat = lat_tentative;
423 ptd->b_positionDoubtful =
false;
424 ptd->b_positionOnceValid =
true;
425 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
426 ptd->PositionReportTicks = now.GetTicks();
428 ptd->b_positionDoubtful =
true;
430 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
431 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
433 ptd->m_utc_sec = bstr->
GetInt(134, 6);
435 if (!ptd->b_isDSCtarget) {
436 if (!isBuoyMmsi(ptd->MMSI))
437 ptd->Class = AIS_CLASS_B;
439 ptd->Class = AIS_BUOY;
442 b_posn_report =
true;
443 ptd->b_VDM_secure_Pos =
true;
451 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
452 int lon = bstr->
GetInt(58, 28);
453 if (lon & 0x08000000)
455 double lon_tentative = lon / 600000.;
457 int lat = bstr->
GetInt(86, 27);
458 if (lat & 0x04000000)
460 double lat_tentative = lat / 600000.;
462 if ((lon_tentative <= 180.) &&
466 ptd->Lon = lon_tentative;
467 ptd->Lat = lat_tentative;
468 ptd->b_positionDoubtful =
false;
469 ptd->b_positionOnceValid =
true;
470 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
471 ptd->PositionReportTicks = now.GetTicks();
473 ptd->b_positionDoubtful =
true;
475 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
476 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
477 ptd->m_utc_sec = bstr->
GetInt(134, 6);
479 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
480 ptd->b_nameValid =
true;
481 if (!ptd->b_isDSCtarget) {
482 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
484 ptd->DimA = bstr->
GetInt(272, 9);
485 ptd->DimB = bstr->
GetInt(281, 9);
486 ptd->DimC = bstr->
GetInt(290, 6);
487 ptd->DimD = bstr->
GetInt(296, 6);
489 if (!ptd->b_isDSCtarget) {
491 if (!isBuoyMmsi(ptd->MMSI))
492 ptd->Class = AIS_CLASS_B;
494 ptd->Class = AIS_BUOY;
497 b_posn_report =
true;
498 ptd->b_VDM_secure_Pos =
true;
510 if (ptd->b_VDM_secure_Pos)
break;
515 int bitCorrection = 10;
519 double lon_tentative = 181.;
520 double lat_tentative = 91.;
523 printf(
"AIS Message 27 - received:\r\n");
524 printf(
"MMSI : %i\r\n", ptd->MMSI);
530 ptd->Class == AIS_CLASS_A ? bstr->
GetInt(41, 4) : UNDEFINED;
532 int lon = bstr->
GetInt(45, 18);
533 int lat = bstr->
GetInt(63, 17);
539 if (lat >= (0x4000000 >> bitCorrection)) {
540 lat_tentative = (0x8000000 >> bitCorrection) - lat;
545 if (lon >= (0x8000000 >> bitCorrection)) {
546 lon_tentative = (0x10000000 >> bitCorrection) - lon;
551 lat_tentative = lat_tentative / resolution / 60.0;
552 lon_tentative = lon_tentative / resolution / 60.0;
555 printf(
"Latitude : %f\r\n", lat_tentative);
556 printf(
"Longitude : %f\r\n", lon_tentative);
560 int positionLatency = bstr->
GetInt(95, 1);
562 if ((lon_tentative <= 180.) &&
566 ptd->Lon = lon_tentative;
567 ptd->Lat = lat_tentative;
568 ptd->b_positionDoubtful =
false;
569 ptd->b_positionOnceValid =
true;
570 if (positionLatency == 0) {
573 printf(
"Low latency position report.\r\n");
575 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
576 ptd->PositionReportTicks = now.GetTicks();
579 ptd->b_positionDoubtful =
true;
581 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
582 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
584 b_posn_report =
true;
590 if (bstr->GetBitCount() >= 424) {
592 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
598 int AIS_version_indicator = bstr->
GetInt(39, 2);
599 if (AIS_version_indicator < 4) {
600 ptd->IMO = bstr->
GetInt(41, 30);
602 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
603 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
604 ptd->b_nameValid =
true;
605 if (!ptd->b_isDSCtarget) {
606 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
609 ptd->DimA = bstr->
GetInt(241, 9);
610 ptd->DimB = bstr->
GetInt(250, 9);
611 ptd->DimC = bstr->
GetInt(259, 6);
612 ptd->DimD = bstr->
GetInt(265, 6);
614 ptd->ETA_Mo = bstr->
GetInt(275, 4);
615 ptd->ETA_Day = bstr->
GetInt(279, 5);
616 ptd->ETA_Hr = bstr->
GetInt(284, 5);
617 ptd->ETA_Min = bstr->
GetInt(289, 6);
619 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
621 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
623 ptd->StaticReportTicks = now.GetTicks();
634 if (!ptd->b_positionOnceValid)
break;
636 int part_number = bstr->
GetInt(39, 2);
637 if (0 == part_number) {
638 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
639 ptd->b_nameValid =
true;
642 }
else if (1 == part_number) {
643 if (!ptd->b_isDSCtarget) {
644 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
646 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
648 ptd->DimA = bstr->
GetInt(133, 9);
649 ptd->DimB = bstr->
GetInt(142, 9);
650 ptd->DimC = bstr->
GetInt(151, 6);
651 ptd->DimD = bstr->
GetInt(157, 6);
658 ptd->Class = AIS_BASE;
660 ptd->m_utc_hour = bstr->
GetInt(62, 5);
661 ptd->m_utc_min = bstr->
GetInt(67, 6);
662 ptd->m_utc_sec = bstr->
GetInt(73, 6);
664 int lon = bstr->
GetInt(80, 28);
665 if (lon & 0x08000000)
667 double lon_tentative = lon / 600000.;
669 int lat = bstr->
GetInt(108, 27);
670 if (lat & 0x04000000)
672 double lat_tentative = lat / 600000.;
674 if ((lon_tentative <= 180.) &&
678 ptd->Lon = lon_tentative;
679 ptd->Lat = lat_tentative;
680 ptd->b_positionDoubtful =
false;
681 ptd->b_positionOnceValid =
true;
682 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
683 ptd->PositionReportTicks = now.GetTicks();
685 ptd->b_positionDoubtful =
true;
692 b_posn_report =
true;
698 ptd->SOG = bstr->
GetInt(51, 10);
700 int lon = bstr->
GetInt(62, 28);
701 if (lon & 0x08000000)
703 double lon_tentative = lon / 600000.;
705 int lat = bstr->
GetInt(90, 27);
706 if (lat & 0x04000000)
708 double lat_tentative = lat / 600000.;
710 if ((lon_tentative <= 180.) &&
714 ptd->Lon = lon_tentative;
715 ptd->Lat = lat_tentative;
716 ptd->b_positionDoubtful =
false;
717 ptd->b_positionOnceValid =
true;
718 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
719 ptd->PositionReportTicks = now.GetTicks();
721 ptd->b_positionDoubtful =
true;
724 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
726 int alt_tent = bstr->
GetInt(39, 12);
727 ptd->altitude = alt_tent;
729 ptd->b_SarAircraftPosnReport =
true;
732 b_posn_report =
true;
738 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
744 ptd->DimA = bstr->
GetInt(220, 9);
745 ptd->DimB = bstr->
GetInt(229, 9);
746 ptd->DimC = bstr->
GetInt(238, 6);
747 ptd->DimD = bstr->
GetInt(244, 6);
750 ptd->m_utc_sec = bstr->
GetInt(254, 6);
752 int offpos = bstr->
GetInt(260, 1);
753 int virt = bstr->
GetInt(270, 1);
756 ptd->NavStatus = ATON_VIRTUAL;
758 ptd->NavStatus = ATON_REAL;
759 if (ptd->m_utc_sec <= 59 ) {
761 if (offpos) ptd->NavStatus += 1;
764 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
767 if (bstr->GetBitCount() > 276) {
768 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
769 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
770 ptd->ShipNameExtension[14] = 0;
772 ptd->ShipNameExtension[0] = 0;
775 ptd->b_nameValid =
true;
779 ptd->Class = AIS_ATON;
781 int lon = bstr->
GetInt(165, 28);
783 if (lon & 0x08000000)
785 double lon_tentative = lon / 600000.;
787 int lat = bstr->
GetInt(193, 27);
789 if (lat & 0x04000000)
791 double lat_tentative = lat / 600000.;
793 if ((lon_tentative <= 180.) &&
797 ptd->Lon = lon_tentative;
798 ptd->Lat = lat_tentative;
799 ptd->b_positionDoubtful =
false;
800 ptd->b_positionOnceValid =
true;
801 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
802 ptd->PositionReportTicks = now.GetTicks();
804 ptd->b_positionDoubtful =
true;
806 b_posn_report =
true;
811 int dac = bstr->
GetInt(41, 10);
812 int fi = bstr->
GetInt(51, 6);
818 ptd->b_isEuroInland =
true;
820 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
821 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
822 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
823 ptd->UN_shiptype = bstr->
GetInt(128, 14);
824 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
828 if (dac == 1 || dac == 366)
832 if (bstr->GetBitCount() >= 111) {
834 an.link_id = bstr->
GetInt(57, 10);
835 an.notice_type = bstr->
GetInt(67, 7);
836 an.month = bstr->
GetInt(74, 4);
837 an.day = bstr->
GetInt(78, 5);
838 an.hour = bstr->
GetInt(83, 5);
839 an.minute = bstr->
GetInt(88, 6);
840 an.duration_minutes = bstr->
GetInt(94, 18);
842 wxDateTime now_ = wxDateTime::Now();
845 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
846 now_.GetYear(), an.hour, an.minute);
851 if (an.start_time > now_ + wxTimeSpan::Hours(48))
852 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
853 now_.GetYear() - 1, an.hour, an.minute);
856 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
861 if (an.expiry_time < now_ - wxTimeSpan::Hours(24)) {
862 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
863 now_.GetYear() + 1, an.hour, an.minute);
865 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
870 int subarea_len = 87;
873 float pos_scale = 60000.0;
881 pos_scale = 600000.0;
886 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
887 for (
int i = 0; i < subarea_count; ++i) {
888 int base = 111 + i * subarea_len;
890 sa.shape = bstr->
GetInt(base + 1, 3);
891 int scale_factor = 1;
892 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
895 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
896 sa.text = wxString(t, wxConvUTF8);
898 int scale_multipliers[4] = {1, 10, 100, 1000};
899 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
901 case AIS8_001_22_SHAPE_SECTOR:
902 sa.left_bound_deg = bstr->
GetInt(
903 base + 6 + lon_len + lat_len + prec_size + 12, 9);
904 sa.right_bound_deg = bstr->
GetInt(
905 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
906 case AIS8_001_22_SHAPE_CIRCLE:
908 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
911 case AIS8_001_22_SHAPE_RECT:
913 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
915 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
918 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
923 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
925 sa.orient_deg = bstr->
GetInt(
926 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
928 case AIS8_001_22_SHAPE_POLYLINE:
929 case AIS8_001_22_SHAPE_POLYGON:
930 for (
int j = 0; j < 4; ++j) {
931 sa.angles[j] = bstr->
GetInt(base + 6 + j * 20, 10) * 0.5;
933 bstr->
GetInt(base + 16 + j * 20, 10) * scale_factor;
937 an.sub_areas.push_back(sa);
939 ptd->area_notices[an.link_id] = an;
946 if (bstr->GetBitCount() >= 360) {
948 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
951 double lon_tentative = 181.;
952 double lat_tentative = 91.;
954 int lon = bstr->
GetInt(57, 25);
955 int lat = bstr->
GetInt(82, 24);
957 if (lon & 0x01000000)
959 lon_tentative = lon / 60000.;
961 if (lat & 0x00800000)
963 lat_tentative = lat / 60000.;
965 ptd->Lon = lon_tentative;
966 ptd->Lat = lat_tentative;
969 wxString x = ptd->ShipName;
970 if (x.Find(
"METEO") == wxNOT_FOUND) {
972 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
973 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
976 wxString nameID =
"METEO ";
977 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
978 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
981 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
982 ptd->met_data.day = bstr->
GetInt(107, 5);
983 ptd->met_data.hour = bstr->
GetInt(112, 5);
984 ptd->met_data.minute = bstr->
GetInt(117, 6);
985 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
986 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
987 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
988 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
990 int tmp = bstr->
GetInt(155, 11);
991 if (tmp & 0x00000400)
993 ptd->met_data.air_temp = tmp / 10.;
994 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
995 int dew = bstr->
GetInt(173, 10);
996 if (dew & 0x00000200)
998 ptd->met_data.dew_point = dew / 10.;
1003 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
1004 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
1006 int horVis = bstr->
GetInt(194, 8);
1007 if (horVis & 0x80u) {
1009 ptd->met_data.hor_vis_GT =
true;
1011 ptd->met_data.hor_vis_GT =
false;
1013 ptd->met_data.hor_vis = horVis / 10.0;
1015 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
1016 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
1017 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
1018 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
1019 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
1020 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
1021 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
1022 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
1023 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
1024 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
1025 ptd->met_data.seastate = bstr->
GetInt(323, 4);
1027 int wt = bstr->
GetInt(327, 10);
1028 if (wt & 0x00000200)
1030 ptd->met_data.water_temp = wt / 10.;
1032 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
1033 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
1034 ptd->met_data.ice = bstr->
GetInt(349, 2);
1036 ptd->Class = AIS_METEO;
1040 ptd->b_NoTrack =
true;
1041 ptd->b_show_track =
false;
1042 ptd->b_positionDoubtful =
false;
1043 ptd->b_positionOnceValid =
true;
1044 b_posn_report =
true;
1045 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1046 ptd->PositionReportTicks = now.GetTicks();
1047 ptd->b_nameValid =
true;
1049 parse_result =
true;
1055 if (dac == 367 && fi == 33) {
1059 const int size = bstr->GetBitCount();
1062 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
1063 const int startbits = 56;
1064 const int slotsize = 112;
1065 const int slots_count = (size - startbits) / slotsize;
1067 for (
int slot = 0; slot < slots_count; slot++) {
1068 slotbit = slot * slotsize;
1069 int type = bstr->
GetInt(slotbit + 57, 4);
1070 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
1071 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
1072 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
1075 if (!ptd->b_nameValid) {
1076 wxString nameID =
"METEO Site: ";
1078 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
1079 ptd->b_nameValid =
true;
1083 int lon = bstr->
GetInt(slotbit + 90, 28);
1084 if (lon & 0x08000000)
1086 ptd->Lon = lon / 600000.;
1088 int lat = bstr->
GetInt(slotbit + 118, 27);
1089 if (lat & 0x04000000)
1091 ptd->Lat = lat / 600000.;
1092 ptd->b_positionOnceValid =
true;
1094 }
else if (type == 1) {
1095 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
1096 ptd->b_nameValid =
true;
1098 }
else if (type == 2) {
1100 int descr = bstr->
GetInt(slotbit + 116, 3);
1101 if (descr == 1 || descr == 2) {
1102 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1103 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1104 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1105 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
1108 }
else if (type == 3) {
1110 int descr = bstr->
GetInt(slotbit + 108, 3);
1111 if (descr == 1 || descr == 2) {
1112 int wltype = bstr->
GetInt(slotbit + 84, 1);
1113 int wl = bstr->
GetInt(slotbit + 85, 16);
1114 if (wl & 0x00004000)
1118 ptd->met_data.water_level = wl / 100.;
1120 ptd->met_data.water_lev_dev = wl / 100.;
1122 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
1123 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
1125 }
else if (type == 6) {
1126 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
1127 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
1128 }
else if (type == 7) {
1130 bstr->
GetInt(slotbit + 111, 3);
1131 if (swell_descr == 1 || swell_descr == 2) {
1132 ptd->met_data.swell_height =
1133 bstr->
GetInt(slotbit + 84, 8) / 10.0;
1134 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
1135 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
1137 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
1138 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
1139 if (wt_descr == 1 || wt_descr == 2)
1140 ptd->met_data.water_temp =
1141 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
1143 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
1144 if (wawe_descr == 1 || wawe_descr == 2) {
1145 ptd->met_data.wave_height =
1146 bstr->
GetInt(slotbit + 134, 8) / 10.0;
1147 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
1148 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
1150 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
1152 }
else if (type == 8) {
1153 ptd->met_data.water_temp =
1154 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
1155 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
1157 }
else if (type == 9) {
1158 int tmp = bstr->
GetInt(slotbit + 84, 11);
1159 if (tmp & 0x00000400)
1161 ptd->met_data.air_temp = tmp / 10.;
1162 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
1173 ptd->met_data.precipitation = pp;
1174 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
1175 ptd->met_data.dew_point =
1176 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
1177 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
1178 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
1179 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
1181 }
else if (type == 11) {
1183 int descr = bstr->
GetInt(slotbit + 113, 3);
1184 if (descr == 1 || descr == 2) {
1185 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1186 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1187 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1192 if (ptd->b_positionOnceValid) {
1193 ptd->Class = AIS_METEO;
1197 ptd->b_NoTrack =
true;
1198 ptd->b_show_track =
false;
1199 ptd->b_positionDoubtful =
false;
1200 b_posn_report =
true;
1201 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1202 ptd->PositionReportTicks = now.GetTicks();
1203 ptd->b_nameValid =
true;
1205 parse_result =
true;
1215 char msg_14_text[968];
1216 if (bstr->GetBitCount() > 40) {
1217 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
1218 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
1220 nd = wxMin(nd, 967);
1221 msg_14_text[nd] = 0;
1222 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
1224 parse_result =
true;
1237 if (b_posn_report) ptd->b_lost =
false;
1239 if (
true == parse_result) {
1241 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
1242 ptd->b_active =
true;
1245 return parse_result;
1249 : m_signalk_selfid(
""), m_callbacks(callbacks) {
1251 AISTargetNamesC =
new AIS_Target_Name_Hash;
1252 AISTargetNamesNC =
new AIS_Target_Name_Hash;
1254 if (g_benableAISNameCache) {
1258 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
1259 wxString line = infile.GetFirstLine();
1260 while (!infile.Eof()) {
1261 if (line.IsSameAs(
"+++==Confirmed Entry's==+++"))
1262 HashFile = AISTargetNamesC;
1264 if (line.IsSameAs(
"+++==Non Confirmed Entry's==+++"))
1265 HashFile = AISTargetNamesNC;
1267 wxStringTokenizer tokenizer(line,
",");
1268 int mmsi = wxAtoi(tokenizer.GetNextToken());
1269 wxString name = tokenizer.GetNextToken().Trim();
1270 (*HashFile)[mmsi] = name;
1273 line = infile.GetNextLine();
1280 BuildERIShipTypeHash();
1282 g_pais_alert_dialog_active =
nullptr;
1283 m_bAIS_Audio_Alert_On =
false;
1287 m_bAIS_AlertPlaying =
false;
1289 TimerAIS.SetOwner(
this, TIMER_AIS1);
1290 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
1292 m_ptentative_dsctarget =
nullptr;
1293 m_dsc_timer.SetOwner(
this, TIMER_DSC);
1300 InitCommListeners();
1303AisDecoder::~AisDecoder() {
1313 wxString content =
"+++==Confirmed Entry's==+++";
1314 AIS_Target_Name_Hash::iterator it;
1315 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
1316 content.append(
"\r\n");
1317 content.append(wxString::Format(
"%i", it->first));
1318 content.append(
",").append(it->second);
1320 content.append(
"\r\n");
1321 content.append(
"+++==Non Confirmed Entry's==+++");
1322 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
1323 content.append(
"\r\n");
1324 content.append(wxString::Format(
"%i", it->first));
1325 content.append(
",").append(it->second);
1327 outfile.Write(content);
1331 AISTargetNamesC->clear();
1332 delete AISTargetNamesC;
1333 AISTargetNamesNC->clear();
1334 delete AISTargetNamesNC;
1339 m_AIS_Audio_Alert_Timer.Stop();
1344 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
1346 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
1350bool IsTargetOnTheIgnoreList(
const int &mmsi) {
1355 if (props->m_bignore) {
1363void AisDecoder::InitCommListeners() {
1369 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
1372 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1373 HandleN0183_AIS(n0183_msg);
1378 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
1380 Bind(EVT_N0183_FRPOS, [&](
const ObservedEvt &ev) {
1382 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1383 HandleN0183_AIS(n0183_msg);
1388 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
1389 Bind(EVT_N0183_CDDSC, [&](
const ObservedEvt &ev) {
1391 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1392 HandleN0183_AIS(n0183_msg);
1397 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
1398 Bind(EVT_N0183_CDDSE, [&](
const ObservedEvt &ev) {
1400 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1401 HandleN0183_AIS(n0183_msg);
1406 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
1410 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1411 HandleN0183_AIS(n0183_msg);
1416 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
1419 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1420 HandleN0183_AIS(n0183_msg);
1425 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
1428 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1429 HandleN0183_AIS(n0183_msg);
1434 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
1437 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1438 HandleN0183_AIS(n0183_msg);
1443 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
1445 HandleSignalK(obs::UnpackEvtPointer<SignalkMsg>(ev));
1450 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
1451 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
1453 HandleN2K_129038(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1458 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
1459 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
1461 HandleN2K_129039(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1466 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
1467 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
1469 HandleN2K_129041(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1474 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
1475 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
1477 HandleN2K_129794(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1482 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
1483 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
1485 HandleN2K_129809(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1490 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
1491 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
1493 HandleN2K_129810(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1498 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
1499 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
1501 HandleN2K_129793(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1505bool AisDecoder::HandleN0183_AIS(
const N0183MsgPtr &n0183_msg) {
1506 std::string str = n0183_msg->payload;
1507 wxString sentence(str.c_str());
1508 DecodeN0183(sentence);
1513bool AisDecoder::HandleN2K_129038(
const N2000MsgPtr &n2k_msg) {
1514 std::vector<unsigned char> v = n2k_msg->payload;
1517 tN2kAISRepeat Repeat;
1528 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
1529 tN2kAISTransceiverInformation AISTransceiverInformation;
1531 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
1532 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
1533 NavStat, AISTransceiverInformation)) {
1534 unsigned mmsi = UserID;
1536 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1540 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1541 bool bnewtarget =
false;
1543 auto it = AISTargetList.find(mmsi);
1544 if (it == AISTargetList.end())
1546 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1550 pTargetData = it->second;
1553 wxDateTime now = wxDateTime::Now();
1557 pTargetData->MMSI = mmsi;
1558 pTargetData->MID = MessageID;
1559 pTargetData->MMSI = mmsi;
1560 pTargetData->Class = AIS_CLASS_A;
1562 if (97 == pTargetData->MMSI / 10000000) {
1563 pTargetData->Class = AIS_SART;
1565 pTargetData->StaticReportTicks = now.GetTicks();
1567 pTargetData->NavStatus =
static_cast<ais_nav_status
>(NavStat);
1568 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1569 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1570 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1571 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1572 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1574 pTargetData->ROTAIS = ROT;
1586 pTargetData->b_OwnShip =
1587 AISTransceiverInformation ==
1588 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1589 pTargetData->b_active =
true;
1590 pTargetData->b_lost =
false;
1591 pTargetData->b_positionOnceValid =
true;
1592 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1593 pTargetData->PositionReportTicks = now.GetTicks();
1595 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1596 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1605bool AisDecoder::HandleN2K_129039(
const N2000MsgPtr &n2k_msg) {
1606 std::vector<unsigned char> v = n2k_msg->payload;
1621 tN2kAISRepeat Repeat;
1631 tN2kAISTransceiverInformation AISTransceiverInformation;
1633 bool DSC, Band, Msg22, State, Display;
1636 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
1637 Accuracy, RAIM, Seconds, COG, SOG,
1638 AISTransceiverInformation, Heading, Unit, Display, DSC,
1639 Band, Msg22, Mode, State)) {
1640 uint32_t mmsi = UserID;
1642 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1646 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1647 bool bnewtarget =
false;
1649 auto it = AISTargetList.find(mmsi);
1650 if (it == AISTargetList.end())
1652 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1656 pTargetData = it->second;
1659 wxDateTime now = wxDateTime::Now();
1663 pTargetData->MMSI = mmsi;
1664 pTargetData->MID = MessageID;
1665 if (!isBuoyMmsi(mmsi))
1666 pTargetData->Class = AIS_CLASS_B;
1668 pTargetData->Class = AIS_BUOY;
1670 pTargetData->NavStatus = UNDEFINED;
1671 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1672 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1673 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1674 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1675 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1677 pTargetData->b_positionOnceValid =
true;
1678 pTargetData->b_active =
true;
1679 pTargetData->b_lost =
false;
1680 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1681 pTargetData->PositionReportTicks = now.GetTicks();
1682 pTargetData->b_OwnShip =
1683 AISTransceiverInformation ==
1684 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1686 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1687 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1697bool AisDecoder::HandleN2K_129041(
const N2000MsgPtr &n2k_msg) {
1698 std::vector<unsigned char> v = n2k_msg->payload;
1700 tN2kAISAtoNReportData data;
1703 struct tN2kAISAtoNReportData {
1705 tN2kAISRepeat Repeat;
1714 double PositionReferenceStarboard ;
1715 double PositionReferenceTrueNorth;
1716 tN2kAISAtoNType AtoNType;
1717 bool OffPositionIndicator;
1718 bool VirtualAtoNFlag;
1719 bool AssignedModeFlag;
1720 tN2kGNSStype GNSSType;
1722 tN2kAISTransceiverInformation AISTransceiverInformation;
1723 char AtoNName[34 + 1];
1726 if (ParseN2kPGN129041(v, data)) {
1727 uint32_t mmsi = data.UserID;
1729 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1733 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1734 bool bnewtarget =
false;
1736 auto it = AISTargetList.find(mmsi);
1737 if (it == AISTargetList.end())
1739 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1743 pTargetData = it->second;
1747 pTargetData->MMSI = mmsi;
1749 wxDateTime now = wxDateTime::Now();
1752 int offpos = data.OffPositionIndicator;
1753 int virt = data.VirtualAtoNFlag;
1756 pTargetData->NavStatus = ATON_VIRTUAL;
1758 pTargetData->NavStatus = ATON_REAL;
1760 pTargetData->m_utc_sec = data.Seconds;
1762 if (pTargetData->m_utc_sec <= 59) {
1763 pTargetData->NavStatus += 1;
1764 if (offpos) pTargetData->NavStatus += 1;
1767 data.AtoNName[34] = 0;
1768 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
1769 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1770 pTargetData->b_nameValid =
true;
1771 pTargetData->MID = 124;
1773 pTargetData->ShipType = data.AtoNType;
1774 pTargetData->Class = AIS_ATON;
1776 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
1777 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
1778 pTargetData->b_positionDoubtful =
false;
1779 pTargetData->b_positionOnceValid =
true;
1780 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1781 pTargetData->PositionReportTicks = now.GetTicks();
1785 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1786 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1788 touch_state.Notify();
1795bool AisDecoder::HandleN2K_129794(
const N2000MsgPtr &n2k_msg) {
1796 std::vector<unsigned char> v = n2k_msg->payload;
1799 tN2kAISRepeat Repeat;
1803 char Name[SHIP_NAME_LEN];
1812 char Destination[DESTINATION_LEN];
1813 tN2kAISVersion AISversion;
1814 tN2kGNSStype GNSStype;
1816 tN2kAISTranceiverInfo AISinfo;
1818 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
1819 VesselType, Length, Beam, PosRefStbd, PosRefBow,
1820 ETAdate, ETAtime, Draught, Destination, AISversion,
1821 GNSStype, DTE, AISinfo)) {
1822 uint32_t mmsi = UserID;
1824 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1828 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1829 bool bnewtarget =
false;
1831 auto it = AISTargetList.find(mmsi);
1832 if (it == AISTargetList.end())
1834 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1838 pTargetData = it->second;
1842 pTargetData->MMSI = mmsi;
1843 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1844 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1845 Name[
sizeof(Name) - 1] = 0;
1846 pTargetData->b_nameValid =
true;
1847 pTargetData->MID = 124;
1849 pTargetData->b_OwnShip =
1851 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
1853 pTargetData->DimA = PosRefBow;
1854 pTargetData->DimB = Length - PosRefBow;
1855 pTargetData->DimC = Beam - PosRefStbd;
1856 pTargetData->DimD = PosRefStbd;
1857 pTargetData->Draft = Draught;
1858 pTargetData->IMO = IMOnumber;
1859 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1860 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1861 pTargetData->ShipType = (
unsigned char)VesselType;
1862 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
1863 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
1864 Destination[
sizeof(Destination) - 1] = 0;
1866 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
1867 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
1868 wxDateTime t((time_t)secs);
1870 wxDateTime tz = t.ToUTC();
1871 pTargetData->ETA_Mo = tz.GetMonth() + 1;
1872 pTargetData->ETA_Day = tz.GetDay();
1873 pTargetData->ETA_Hr = tz.GetHour();
1874 pTargetData->ETA_Min = tz.GetMinute();
1878 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1879 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1887bool AisDecoder::HandleN2K_129809(
const N2000MsgPtr &n2k_msg) {
1888 std::vector<unsigned char> v = n2k_msg->payload;
1891 tN2kAISRepeat Repeat;
1895 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
1896 unsigned mmsi = UserID;
1898 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1902 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1903 bool bnewtarget =
false;
1905 auto it = AISTargetList.find(mmsi);
1906 if (it == AISTargetList.end())
1908 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1912 pTargetData = it->second;
1916 pTargetData->MMSI = mmsi;
1917 Name[
sizeof(Name) - 1] = 0;
1918 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1919 pTargetData->b_nameValid =
true;
1920 pTargetData->MID = 124;
1922 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1923 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1933bool AisDecoder::HandleN2K_129810(
const N2000MsgPtr &n2k_msg) {
1934 std::vector<unsigned char> v = n2k_msg->payload;
1937 tN2kAISRepeat Repeat;
1946 uint32_t MothershipID;
1948 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
1949 Callsign, Length, Beam, PosRefStbd, PosRefBow,
1951 unsigned mmsi = UserID;
1953 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1957 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1958 bool bnewtarget =
false;
1960 auto it = AISTargetList.find(mmsi);
1961 if (it == AISTargetList.end())
1963 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1967 pTargetData = it->second;
1971 pTargetData->MMSI = mmsi;
1972 pTargetData->DimA = PosRefBow;
1973 pTargetData->DimB = Length - PosRefBow;
1974 pTargetData->DimC = Beam - PosRefStbd;
1975 pTargetData->DimD = PosRefStbd;
1976 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1977 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1978 pTargetData->ShipType = (
unsigned char)VesselType;
1980 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
1981 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1990bool AisDecoder::HandleN2K_129793(
const N2000MsgPtr &n2k_msg) {
1991 std::vector<unsigned char> v = n2k_msg->payload;
1994 tN2kAISRepeat Repeat;
1998 unsigned int SecondsSinceMidnight;
1999 unsigned int DaysSinceEpoch;
2001 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
2002 SecondsSinceMidnight, DaysSinceEpoch)) {
2003 wxDateTime now = wxDateTime::Now();
2009 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2010 bool bnewtarget =
false;
2012 auto it = AISTargetList.find(mmsi);
2013 if (it == AISTargetList.end())
2015 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2019 pTargetData = it->second;
2023 pTargetData->MMSI = mmsi;
2024 pTargetData->Class = AIS_BASE;
2026 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
2027 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
2028 pTargetData->b_positionDoubtful =
false;
2029 pTargetData->b_positionOnceValid =
true;
2030 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2031 pTargetData->PositionReportTicks = now.GetTicks();
2035 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
2036 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
2055void AisDecoder::HandleSignalK(
const SignalKMsgPtr &sK_msg) {
2056 rapidjson::Document root;
2058 root.Parse(sK_msg->raw_message);
2060 if (root.HasParseError())
return;
2062 if (root.HasMember(
"self")) {
2068 if (m_signalk_selfid.IsEmpty()) {
2073 int meteo_SiteID = 0;
2074 if (root.HasMember(
"context") && root[
"context"].IsString()) {
2075 wxString context = root[
"context"].GetString();
2076 if (context == m_signalk_selfid) {
2078 wxLogMessage(
"** Ignore context own ship..");
2082 wxString mmsi_string;
2083 if (context.StartsWith(
"vessels.urn:mrn:imo:mmsi:", &mmsi_string) ||
2084 context.StartsWith(
"atons.urn:mrn:imo:mmsi:", &mmsi_string) ||
2085 context.StartsWith(
"aircraft.urn:mrn:imo:mmsi:", &mmsi_string)) {
2088 if (mmsi_string.ToLong(&mmsi)) {
2093 }
else if (context.StartsWith(
"meteo.urn:mrn:imo:mmsi:", &mmsi_string)) {
2095 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
2096 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
2099 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
2110 if (g_pMUX && g_pMUX->IsLogActive()) {
2112 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
2117 if (IsTargetOnTheIgnoreList(mmsi))
return;
2123 wxLogDebug(
"AisDecoder::OnEvtSignalK: %s", root.dump().c_str(); );
2125 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2126 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2127 bool bnewtarget =
false;
2128 int last_report_ticks;
2130 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
2133 pTargetData->MMSI = mmsi;
2134 getMmsiProperties(pTargetData);
2135 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
2136 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
2137 itr != root[
"updates"].End(); ++itr) {
2138 handleUpdate(pTargetData, bnewtarget, *itr);
2143 if (97 == mmsi / 10000000) {
2144 pTargetData->Class = AIS_SART;
2145 }
else if (1994 == mmsi / 100000) {
2147 pTargetData->Class = AIS_METEO;
2148 pTargetData->met_data.original_mmsi = origin_mmsi;
2149 pTargetData->met_data.stationID = meteo_SiteID;
2152 wxString met_name = pTargetData->ShipName;
2153 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
2156 s_id << meteo_SiteID;
2157 id1 = wxAtoi(s_id.Mid(1, 3));
2158 id2 = wxAtoi(s_id.Mid(4, 3));
2159 met_name =
"METEO ";
2160 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
2161 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
2163 pTargetData->b_nameValid =
true;
2164 pTargetData->MID = 123;
2165 pTargetData->COG = -1.;
2166 pTargetData->HDG = 511;
2167 pTargetData->SOG = -1.;
2168 pTargetData->b_NoTrack =
true;
2169 pTargetData->b_show_track =
false;
2171 pTargetData->b_OwnShip =
false;
2172 AISTargetList[pTargetData->MMSI] = pTargetData;
2176void AisDecoder::handleUpdate(
const std::shared_ptr<AisTargetData> &pTargetData,
2177 bool bnewtarget,
const rapidjson::Value &update) {
2178 wxString sfixtime =
"";
2180 if (update.HasMember(
"timestamp")) {
2181 sfixtime = update[
"timestamp"].GetString();
2183 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
2184 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
2185 itr != update[
"values"].End(); ++itr) {
2186 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
2189 wxDateTime now = wxDateTime::Now();
2190 pTargetData->m_utc_hour = now.ToUTC().GetHour();
2191 pTargetData->m_utc_min = now.ToUTC().GetMinute();
2192 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
2194 pTargetData->b_active =
true;
2195 pTargetData->b_lost =
false;
2197 if (pTargetData->b_positionOnceValid) {
2198 long mmsi_long = pTargetData->MMSI;
2200 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2201 (
void *)mmsi_long, SELTYPE_AISTARGET);
2202 pSel->SetUserData(pTargetData->MMSI);
2204 UpdateOneCPA(pTargetData.get());
2205 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2208void AisDecoder::updateItem(
const std::shared_ptr<AisTargetData> &pTargetData,
2209 bool bnewtarget,
const rapidjson::Value &item,
2210 wxString &sfixtime)
const {
2211 if (item.HasMember(
"path") && item.HasMember(
"value")) {
2212 const wxString &update_path = item[
"path"].GetString();
2213 if (update_path ==
"navigation.position") {
2214 if (item[
"value"].HasMember(
"latitude") &&
2215 item[
"value"].HasMember(
"longitude")) {
2216 wxDateTime now = wxDateTime::Now();
2218 double lat = item[
"value"][
"latitude"].GetDouble();
2219 double lon = item[
"value"][
"longitude"].GetDouble();
2220 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2221 pTargetData->PositionReportTicks = now.GetTicks();
2222 pTargetData->StaticReportTicks = now.GetTicks();
2223 pTargetData->Lat = lat;
2224 pTargetData->Lon = lon;
2225 pTargetData->b_positionOnceValid =
true;
2226 pTargetData->b_positionDoubtful =
false;
2233 }
else if (update_path ==
"navigation.speedOverGround" &&
2234 item[
"value"].IsNumber()) {
2235 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
2236 }
else if (update_path ==
"navigation.courseOverGroundTrue" &&
2237 item[
"value"].IsNumber()) {
2238 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2239 }
else if (update_path ==
"navigation.headingTrue" &&
2240 item[
"value"].IsNumber()) {
2241 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2242 }
else if (update_path ==
"navigation.rateOfTurn" &&
2243 item[
"value"].IsNumber()) {
2244 double deg_min = item[
"value"].GetDouble() * 180.0 / M_PI * 60.0;
2245 double rot = 4.733 * sqrt(fabs(deg_min));
2246 int rotais = wxRound(rot);
2247 if (rotais > 126) rotais = 126;
2248 pTargetData->ROTAIS = deg_min < 0 ? -rotais : rotais;
2249 pTargetData->ROTIND = wxRound(deg_min);
2250 }
else if (update_path ==
"design.aisShipType") {
2251 if (item[
"value"].HasMember(
"id")) {
2252 if (!pTargetData->b_isDSCtarget) {
2253 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2256 }
else if (update_path ==
"atonType") {
2257 if (item[
"value"].HasMember(
"id")) {
2258 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2260 }
else if (update_path ==
"virtual") {
2261 if (item[
"value"].GetBool()) {
2262 pTargetData->NavStatus = ATON_VIRTUAL;
2264 pTargetData->NavStatus = ATON_REAL;
2266 }
else if (update_path ==
"offPosition") {
2267 if (item[
"value"].GetBool()) {
2268 if (ATON_REAL == pTargetData->NavStatus) {
2269 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
2270 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
2271 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
2274 }
else if (update_path ==
"design.draft") {
2275 if (item[
"value"].HasMember(
"maximum") &&
2276 item[
"value"][
"maximum"].IsNumber()) {
2277 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
2278 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
2280 if (item[
"value"].HasMember(
"current") &&
2281 item[
"value"][
"current"].IsNumber()) {
2282 double draft = item[
"value"][
"current"].GetDouble();
2284 pTargetData->Draft = draft;
2285 pTargetData->Euro_Draft = draft;
2288 }
else if (update_path ==
"design.length") {
2289 if (pTargetData->DimB == 0) {
2290 if (item[
"value"].HasMember(
"overall")) {
2291 if (item[
"value"][
"overall"].IsNumber()) {
2292 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
2293 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
2295 pTargetData->DimB = 0;
2298 }
else if (update_path ==
"sensors.ais.class") {
2299 wxString aisclass = item[
"value"].GetString();
2300 if (aisclass ==
"A") {
2301 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
2302 }
else if (aisclass ==
"B") {
2303 if (!pTargetData->b_isDSCtarget) {
2304 if (!isBuoyMmsi(pTargetData->MMSI))
2305 pTargetData->Class = AIS_CLASS_B;
2307 pTargetData->Class = AIS_BUOY;
2310 pTargetData->NavStatus = UNDEFINED;
2312 }
else if (aisclass ==
"BASE") {
2313 pTargetData->Class = AIS_BASE;
2314 }
else if (aisclass ==
"ATON") {
2315 pTargetData->Class = AIS_ATON;
2317 }
else if (update_path ==
"sensors.ais.fromBow") {
2318 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
2319 int length = pTargetData->DimA;
2320 if (item[
"value"].IsNumber()) {
2321 pTargetData->DimA = item[
"value"].GetDouble();
2322 pTargetData->DimB = length - item[
"value"].GetDouble();
2325 }
else if (update_path ==
"design.beam") {
2326 if (pTargetData->DimD == 0) {
2327 if (item[
"value"].IsNumber()) {
2328 pTargetData->Euro_Beam = item[
"value"].GetDouble();
2329 pTargetData->DimC = item[
"value"].GetDouble();
2331 pTargetData->DimD = 0;
2333 }
else if (update_path ==
"sensors.ais.fromCenter") {
2334 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
2335 int beam = pTargetData->DimC;
2336 int center = beam / 2;
2337 if (item[
"value"].IsNumber()) {
2340 pTargetData->DimC = center + item[
"value"].GetDouble();
2341 pTargetData->DimD = beam - pTargetData->DimC;
2344 }
else if (update_path ==
"navigation.state") {
2345 wxString state = item[
"value"].GetString();
2346 if (state ==
"motoring") {
2347 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
2348 }
else if (state ==
"anchored") {
2349 pTargetData->NavStatus = AT_ANCHOR;
2350 }
else if (state ==
"not under command") {
2351 pTargetData->NavStatus = NOT_UNDER_COMMAND;
2352 }
else if (state ==
"restricted manouverability") {
2353 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
2354 }
else if (state ==
"constrained by draft") {
2355 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
2356 }
else if (state ==
"moored") {
2357 pTargetData->NavStatus = MOORED;
2358 }
else if (state ==
"aground") {
2359 pTargetData->NavStatus = AGROUND;
2360 }
else if (state ==
"fishing") {
2361 pTargetData->NavStatus = FISHING;
2362 }
else if (state ==
"sailing") {
2363 pTargetData->NavStatus = UNDERWAY_SAILING;
2364 }
else if (state ==
"hazardous material high speed") {
2365 pTargetData->NavStatus = HSC;
2366 }
else if (state ==
"hazardous material wing in ground") {
2367 pTargetData->NavStatus = WIG;
2368 }
else if (state ==
"ais-sart") {
2369 pTargetData->NavStatus = RESERVED_14;
2371 pTargetData->NavStatus = UNDEFINED;
2373 }
else if (update_path ==
"navigation.destination.commonName") {
2374 const wxString &destination = item[
"value"].GetString();
2375 pTargetData->Destination[0] =
'\0';
2376 strncpy(pTargetData->Destination, destination.c_str(),
2377 DESTINATION_LEN - 1);
2378 }
else if (update_path ==
"navigation.destination.eta") {
2379 const wxString &eta = item[
"value"].GetString();
2384 pTargetData->ETA_Mo = tz.GetMonth() + 1;
2385 pTargetData->ETA_Day = tz.GetDay();
2386 pTargetData->ETA_Hr = tz.GetHour();
2387 pTargetData->ETA_Min = tz.GetMinute();
2389 }
else if (update_path ==
"navigation.specialManeuver") {
2390 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
2391 pTargetData->IMO < 1) {
2392 const wxString &bluesign = item[
"value"].GetString();
2393 if (
"not engaged" == bluesign) {
2394 pTargetData->blue_paddle = 1;
2396 if (
"engaged" == bluesign) {
2397 pTargetData->blue_paddle = 2;
2399 pTargetData->b_blue_paddle =
2400 pTargetData->blue_paddle == 2 ? true :
false;
2402 }
else if (update_path ==
"sensors.ais.designatedAreaCode") {
2403 if (item[
"value"].GetInt() == 200) {
2404 pTargetData->b_hasInlandDac =
true;
2406 }
else if (update_path ==
"sensors.ais.functionalId") {
2407 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
2409 pTargetData->b_isEuroInland =
true;
2413 }
else if (update_path ==
"environment.date") {
2414 const wxString &issued = item[
"value"].GetString();
2419 pTargetData->met_data.day = tz.GetDay();
2420 pTargetData->met_data.hour = tz.GetHour();
2421 pTargetData->met_data.minute = tz.GetMinute();
2423 }
else if (update_path ==
"environment.wind.averageSpeed" &&
2424 item[
"value"].IsNumber()) {
2425 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
2426 }
else if (update_path ==
"environment.wind.gust" &&
2427 item[
"value"].IsNumber()) {
2428 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
2429 }
else if (update_path ==
"environment.wind.directionTrue" &&
2430 item[
"value"].IsNumber()) {
2431 pTargetData->met_data.wind_dir =
2432 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2433 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
2434 item[
"value"].IsNumber()) {
2435 pTargetData->met_data.wind_gust_dir =
2436 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2437 }
else if (update_path ==
"environment.outside.temperature" &&
2438 item[
"value"].IsNumber()) {
2439 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
2440 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
2441 item[
"value"].IsNumber()) {
2442 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
2443 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
2444 item[
"value"].IsNumber()) {
2445 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
2446 }
else if (update_path ==
"environment.outside.pressure" &&
2447 item[
"value"].IsNumber()) {
2448 pTargetData->met_data.airpress =
2449 static_cast<int>(item[
"value"].GetDouble() / 100);
2450 }
else if (update_path ==
"environment.water.level" &&
2451 item[
"value"].IsNumber()) {
2452 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
2453 }
else if (update_path ==
"environment.water.current.drift" &&
2454 item[
"value"].IsNumber()) {
2455 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
2456 }
else if (update_path ==
"environment.water.current.set" &&
2457 item[
"value"].IsNumber()) {
2458 pTargetData->met_data.curr_dir =
2459 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2460 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
2461 item[
"value"].IsNumber()) {
2462 pTargetData->met_data.water_lev_trend =
2463 static_cast<int>(item[
"value"].GetDouble());
2464 }
else if (update_path ==
"environment.water.levelTendency") {
2466 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
2467 item[
"value"].IsNumber()) {
2468 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
2469 }
else if (update_path ==
"environment.water.waves.period" &&
2470 item[
"value"].IsNumber()) {
2471 pTargetData->met_data.wave_period =
2472 static_cast<int>(item[
"value"].GetDouble());
2473 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
2474 item[
"value"].IsNumber()) {
2475 pTargetData->met_data.wave_dir =
2476 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2477 }
else if (update_path ==
"environment.water.swell.height" &&
2478 item[
"value"].IsNumber()) {
2479 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
2480 }
else if (update_path ==
"environment.water.swell.period" &&
2481 item[
"value"].IsNumber()) {
2482 pTargetData->met_data.swell_per =
2483 static_cast<int>(item[
"value"].GetDouble());
2484 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
2485 item[
"value"].IsNumber()) {
2486 pTargetData->met_data.swell_dir =
2487 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2488 }
else if (update_path ==
"environment.water.temperature" &&
2489 item[
"value"].IsNumber()) {
2490 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
2491 }
else if (update_path ==
"environment.water.salinity" &&
2492 item[
"value"].IsNumber()) {
2493 pTargetData->met_data.salinity = item[
"value"].GetDouble();
2494 }
else if (update_path ==
"environment.water.ice") {
2496 }
else if (update_path ==
"environment.water.iceValue" &&
2497 item[
"value"].IsNumber()) {
2498 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
2499 }
else if (update_path ==
"environment.water.seaStateValue" &&
2500 item[
"value"].IsNumber()) {
2501 pTargetData->met_data.seastate =
2502 static_cast<int>(item[
"value"].GetDouble());
2503 }
else if (update_path ==
"environment.water.seaState") {
2505 }
else if (update_path ==
"environment.outside.precipitation") {
2507 }
else if (update_path ==
"environment.outside.precipitationValue" &&
2508 item[
"value"].IsNumber()) {
2509 pTargetData->met_data.precipitation =
2510 static_cast<int>(item[
"value"].GetDouble());
2511 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
2512 item[
"value"].IsNumber()) {
2513 pTargetData->met_data.airpress_tend =
2514 static_cast<int>(item[
"value"].GetDouble());
2515 }
else if (update_path ==
"environment.outside.pressureTendency") {
2517 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
2518 item[
"value"].IsNumber()) {
2519 pTargetData->met_data.hor_vis =
2520 GEODESIC_METERS2NM(item[
"value"].GetDouble());
2521 }
else if (update_path ==
2522 "environment.outside.horizontalVisibility.overRange") {
2523 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
2524 }
else if (update_path.empty()) {
2525 if (item[
"value"].HasMember(
"name")) {
2526 const wxString &name = item[
"value"][
"name"].GetString();
2527 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
2528 pTargetData->b_nameValid =
true;
2529 pTargetData->MID = 123;
2530 }
else if (item[
"value"].HasMember(
"registrations")) {
2531 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
2532 pTargetData->IMO = wxAtoi(imo.Right(7));
2533 }
else if (item[
"value"].HasMember(
"communication")) {
2534 const wxString &callsign =
2535 item[
"value"][
"communication"][
"callsignVhf"].GetString();
2536 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
2538 if (item[
"value"].HasMember(
"mmsi") &&
2539 1994 != (pTargetData->MMSI) / 100000) {
2541 wxString tmp = item[
"value"][
"mmsi"].GetString();
2542 if (tmp.ToLong(&mmsi)) {
2543 pTargetData->MMSI = mmsi;
2545 if (97 == mmsi / 10000000) {
2546 pTargetData->Class = AIS_SART;
2548 if (111 == mmsi / 1000000) {
2549 pTargetData->b_SarAircraftPosnReport =
true;
2552 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2557 wxLogMessage(wxString::Format(
2558 "** AisDecoder::updateItem: unhandled path %s", update_path));
2560 rapidjson::StringBuffer buffer;
2561 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2562 item.Accept(writer);
2563 wxString msg(
"update: ");
2564 msg.append(buffer.GetString());
2574AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
2575 wxString *accumulator) {
2577 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2579 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
2581 if (!pos)
return AIS_GENERIC_ERROR;
2583 if (!accumulator)
return AIS_GENERIC_ERROR;
2586 if (!str.Mid(1, 5).IsSameAs(
"AIVDO"))
return AIS_GENERIC_ERROR;
2589 wxStringTokenizer tkz(str,
",");
2592 token = tkz.GetNextToken();
2594 token = tkz.GetNextToken();
2595 int nsentences = atoi(token.mb_str());
2597 token = tkz.GetNextToken();
2598 int isentence = atoi(token.mb_str());
2600 token = tkz.GetNextToken();
2601 token = tkz.GetNextToken();
2603 wxString string_to_parse;
2604 string_to_parse.Clear();
2617 if ((1 == nsentences) && (1 == isentence)) {
2618 string_to_parse = tkz.GetNextToken();
2621 else if (nsentences > 1) {
2622 if (1 == isentence) {
2623 *accumulator = tkz.GetNextToken();
2627 accumulator->Append(tkz.GetNextToken());
2630 if (isentence == nsentences) {
2631 string_to_parse = *accumulator;
2635 if (string_to_parse.IsEmpty() &&
2637 return AIS_INCOMPLETE_MULTIPART;
2641 if (string_to_parse.Len() >= AIS_MAX_MESSAGE_LEN) {
2642 accumulator->Clear();
2643 return AIS_NMEAVDX_TOO_LONG;
2652 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2654 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
2656 if (bdecode_result) {
2657 switch (TargetData->MID) {
2662 if (!TargetData->b_positionDoubtful) {
2663 pos->
kLat = TargetData->Lat;
2664 pos->
kLon = TargetData->Lon;
2670 if (TargetData->COG == 360.0)
2673 pos->
kCog = TargetData->COG;
2675 if (TargetData->SOG > 102.2)
2678 pos->
kSog = TargetData->SOG;
2680 if ((
int)TargetData->HDG == 511)
2683 pos->
kHdt = TargetData->HDG;
2691 return AIS_GENERIC_ERROR;
2696 return AIS_GENERIC_ERROR;
2704AisError AisDecoder::DecodeN0183(
const wxString &str) {
2705 AisError ret = AIS_GENERIC_ERROR;
2706 wxString string_to_parse;
2708 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
2709 double gpsg_cog, gpsg_sog, gpsg_utc_time;
2710 int gpsg_utc_hour = 0;
2711 int gpsg_utc_min = 0;
2712 int gpsg_utc_sec = 0;
2713 char gpsg_name_str[21];
2716 bool bdecode_result =
false;
2723 long arpa_tgt_num = 0;
2724 double arpa_sog = 0.;
2725 double arpa_cog = 0.;
2726 double arpa_lat = 0.;
2727 double arpa_lon = 0.;
2728 double arpa_dist = 0.;
2729 double arpa_brg = 0.;
2730 wxString arpa_brgunit;
2731 wxString arpa_status;
2732 wxString arpa_distunit;
2733 wxString arpa_cogunit;
2734 double arpa_mins, arpa_degs;
2735 double arpa_utc_time;
2736 int arpa_utc_hour = 0;
2737 int arpa_utc_min = 0;
2738 int arpa_utc_sec = 0;
2739 char arpa_name_str[21];
2740 bool arpa_lost =
true;
2741 bool arpa_nottracked =
false;
2743 double aprs_lat = 0.;
2744 double aprs_lon = 0.;
2745 char aprs_name_str[21];
2746 double aprs_mins, aprs_degs;
2748 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2749 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2750 bool bnewtarget =
false;
2751 int last_report_ticks;
2755 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2757 if (!NMEACheckSumOK(str)) {
2758 return AIS_NMEAVDX_CHECKSUM_BAD;
2760 if (str.Mid(1, 2).IsSameAs(
"CD")) {
2763 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
2767 wxStringTokenizer tkz(str,
",*");
2770 token = tkz.GetNextToken();
2771 token = tkz.GetNextToken();
2772 token.ToLong(&arpa_tgt_num);
2773 token = tkz.GetNextToken();
2774 token.ToDouble(&arpa_dist);
2775 token = tkz.GetNextToken();
2776 token.ToDouble(&arpa_brg);
2777 arpa_brgunit = tkz.GetNextToken();
2778 if (arpa_brgunit ==
"R") {
2779 if (std::isnan(arpa_ref_hdg)) {
2780 if (!std::isnan(
gHdt))
2785 arpa_brg += arpa_ref_hdg;
2786 if (arpa_brg >= 360.) arpa_brg -= 360.;
2788 token = tkz.GetNextToken();
2789 token.ToDouble(&arpa_sog);
2790 token = tkz.GetNextToken();
2791 token.ToDouble(&arpa_cog);
2792 arpa_cogunit = tkz.GetNextToken();
2793 if (arpa_cogunit ==
"R") {
2794 if (std::isnan(arpa_ref_hdg)) {
2795 if (!std::isnan(
gHdt))
2800 arpa_cog += arpa_ref_hdg;
2801 if (arpa_cog >= 360.) arpa_cog -= 360.;
2803 token = tkz.GetNextToken();
2804 token = tkz.GetNextToken();
2806 arpa_distunit = tkz.GetNextToken();
2807 token = tkz.GetNextToken();
2808 if (token ==
"") token = wxString::Format(
"ARPA %ld", arpa_tgt_num);
2809 int len = wxMin(token.Length(), 20);
2810 strncpy(arpa_name_str, token.mb_str(), len);
2811 arpa_name_str[len] = 0;
2812 arpa_status = tkz.GetNextToken();
2813 if (arpa_status !=
"L") {
2815 }
else if (arpa_status !=
"")
2816 arpa_nottracked =
true;
2817 wxString arpa_reftarget = tkz.GetNextToken();
2818 if (tkz.HasMoreTokens()) {
2819 token = tkz.GetNextToken();
2820 token.ToDouble(&arpa_utc_time);
2821 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2822 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2824 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2826 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
2827 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
2828 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
2831 if (arpa_distunit ==
"K") {
2834 }
else if (arpa_distunit ==
"S") {
2839 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2843 }
else if (str.Mid(3, 3).IsSameAs(
"TLL")) {
2846 wxStringTokenizer tkz(str,
",*");
2849 wxString aprs_tll_str = tkz.GetNextToken();
2850 token = tkz.GetNextToken();
2851 token.ToLong(&arpa_tgt_num);
2852 token = tkz.GetNextToken();
2853 token.ToDouble(&arpa_lat);
2854 arpa_degs = (int)(arpa_lat / 100.0);
2855 arpa_mins = arpa_lat - arpa_degs * 100.0;
2856 arpa_lat = arpa_degs + arpa_mins / 60.0;
2857 token = tkz.GetNextToken();
2858 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2859 token.Mid(0, 1).Contains(
"s") ==
true)
2860 arpa_lat = 0. - arpa_lat;
2861 token = tkz.GetNextToken();
2862 token.ToDouble(&arpa_lon);
2863 arpa_degs = (int)(arpa_lon / 100.0);
2864 arpa_mins = arpa_lon - arpa_degs * 100.0;
2865 arpa_lon = arpa_degs + arpa_mins / 60.0;
2866 token = tkz.GetNextToken();
2867 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2868 token.Mid(0, 1).Contains(
"w") ==
true)
2869 arpa_lon = 0. - arpa_lon;
2870 token = tkz.GetNextToken();
2871 if (token ==
"") token = wxString::Format(
"ARPA %d", arpa_tgt_num);
2872 int len = wxMin(token.Length(), 20);
2873 strncpy(arpa_name_str, token.mb_str(), len);
2874 arpa_name_str[len] = 0;
2875 token = tkz.GetNextToken();
2876 token.ToDouble(&arpa_utc_time);
2877 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2878 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2880 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2885 if (arpa_status !=
"L")
2887 else if (arpa_status !=
"")
2888 arpa_nottracked =
true;
2889 wxString arpa_reftarget = tkz.GetNextToken();
2890 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2894 }
else if (str.Mid(3, 3).IsSameAs(
"OSD")) {
2896 wxStringTokenizer tkz(str,
",*");
2899 token = tkz.GetNextToken();
2900 token = tkz.GetNextToken();
2901 token.ToDouble(&arpa_ref_hdg);
2911 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(
"WPL")) {
2913 wxStringTokenizer tkz(str,
",*");
2916 token = tkz.GetNextToken();
2917 token = tkz.GetNextToken();
2918 token.ToDouble(&aprs_lat);
2919 aprs_degs = (int)(aprs_lat / 100.0);
2920 aprs_mins = aprs_lat - aprs_degs * 100.0;
2921 aprs_lat = aprs_degs + aprs_mins / 60.0;
2922 token = tkz.GetNextToken();
2923 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2924 token.Mid(0, 1).Contains(
"s") ==
true)
2925 aprs_lat = 0. - aprs_lat;
2926 token = tkz.GetNextToken();
2927 token.ToDouble(&aprs_lon);
2928 aprs_degs = (int)(aprs_lon / 100.0);
2929 aprs_mins = aprs_lon - aprs_degs * 100.0;
2930 aprs_lon = aprs_degs + aprs_mins / 60.0;
2931 token = tkz.GetNextToken();
2932 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2933 token.Mid(0, 1).Contains(
"w") ==
true)
2934 aprs_lon = 0. - aprs_lon;
2935 token = tkz.GetNextToken();
2936 int len = wxMin(token.Length(), 20);
2937 strncpy(aprs_name_str, token.mb_str(), len + 1);
2938 if (0 == g_WplAction) {
2940 aprs_name_str[len] = 0;
2941 for (i = 0; i < len; i++) {
2943 hash += (int)(aprs_name_str[i]);
2944 while (hash >= 100000) hash = hash / 100000;
2946 mmsi = aprs_mmsi = 199300000 + hash;
2950 }
else if (1 == g_WplAction) {
2952 new RoutePoint(aprs_lat, aprs_lon,
"", aprs_name_str,
"",
false);
2953 pWP->m_bIsolatedMark =
true;
2954 InsertWpt(pWP,
true);
2956 }
else if (str.Mid(1, 5).IsSameAs(
"FRPOS")) {
2960 wxStringTokenizer tkz(str,
",*");
2963 token = tkz.GetNextToken();
2965 token = tkz.GetNextToken();
2966 token.ToDouble(&gpsg_lat);
2967 gpsg_degs = (int)(gpsg_lat / 100.0);
2968 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
2969 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
2971 token = tkz.GetNextToken();
2972 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2973 token.Mid(0, 1).Contains(
"s") ==
true)
2974 gpsg_lat = 0. - gpsg_lat;
2976 token = tkz.GetNextToken();
2977 token.ToDouble(&gpsg_lon);
2978 gpsg_degs = (int)(gpsg_lon / 100.0);
2979 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
2980 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
2982 token = tkz.GetNextToken();
2983 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2984 token.Mid(0, 1).Contains(
"w") ==
true)
2985 gpsg_lon = 0. - gpsg_lon;
2987 token = tkz.GetNextToken();
2990 token = tkz.GetNextToken();
2991 token.ToDouble(&gpsg_sog);
2993 token = tkz.GetNextToken();
2994 token.ToDouble(&gpsg_cog);
2996 token = tkz.GetNextToken();
2999 token = tkz.GetNextToken();
3000 token.ToDouble(&gpsg_utc_time);
3001 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
3002 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
3004 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
3008 token = tkz.GetNextToken();
3009 int i, len, hash = 0;
3010 len = wxMin(wxStrlen(token), 20);
3011 strncpy(gpsg_name_str, token.mb_str(), len);
3012 gpsg_name_str[len] = 0;
3013 for (i = 0; i < len; i++) {
3015 hash += (int)(token[i]);
3016 while (hash >= 100000) hash = hash / 100000;
3019 gpsg_mmsi = 199000000 + hash;
3021 }
else if (!str.Mid(3, 2).IsSameAs(
"VD")) {
3022 return AIS_NMEAVDX_BAD;
3028 string_to_parse.Clear();
3030 if (str.Mid(3, 2).IsSameAs(
"VD")) {
3031 wxStringTokenizer tkz(str,
",");
3034 token = tkz.GetNextToken();
3036 token = tkz.GetNextToken();
3037 nsentences = atoi(token.mb_str());
3039 token = tkz.GetNextToken();
3040 isentence = atoi(token.mb_str());
3042 token = tkz.GetNextToken();
3043 long lsequence_id = 0;
3044 token.ToLong(&lsequence_id);
3046 token = tkz.GetNextToken();
3048 token.ToLong(&lchannel);
3053 if ((1 == nsentences) && (1 == isentence)) {
3054 string_to_parse = tkz.GetNextToken();
3057 else if (nsentences > 1) {
3058 if (1 == isentence) {
3059 sentence_accumulator = tkz.GetNextToken();
3063 sentence_accumulator += tkz.GetNextToken();
3066 if (isentence == nsentences) {
3067 string_to_parse = sentence_accumulator;
3072 if (mmsi || (!string_to_parse.IsEmpty() &&
3073 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
3075 wxCharBuffer abuf = string_to_parse.ToUTF8();
3077 return AIS_GENERIC_ERROR;
3082 if (!mmsi) mmsi = strbit.GetInt(9, 30);
3083 long mmsi_long = mmsi;
3086 int origin_mmsi = 0;
3087 int messID = strbit.GetInt(1, 6);
3089 if (messID < 1 || messID > 63)
return AIS_GENERIC_ERROR;
3091 int dac = strbit.GetInt(41, 10);
3092 int fi = strbit.GetInt(51, 6);
3094 int met_lon, met_lat;
3095 if (dac == 001 && fi == 31) {
3097 met_lon = strbit.GetInt(57, 25);
3098 met_lat = strbit.GetInt(82, 24);
3099 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
3102 }
else if (dac == 367 && fi == 33) {
3104 const int size = strbit.GetBitCount();
3105 if (size < 168)
return AIS_GENERIC_ERROR;
3106 const int startb = 56;
3107 const int slot_size = 112;
3108 const int extra_bits = (size - startb) % slot_size;
3109 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
3111 int mes_type = strbit.GetInt(57, 4);
3112 int site_ID = strbit.GetInt(77, 7);
3113 if (mes_type == 0) {
3115 met_lon = strbit.GetInt(90, 28);
3116 met_lat = strbit.GetInt(118, 27);
3117 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
3122 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
3128 return AIS_GENERIC_ERROR;
3137 auto it = AISTargetList.find(mmsi);
3138 if (it == AISTargetList.end())
3140 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3145 pTargetData->MMSI = mmsi;
3146 pTargetData->met_data.original_mmsi = origin_mmsi;
3149 pTargetData = it->second;
3152 pStaleTarget = pTargetData;
3154 pTargetData->MMSI = mmsi;
3155 pTargetData->met_data.original_mmsi = origin_mmsi;
3160 if (mmsi ==
static_cast<unsigned>(props->MMSI)) {
3162 if (TRACKTYPE_NEVER == props->TrackType) {
3163 pTargetData->b_show_track =
false;
3164 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
3165 pTargetData->b_show_track =
true;
3170 if (props->m_bignore)
return AIS_NoError;
3173 else if (props->m_bVDM) {
3175 if (str.Mid(3, 9).IsSameAs(
"VDM,1,1,,")) {
3176 int message_ID = strbit.GetInt(1, 6);
3179 if ((message_ID <= 3) || (message_ID == 18)) {
3181 pTargetData->b_OwnShip =
true;
3183 wxString aivdostr = str;
3184 aivdostr.replace(1, 5,
"AIVDO");
3185 unsigned char calculated_checksum = 0;
3186 wxString::iterator j;
3187 for (j = aivdostr.begin() + 1; j != aivdostr.end() && *j !=
'*';
3189 calculated_checksum ^=
static_cast<unsigned char>(*j);
3192 if (j <= aivdostr.end() - 3)
3195 wxString::Format(_(
"%02X"), calculated_checksum));
3197 gps_watchdog_timeout_ticks =
3200 std::string full_sentence = aivdostr.ToStdString();
3201 auto address = std::make_shared<NavAddr0183>(
"virtual");
3204 auto msg = std::make_shared<const Nmea0183Msg>(
3205 "AIVDO", full_sentence, address);
3206 NavMsgBus::GetInstance().
Notify(std::move(msg));
3216 wxDateTime now = wxDateTime::Now();
3220 last_report_ticks = pStaleTarget->PositionReportTicks;
3222 last_report_ticks = now.GetTicks();
3226 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3230 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3231 pTargetData->PositionReportTicks = now.GetTicks();
3232 pTargetData->StaticReportTicks = now.GetTicks();
3233 pTargetData->m_utc_hour = gpsg_utc_hour;
3234 pTargetData->m_utc_min = gpsg_utc_min;
3235 pTargetData->m_utc_sec = gpsg_utc_sec;
3236 pTargetData->m_date_string = gpsg_date;
3237 pTargetData->MMSI = gpsg_mmsi;
3238 pTargetData->NavStatus = 0;
3239 pTargetData->Lat = gpsg_lat;
3240 pTargetData->Lon = gpsg_lon;
3241 pTargetData->b_positionOnceValid =
true;
3242 pTargetData->COG = gpsg_cog;
3243 pTargetData->SOG = gpsg_sog;
3244 pTargetData->ShipType = 52;
3245 pTargetData->Class = AIS_GPSG_BUDDY;
3246 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
3247 pTargetData->b_nameValid =
true;
3248 pTargetData->b_active =
true;
3249 pTargetData->b_lost =
false;
3251 bdecode_result =
true;
3252 }
else if (arpa_mmsi) {
3253 pTargetData->m_utc_hour = arpa_utc_hour;
3254 pTargetData->m_utc_min = arpa_utc_min;
3255 pTargetData->m_utc_sec = arpa_utc_sec;
3256 pTargetData->MMSI = arpa_mmsi;
3257 pTargetData->NavStatus = 15;
3258 if (str.Mid(3, 3).IsSameAs(
"TLL")) {
3261 (now.GetTicks() - pTargetData->PositionReportTicks);
3262 if (age_of_last > 0) {
3263 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
3264 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
3265 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3268 pTargetData->Lat = arpa_lat;
3269 pTargetData->Lon = arpa_lon;
3270 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
3271 if (arpa_dist != 0.)
3272 ll_gc_ll(
gLat,
gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
3276 pTargetData->COG = arpa_cog;
3277 pTargetData->SOG = arpa_sog;
3279 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3280 pTargetData->PositionReportTicks = now.GetTicks();
3281 pTargetData->StaticReportTicks = now.GetTicks();
3282 pTargetData->b_positionOnceValid =
true;
3283 pTargetData->ShipType = 55;
3284 pTargetData->Class = AIS_ARPA;
3286 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
3287 if (arpa_status !=
"Q")
3288 pTargetData->b_nameValid =
true;
3290 pTargetData->b_nameValid =
false;
3291 pTargetData->b_active = !arpa_lost;
3292 pTargetData->b_lost = arpa_nottracked;
3294 bdecode_result =
true;
3295 }
else if (aprs_mmsi) {
3296 pTargetData->m_utc_hour = now.GetHour();
3297 pTargetData->m_utc_min = now.GetMinute();
3298 pTargetData->m_utc_sec = now.GetSecond();
3299 pTargetData->MMSI = aprs_mmsi;
3300 pTargetData->NavStatus = 15;
3302 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
3303 if (age_of_last > 0) {
3304 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
3305 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
3306 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3309 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3310 pTargetData->PositionReportTicks = now.GetTicks();
3311 pTargetData->StaticReportTicks = now.GetTicks();
3312 pTargetData->Lat = aprs_lat;
3313 pTargetData->Lon = aprs_lon;
3314 pTargetData->b_positionOnceValid =
true;
3315 pTargetData->ShipType = 56;
3316 pTargetData->Class = AIS_APRS;
3317 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
3318 pTargetData->b_nameValid =
true;
3319 pTargetData->b_active =
true;
3320 pTargetData->b_lost =
false;
3322 bdecode_result =
true;
3326 Parse_VDXBitstring(&strbit, pTargetData);
3330 getMmsiProperties(pTargetData);
3333 pTargetData->RecentPeriod =
3334 pTargetData->PositionReportTicks - last_report_ticks;
3339 pTargetData =
nullptr;
3344 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
3349 if ((n_msgs % 10000) == 0)
3350 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
3351 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
3357void AisDecoder::CommitAISTarget(
3358 const std::shared_ptr<AisTargetData> &pTargetData,
const wxString &str,
3359 bool message_valid,
bool new_target) {
3360 m_pLatestTargetData = pTargetData;
3362 if (!str.IsEmpty()) {
3363 if (str.Mid(3, 3).IsSameAs(
"VDO"))
3364 pTargetData->b_OwnShip =
true;
3366 pTargetData->b_OwnShip =
false;
3369 if (!pTargetData->b_OwnShip) {
3371 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
3373 pTargetData->b_PersistTrack =
false;
3378 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
3385 pTargetData->b_NoTrack =
false;
3390 if (message_valid) {
3392 if (pTargetData->MMSI) {
3393 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
3396 AISTargetList[pTargetData->MMSI] =
3399 if (!pTargetData->area_notices.empty()) {
3400 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
3401 if (it == AIS_AreaNotice_Sources.end())
3402 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
3407 if (!pTargetData->b_OwnShip) {
3408 if (pTargetData->b_positionOnceValid) {
3409 long mmsi_long = pTargetData->MMSI;
3411 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3413 pSel->SetUserData(pTargetData->MMSI);
3417 UpdateOneCPA(pTargetData.get());
3420 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3433 if (!pTargetData->b_OwnShip) {
3434 if (pTargetData->b_positionOnceValid) {
3435 long mmsi_long = pTargetData->MMSI;
3437 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3439 pSel->SetUserData(pTargetData->MMSI);
3446void AisDecoder::getAISTarget(
long mmsi,
3447 std::shared_ptr<AisTargetData> &pTargetData,
3448 std::shared_ptr<AisTargetData> &pStaleTarget,
3449 bool &bnewtarget,
int &last_report_ticks,
3451 now = wxDateTime::Now();
3452 auto it = AISTargetList.find(mmsi);
3453 if (it == AISTargetList.end())
3455 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3459 pTargetData = it->second;
3460 pStaleTarget = pTargetData;
3467 last_report_ticks = pStaleTarget->PositionReportTicks;
3469 last_report_ticks = now.GetTicks();
3473 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
3476std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
3478 double dsc_lat = 0.;
3479 double dsc_lon = 0.;
3480 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
3482 double dse_lat = 0.;
3483 double dse_lon = 0.;
3484 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
3487 int dsc_tx_mmsi = 0;
3489 double dse_cog = 0.;
3490 double dse_sog = 0.;
3491 wxString dse_shipName =
"";
3496 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
3500 wxStringTokenizer tkz(str,
",*");
3503 token = tkz.GetNextToken();
3505 if (str.Mid(3, 3).IsSameAs(
"DSC")) {
3506 m_dsc_last_string = str;
3508 token = tkz.GetNextToken();
3512 token = tkz.GetNextToken();
3514 if (dsc_fmt == 12 || dsc_fmt == 16) {
3515 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3517 token.ToDouble(&dsc_addr);
3518 dsc_mmsi = 0 - (int)(dsc_addr / 10);
3521 token = tkz.GetNextToken();
3522 token.ToLong(&dsc_cat);
3524 token = tkz.GetNextToken();
3525 if (!token.IsSameAs(
"")) {
3526 token.ToLong(&dsc_nature);
3530 token = tkz.GetNextToken();
3532 token = tkz.GetNextToken();
3533 token.ToDouble(&dsc_tmp);
3535 token = tkz.GetNextToken();
3536 token = tkz.GetNextToken();
3537 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(
"")) {
3539 dsc_tx_mmsi = dsc_mmsi;
3540 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3542 token = tkz.GetNextToken();
3543 if (dsc_fmt == 16 && dsc_cat == 12) {
3544 if (!token.IsSameAs(
"")) {
3545 token.ToLong(&dsc_nature);
3549 token = tkz.GetNextToken();
3550 token = tkz.GetNextToken();
3552 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
3554 if (dsc_quadrant > 3)
3557 dsc_lat = (int)(dsc_tmp / 100000.0);
3558 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
3559 dsc_lat = dsc_lat - dsc_quadrant * 10000;
3560 dsc_degs = (int)(dsc_lat / 100.0);
3561 dsc_mins = dsc_lat - dsc_degs * 100.0;
3562 dsc_lat = dsc_degs + dsc_mins / 60.0;
3564 dsc_degs = (int)(dsc_lon / 100.0);
3565 dsc_mins = dsc_lon - dsc_degs * 100.0;
3566 dsc_lon = dsc_degs + dsc_mins / 60.0;
3567 switch (dsc_quadrant) {
3583 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
3585 }
else if (str.Mid(3, 3).IsSameAs(
"DSE")) {
3586 token = tkz.GetNextToken();
3587 token = tkz.GetNextToken();
3588 token = tkz.GetNextToken();
3589 token = tkz.GetNextToken();
3590 dse_mmsi = wxAtoi(token.Mid(
3596 token = tkz.GetNextToken();
3599 token.ToDouble(&dse_tmp);
3600 dse_lat = (int)(dse_tmp / 10000.0);
3601 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3602 dse_lat = dse_lat / 600000.0;
3603 dse_lon = dse_lon / 600000.0;
3606 while (tkz.HasMoreTokens()) {
3607 dseSymbol = tkz.GetNextToken();
3608 token = tkz.GetNextToken();
3609 if (dseSymbol.IsSameAs(
"00")) {
3610 token.ToDouble(&dse_tmp);
3611 dse_lat = (int)(dse_tmp / 10000.0);
3612 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3613 dse_lat = dse_lat / 600000.0;
3614 dse_lon = dse_lon / 600000.0;
3615 }
else if (dseSymbol.IsSameAs(
"01")) {
3616 }
else if (dseSymbol.IsSameAs(
"02")) {
3617 token.ToDouble(&dse_tmp);
3618 dse_sog = dse_tmp / 10.0;
3619 }
else if (dseSymbol.IsSameAs(
"03")) {
3620 token.ToDouble(&dse_tmp);
3621 dse_cog = dse_tmp / 10.0;
3622 }
else if (dseSymbol.IsSameAs(
"04")) {
3623 dse_shipName = DecodeDSEExpansionCharacters(token);
3624 }
else if (dseSymbol.IsSameAs(
"05")) {
3625 }
else if (dseSymbol.IsSameAs(
"06")) {
3628 mmsi = abs((
int)dse_mmsi);
3632 wxDateTime now = wxDateTime::Now();
3634 int last_report_ticks = now.GetTicks();
3637 auto it = AISTargetList.find(mmsi);
3638 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
3639 if (it == AISTargetList.end()) {
3641 pStaleTarget = it->second;
3642 last_report_ticks = pStaleTarget->PositionReportTicks;
3648 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
3650 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
3651 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
3653 m_ptentative_dsctarget->MMSI = mmsi;
3654 m_ptentative_dsctarget->NavStatus =
3656 m_ptentative_dsctarget->Lat = dsc_lat;
3657 m_ptentative_dsctarget->Lon = dsc_lon;
3658 m_ptentative_dsctarget->b_positionOnceValid =
true;
3659 m_ptentative_dsctarget->COG = 0;
3660 m_ptentative_dsctarget->SOG = 0;
3661 m_ptentative_dsctarget->ShipType = dsc_fmt;
3662 m_ptentative_dsctarget->Class = AIS_DSC;
3663 m_ptentative_dsctarget->b_isDSCtarget =
true;
3664 m_ptentative_dsctarget->b_nameValid =
true;
3665 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
3666 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %u",
3668 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
3669 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
3671 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %u",
3675 m_ptentative_dsctarget->b_active =
true;
3676 m_ptentative_dsctarget->b_lost =
false;
3677 m_ptentative_dsctarget->RecentPeriod =
3678 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3681 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
3686 if (dse_mmsi || b_take_dsc) {
3687 if (m_ptentative_dsctarget) {
3693 m_ptentative_dsctarget->Lat =
3694 m_ptentative_dsctarget->Lat +
3695 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
3696 m_ptentative_dsctarget->Lon =
3697 m_ptentative_dsctarget->Lon +
3698 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
3699 if (!dse_shipName.empty()) {
3700 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
3701 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"%s",
3702 dse_shipName.ToAscii().data());
3704 m_ptentative_dsctarget->COG = dse_cog;
3705 m_ptentative_dsctarget->SOG = dse_sog;
3709 m_ptentative_dsctarget->RecentPeriod =
3710 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3715 auto found = AISTargetList.find(mmsi);
3716 if (found == AISTargetList.end()) {
3717 pTargetData = m_ptentative_dsctarget;
3719 pTargetData = found->second;
3720 std::vector<AISTargetTrackPoint> ptrack =
3721 std::move(pTargetData->m_ptrack);
3722 pTargetData->CloneFrom(
3723 m_ptentative_dsctarget
3726 pTargetData->m_ptrack =
3733 m_ptentative_dsctarget =
nullptr;
3735 m_pLatestTargetData = pTargetData;
3737 AISTargetList[pTargetData->MMSI] =
3740 long mmsi_long = pTargetData->MMSI;
3744 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3747 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
3748 (
void *)mmsi_long, SELTYPE_AISTARGET);
3749 pSel->SetUserData(pTargetData->MMSI);
3752 UpdateOneCPA(pTargetData.get());
3755 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3762bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3763 unsigned char checksum_value = 0;
3764 int sentence_hex_sum;
3766 wxCharBuffer buf = str_in.ToUTF8();
3767 if (!buf.data())
return false;
3769 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3770 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3771 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3773 int string_length = strlen(str_ascii);
3775 int payload_length = 0;
3776 while ((payload_length < string_length) &&
3777 (str_ascii[payload_length] !=
'*'))
3780 if (payload_length == string_length)
3785 while (index < payload_length) {
3786 checksum_value ^= str_ascii[index];
3790 if (string_length > 4) {
3792 scanstr[0] = str_ascii[payload_length + 1];
3793 scanstr[1] = str_ascii[payload_length + 2];
3795 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3797 if (sentence_hex_sum == checksum_value)
return true;
3803void AisDecoder::UpdateAllCPA() {
3805 for (
const auto &it : GetTargetList()) {
3806 std::shared_ptr<AisTargetData> td = it.second;
3808 if (
nullptr != td) UpdateOneCPA(td.get());
3812void AisDecoder::UpdateAllTracks() {
3813 for (
const auto &it : GetTargetList()) {
3814 std::shared_ptr<AisTargetData> td = it.second;
3815 if (td) UpdateOneTrack(td.get());
3821 if (!ptarget->b_positionOnceValid)
return;
3823 if (!ptarget->m_ptrack.empty()) {
3825 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3826 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3829 ptarget->m_ptrack.pop_back();
3830 ptarget->b_positionDoubtful =
true;
3837 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3840 ptrackpoint.m_lat = ptarget->Lat;
3841 ptrackpoint.m_lon = ptarget->Lon;
3842 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3844 ptarget->m_ptrack.push_back(ptrackpoint);
3846 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3848 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3850 t->SetName(wxString::Format(
3851 "AIS %s (%u) %s %s", ptarget->GetFullName().c_str(), ptarget->MMSI,
3852 wxDateTime::Now().FormatISODate().c_str(),
3853 wxDateTime::Now().FormatISOTime().c_str()));
3856 m_persistent_tracks[ptarget->MMSI] = t;
3858 t = m_persistent_tracks[ptarget->MMSI];
3861 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3863 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3866 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3867 tp1->m_lon, tp, tp1, t);
3877 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3879 ptarget->m_ptrack.erase(
3880 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3882 return track.m_time < test_time;
3884 ptarget->m_ptrack.end());
3889void AisDecoder::DeletePersistentTrack(
const Track *track) {
3890 for (
auto it = m_persistent_tracks.begin(); it != m_persistent_tracks.end();
3892 if (it->second == track) {
3893 unsigned mmsi = it->first;
3894 m_persistent_tracks.erase(it);
3896 if (0 == m_persistent_tracks.count(mmsi)) {
3900 if (props->m_bPersistentTrack) {
3904 std::shared_ptr<AisTargetData> td =
3905 Get_Target_Data_From_MMSI(mmsi);
3907 props->m_bPersistentTrack =
false;
3908 td->b_mPropPersistTrack =
false;
3910 if (!m_callbacks.confirm_stop_track()) {
3911 props->m_bPersistentTrack =
true;
3923void AisDecoder::UpdateAllAlarms() {
3924 m_bGeneralAlert =
false;
3927 for (
const auto &it : GetTargetList()) {
3928 std::shared_ptr<AisTargetData> td = it.second;
3932 if (!m_bGeneralAlert) {
3934 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3935 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3936 m_bGeneralAlert =
true;
3939 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3940 m_bGeneralAlert =
false;
3943 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3944 m_bGeneralAlert =
false;
3947 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3950 if (td->Class == AIS_SART && td->NavStatus == 14)
3951 m_bGeneralAlert =
true;
3954 if ((td->Class == AIS_DSC) &&
3955 ((td->ShipType == 12) || (td->ShipType == 16)))
3956 m_bGeneralAlert =
true;
3959 ais_alert_type this_alarm = AIS_NO_ALERT;
3962 if (td->Class == AIS_SART && td->NavStatus == 14)
3963 this_alarm = AIS_ALERT_SET;
3966 if ((td->Class == AIS_DSC) &&
3967 ((td->ShipType == 12) || (td->ShipType == 16)))
3968 this_alarm = AIS_ALERT_SET;
3970 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3971 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3974 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3975 td->n_alert_state = AIS_NO_ALERT;
3981 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3982 (td->SOG <= g_ShowMoored_Kts)) {
3983 td->n_alert_state = AIS_NO_ALERT;
3989 if (td->Range_NM > g_CPAMax_NM) {
3990 td->n_alert_state = AIS_NO_ALERT;
3995 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3996 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3997 (td->Class != AIS_METEO)) {
3999 if (td->TCPA < g_TCPA_Max) {
4000 if (td->b_isFollower)
4001 this_alarm = AIS_ALERT_NO_DIALOG_SET;
4003 this_alarm = AIS_ALERT_SET;
4006 if (td->b_isFollower)
4007 this_alarm = AIS_ALERT_NO_DIALOG_SET;
4009 this_alarm = AIS_ALERT_SET;
4016 if (td->Class == AIS_SART) {
4017 if (td->b_in_ack_timeout) {
4018 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
4020 if (delta.GetMinutes() >= 10) td->b_in_ack_timeout =
false;
4022 }
else if (g_bAIS_ACK_Timeout ||
4023 ((td->Class == AIS_DSC) &&
4024 ((td->ShipType == 12) || (td->ShipType == 16)))) {
4025 if (td->b_in_ack_timeout) {
4026 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
4027 if (delta.GetMinutes() > g_AckTimeout_Mins)
4028 td->b_in_ack_timeout =
false;
4034 if (td->b_in_ack_timeout) {
4035 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
4039 td->n_alert_state = this_alarm;
4045 ptarget->Range_NM = -1.;
4054 DistanceBearingMercator(ptarget->Lat, ptarget->Lon,
gLat,
gLon, &brg, &dist);
4055 ptarget->Range_NM = dist;
4058 if (dist <= 1e-5) ptarget->Brg = -1.0;
4060 if (!ptarget->b_positionOnceValid || !
bGPSValid) {
4061 ptarget->bCPA_Valid =
false;
4065 if (ptarget->Class == AIS_METEO) {
4066 ptarget->bCPA_Valid =
false;
4075 if (ptarget->b_OwnShip) {
4077 ptarget->TCPA = -100;
4078 ptarget->bCPA_Valid =
false;
4082 double cpa_calc_ownship_cog =
gCog;
4083 double cpa_calc_target_cog = ptarget->COG;
4086 if (std::isnan(
gSog) || (
gSog > 102.2)) {
4087 ptarget->bCPA_Valid =
false;
4092 if (std::isnan(
gCog) ||
gCog == 360.0) {
4094 cpa_calc_ownship_cog =
4098 ptarget->bCPA_Valid =
false;
4104 if (ptarget->COG == 360.0) {
4105 if (ptarget->SOG > 102.2) {
4106 ptarget->bCPA_Valid =
false;
4108 }
else if (ptarget->SOG < .01) {
4109 cpa_calc_target_cog = 0.;
4112 ptarget->bCPA_Valid =
false;
4118 double v0 =
gSog * 1852.;
4119 double v1 = ptarget->SOG * 1852.;
4121 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4125 ptarget->bCPA_Valid =
false;
4132 double east1 = (ptarget->Lon -
gLon) * 60 * 1852;
4133 double north1 = (ptarget->Lat -
gLat) * 60 * 1852;
4135 double east = east1 * (cos(
gLat * PI / 180.));
4137 double north = north1;
4140 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4141 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4142 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4143 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4146 double fc = (v0 * cosa) - (v1 * cosb);
4147 double fs = (v0 * sina) - (v1 * sinb);
4149 double d = (fc * fc) + (fs * fs);
4157 tcpa = ((fc * east) + (fs * north)) / d;
4160 ptarget->TCPA = tcpa * 60.;
4165 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4167 ll_gc_ll(
gLat,
gLon, cpa_calc_ownship_cog,
gSog * tcpa, &OwnshipLatCPA,
4169 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4170 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4173 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4176 ptarget->bCPA_Valid =
true;
4178 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4182void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4185 if (m_ptentative_dsctarget) {
4186 ProcessDSx(m_dsc_last_string,
true);
4190void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4195 wxDateTime now = wxDateTime::Now();
4198 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4201 auto it = current_targets.begin();
4202 std::vector<int> remove_array;
4204 while (it != current_targets.end()) {
4208 current_targets.erase(it);
4213 std::shared_ptr<AisTargetData> xtd = it->second;
4215 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4216 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4228 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4230 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4231 double iECD_LostTimeOut = 0.0;
4235 if (xtd->Class == AIS_CLASS_A) {
4236 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4238 iECD_LostTimeOut = 18 * 60;
4240 iECD_LostTimeOut = 60;
4242 iECD_LostTimeOut = 60;
4243 }
else if (xtd->Class == AIS_CLASS_B) {
4245 iECD_LostTimeOut = 18 * 60;
4247 iECD_LostTimeOut = 180;
4248 }
else if (xtd->Class == AIS_ATON || xtd->Class == AIS_BASE)
4249 iECD_LostTimeOut = 18 * 60;
4251 if ((target_posn_age > iECD_LostTimeOut) &&
4252 (xtd->Class != AIS_GPSG_BUDDY))
4253 xtd->b_active =
false;
4255 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4257 }
else if (g_bMarkLost) {
4258 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4259 (xtd->Class != AIS_GPSG_BUDDY) && (xtd->Class != AIS_SART))
4260 xtd->b_active =
false;
4263 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4264 removelost_Mins = 18.0;
4268 if (g_bRemoveLost || g_bInlandEcdis) {
4270 (xtd->Class == AIS_ARPA &&
4272 if (((target_posn_age > removelost_Mins * 60) &&
4273 (xtd->Class != AIS_GPSG_BUDDY)) ||
4278 xtd->b_positionOnceValid =
false;
4286 long mmsi_long = xtd->MMSI;
4287 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4292 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4293 xtd->b_removed =
true;
4295 remove_array.push_back(xtd->MMSI);
4304 if (xtd->MMSI == props->MMSI) {
4305 if (props->m_bignore) {
4306 remove_array.push_back(xtd->MMSI);
4307 xtd->b_removed =
true;
4316 remove_array.push_back(xtd->MMSI);
4317 xtd->b_removed =
true;
4325 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4326 auto itd = current_targets.find(remove_array[i]);
4327 if (itd != current_targets.end()) {
4328 std::shared_ptr<AisTargetData> td = itd->second;
4329 current_targets.erase(itd);
4338 m_bSuppressed =
false;
4339 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4340 (g_bShowScaled && g_bAllowShowScaled))
4341 m_bSuppressed =
true;
4343 m_bAIS_Audio_Alert_On =
false;
4352 std::shared_ptr<AisTargetData> palert_target =
nullptr;
4354 if (!g_pais_alert_dialog_active) {
4356 double tcpa_min = 1e6;
4357 double sart_range = 1e6;
4358 std::shared_ptr<AisTargetData> palert_target_cpa =
nullptr;
4359 std::shared_ptr<AisTargetData> palert_target_sart =
nullptr;
4360 std::shared_ptr<AisTargetData> palert_target_dsc =
nullptr;
4362 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4363 std::shared_ptr<AisTargetData> td = it->second;
4365 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4366 if (g_bAIS_CPA_Alert && td->b_active) {
4367 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4368 if (td->TCPA < tcpa_min) {
4369 tcpa_min = td->TCPA;
4370 palert_target_cpa = td;
4374 }
else if ((td->Class == AIS_DSC) &&
4375 ((td->ShipType == 12) || (td->ShipType == 16))) {
4377 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4378 palert_target_dsc = td;
4381 td->b_isDSCtarget =
false;
4386 else if (td->Class == AIS_SART) {
4388 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4389 if (td->Range_NM < sart_range) {
4390 tcpa_min = sart_range;
4391 palert_target_sart = td;
4401 palert_target = palert_target_cpa;
4403 if (palert_target_sart) {
4404 palert_target = palert_target_sart;
4407 if (palert_target_dsc) {
4408 palert_target = palert_target_dsc;
4412 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4417 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4420std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
4422 if (AISTargetList.find(mmsi) == AISTargetList.end())
return nullptr;
4423 return AISTargetList[mmsi];
4430MmsiProperties::MmsiProperties(wxString &spec) {
4432 wxStringTokenizer tkz(spec,
";");
4435 s = tkz.GetNextToken();
4440 s = tkz.GetNextToken();
4442 if (s.Upper() ==
"ALWAYS")
4443 TrackType = TRACKTYPE_ALWAYS;
4444 else if (s.Upper() ==
"NEVER")
4445 TrackType = TRACKTYPE_NEVER;
4448 s = tkz.GetNextToken();
4450 if (s.Upper() ==
"IGNORE") m_bignore =
true;
4453 s = tkz.GetNextToken();
4455 if (s.Upper() ==
"MOB") m_bMOB =
true;
4458 s = tkz.GetNextToken();
4460 if (s.Upper() ==
"VDM") m_bVDM =
true;
4463 s = tkz.GetNextToken();
4465 if (s.Upper() ==
"FOLLOWER") m_bFollower =
true;
4468 s = tkz.GetNextToken();
4470 if (s.Upper() ==
"PERSIST") m_bPersistentTrack =
true;
4473 s = tkz.GetNextToken();
4475 m_ShipName = s.Upper();
4479MmsiProperties::~MmsiProperties() =
default;
4481void MmsiProperties::Init() {
4483 TrackType = TRACKTYPE_DEFAULT;
4487 m_bFollower =
false;
4488 m_bPersistentTrack =
false;
4492wxString MmsiProperties::Serialize() {
4496 sMMSI.Printf(
"%d", MMSI);
4500 if (TRACKTYPE_ALWAYS == TrackType)
4502 else if (TRACKTYPE_NEVER == TrackType)
4523 sMMSI <<
"Follower";
4527 if (m_bPersistentTrack) {
4532 if (m_ShipName ==
"") {
4533 m_ShipName = GetShipNameFromFile(MMSI);
4535 sMMSI << m_ShipName;
4540 AIS_Target_Name_Hash *AISTargetNamesC,
4541 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4542 if (g_benableAISNameCache) {
4543 wxString ship_name =
"";
4546 if (!pTargetData->b_nameValid) {
4547 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4548 if (it != AISTargetNamesC->end()) {
4549 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4550 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4551 ship_name.length() + 1);
4552 pTargetData->b_nameValid =
true;
4553 pTargetData->b_nameFromCache =
true;
4554 }
else if (!g_bUseOnlyConfirmedAISName) {
4555 it = AISTargetNamesNC->find(mmsi);
4556 if (it != AISTargetNamesNC->end()) {
4557 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4558 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4559 ship_name.length() + 1);
4560 pTargetData->b_nameValid =
true;
4561 pTargetData->b_nameFromCache =
true;
4566 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4567 (pTargetData->MID == 19) ||
4568 (pTargetData->MID == 123) ||
4569 (pTargetData->MID == 124)) {
4571 pTargetData->b_nameFromCache =
false;
4572 ship_name = trimAISField(pTargetData->ShipName);
4573 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4574 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4576 AISTargetNamesC->end()) {
4577 if ((*AISTargetNamesC)[mmsi] ==
4579 if (itNC != AISTargetNamesNC->end()) {
4581 AISTargetNamesNC->erase(itNC);
4584 if (itNC != AISTargetNamesNC->end()) {
4586 if ((*AISTargetNamesNC)[mmsi] ==
4589 (*AISTargetNamesC)[mmsi] = ship_name;
4591 AISTargetNamesNC->erase(itNC);
4594 (*AISTargetNamesNC)[mmsi] = ship_name;
4596 if (g_bUseOnlyConfirmedAISName)
4597 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4598 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4600 (*AISTargetNamesNC)[mmsi] = ship_name;
4605 AISTargetNamesNC->end()) {
4606 if ((*AISTargetNamesNC)[mmsi] ==
4609 (*AISTargetNamesC)[mmsi] = ship_name;
4611 AISTargetNamesNC->erase(itNC);
4613 (*AISTargetNamesNC)[mmsi] = ship_name;
4616 (*AISTargetNamesNC)[mmsi] = ship_name;
4618 if (g_bUseOnlyConfirmedAISName) {
4619 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4620 strncpy(pTargetData->ShipName,
"Unknown ",
4628wxString GetShipNameFromFile(
int nmmsi) {
4630 if (g_benableAISNameCache) {
4634 while (getline(infile, line)) {
4635 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4636 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4637 name = tokenizer.GetNextToken().Trim();
4640 tokenizer.GetNextToken();
4648void AisDecoder::UpdateMMSItoNameFile(
const wxString &mmsi,
4649 const wxString &name) {
4653 std::map<wxString, wxString> mmsi_name_map;
4659 while (getline(infile, line)) {
4660 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4661 wxString file_mmsi = tokenizer.GetNextToken();
4662 wxString file_name = tokenizer.GetNextToken().Trim();
4663 mmsi_name_map[file_mmsi] = file_name;
4669 mmsi_name_map[mmsi] = name.Upper();
4674 for (
const auto &pair : mmsi_name_map) {
4675 std::string line = std::string(pair.first.mb_str()) +
"," +
4676 std::string(pair.second.mb_str()) +
"\n";
4683wxString AisDecoder::GetMMSItoNameEntry(
const wxString &mmsi) {
4684 return GetShipNameFromFile(wxAtoi(mmsi));
4688int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4692 if ((!lon_bits || lon_bits == 999) && siteID) {
4695 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4696 if (points.size()) {
4697 for (
const auto &point : points) {
4699 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4701 new_mmsi = point.mmsi;
4707 if (!found && !lon_bits) {
4712 double lon_tentative = 181.;
4713 double lat_tentative = 91.;
4715 if (lon_bits == 25) {
4716 if (m_lon & 0x01000000)
4717 m_lon |= 0xFE000000;
4718 lon_tentative = m_lon / 60000.;
4720 if (m_lat & 0x00800000)
4721 m_lat |= 0xFF000000;
4722 lat_tentative = m_lat / 60000.;
4724 }
else if (lon_bits == 28) {
4725 if (m_lon & 0x08000000)
4726 m_lon |= 0xf0000000;
4727 lon_tentative = m_lon / 600000.;
4729 if (m_lat & 0x04000000)
4730 m_lat |= 0xf8000000;
4731 lat_tentative = m_lat / 600000.;
4736 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4737 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4745 static int nextMeteommsi = 199400000;
4746 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4748 if (lon_bits != 999 && !points.empty()) {
4749 wxString t_lat, t_lon;
4750 for (
const auto &point : points) {
4752 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4754 new_mmsi = point.mmsi;
4763 points.emplace_back(
4764 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4765 new_mmsi = nextMeteommsi;
Select * pSelectAIS
Global instance.
wxString AISTargetNameFileName
Global instance.
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
unsigned g_OwnShipmmsi
Global instance.
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
Select * pSelectAIS
Global instance.
wxString AISTargetNameFileName
Global instance.
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
unsigned g_OwnShipmmsi
Global instance.
Global state for AIS decoder.
int GetInt(int sp, int len, bool signed_flag=false)
sp is starting bit, 1-based
obs::EventVar info_update
Notified when AIS user dialogs should update.
obs::EventVar plugin_msg_evt
A JSON message should be sent.
obs::EventVar touch_state
Notified when gFrame->TouchAISActive() should be invoked.
obs::EventVar new_track
Notified on new track creation.
Add a new point to the list of Meteo stations.
Process incoming AIS messages.
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString &error_msg="") const
Logs an input message with context information.
void Notify(std::shared_ptr< const NavMsg > message)
Accept message received by driver, make it available for upper layers.
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 listeners on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Represents a waypoint or mark within the navigation system.
A parsed SignalK message over ipv4.
Represents a single point in a track.
Represents a track, which is a series of connected track points.
void Notify() override
Notify all listeners, no data supplied.
Raw messages layer, supports sending and recieving navmsg messages.
int g_iSpeedFormat
User-selected speed unit format for display and input.
int g_iDistanceFormat
User-selected distance (horizontal) unit format for display and input.
Global variables stored in configuration file.
GUI constant definitions.
Meteo points are Meteorological and Hydrographic data received by NMEA0183 (AIS) VDM message 8 dac:00...
Multiplexer class and helpers.
const wxChar * ParseGPXDateTime(wxDateTime &dt, const wxChar *datetime)
This function parses a string containing a GPX time representation and returns a wxDateTime containin...
double fromUsrDistance(double usr_distance, int unit, int default_val)
Convert distance from user units to nautical miles.
Navigation Utility Functions without GUI dependencies.
bool bGPSValid
Indicate whether the Global Navigation Satellite System (GNSS) has a valid position.
double gHdt
True heading in degrees (0-359.99).
double gLat
Vessel's current latitude in decimal degrees.
double gCog
Course over ground in degrees (0-359.99).
double gSog
Speed over ground in knots.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
Waypoint or mark abstraction.
Route * pAISMOBRoute
Global instance.
Select * pSelect
Global instance.
Selected route, segment, waypoint, etc.
A generic position and navigation data structure.
double kCog
Course over ground in degrees.
double kHdt
True heading in degrees.
double kHdm
Magnetic heading in degrees.
double kLat
Latitude in decimal degrees.
double kSog
Speed over ground in knots.
double kVar
Magnetic variation in degrees.
double kLon
Longitude in decimal degrees.
std::vector< Track * > g_TrackList
Global instance.
Recorded track abstraction.