40#include <wx/datetime.h>
44#include <wx/textfile.h>
46#include <wx/tokenzr.h>
47#include <wx/filename.h>
51#include "rapidjson/document.h"
52#include "rapidjson/writer.h"
53#include "rapidjson/stringbuffer.h"
61#include "model/geodesic.h"
73static const long long lNaN = 0xfff8000000000000;
74#define NAN (*(double *)&lNaN)
77wxEvtHandler *g_pais_alert_dialog_active;
81wxString GetShipNameFromFile(
int);
105EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
106EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
109static constexpr
double ms_to_knot_factor = 1.9438444924406;
115static
bool b_firstrx;
116static
int first_rx_ticks;
118static
double arpa_ref_hdg = NAN;
120static inline
double GeodesicRadToDeg(
double rads) {
121 return rads * 180.0 / M_PI;
124static inline double MS2KNOTS(
double ms) {
return ms * 1.9438444924406; }
126static bool isBuoyMmsi(
unsigned msi) {
131 int mid = msi / 1000000;
132 if ((mid > 200 && mid < 880) || mid >= 970) {
141int AisMeteoNewMmsi(
int,
int,
int,
int,
int);
145 AIS_Target_Name_Hash *AISTargetNamesC,
146 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
147static void BuildERIShipTypeHash() {
148 make_hash_ERI(8000, _(
"Vessel, type unknown"));
149 make_hash_ERI(8150, _(
"Freightbarge"));
150 make_hash_ERI(8160, _(
"Tankbarge"));
151 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
152 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
153 make_hash_ERI(8430, _(
"Pushboat, single"));
154 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
155 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
156 make_hash_ERI(8490, _(
"Bunkership"));
157 make_hash_ERI(8010, _(
"Motor freighter"));
158 make_hash_ERI(8020, _(
"Motor tanker"));
159 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
160 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
161 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
162 make_hash_ERI(8030, _(
"Container vessel"));
163 make_hash_ERI(8040, _(
"Gas tanker"));
164 make_hash_ERI(8050, _(
"Motor freighter, tug"));
165 make_hash_ERI(8060, _(
"Motor tanker, tug"));
166 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
167 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
168 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
169 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
170 make_hash_ERI(8110, _(
"Tug, freighter"));
171 make_hash_ERI(8120, _(
"Tug, tanker"));
172 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
173 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
174 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
175 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
176 make_hash_ERI(8170, _(
"Freightbarge with containers"));
177 make_hash_ERI(8180, _(
"Tankbarge, gas"));
178 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
179 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
180 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
181 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
182 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
183 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
184 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
185 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
186 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
187 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
189 _(
"Pushtow, two barges at least one tanker or gas barge"));
191 _(
"Pushtow, three barges at least one tanker or gas barge"));
193 _(
"Pushtow, four barges at least one tanker or gas barge"));
195 _(
"Pushtow, five barges at least one tanker or gas barge"));
197 _(
"Pushtow, six barges at least one tanker or gas barge"));
199 _(
"Pushtow, seven barges at least one tanker or gas barge"));
201 _(
"Pushtow, eight barges at least one tanker or gas barge"));
203 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
204 make_hash_ERI(8400, _(
"Tug, single"));
205 make_hash_ERI(8410, _(
"Tug, one or more tows"));
206 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
207 make_hash_ERI(8430, _(
"Pushboat, single"));
208 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
209 make_hash_ERI(8441, _(
"Ferry"));
210 make_hash_ERI(8442, _(
"Red cross ship"));
211 make_hash_ERI(8443, _(
"Cruise ship"));
212 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
213 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
214 "cable-ship, buoy-ship, dredge"));
215 make_hash_ERI(8480, _(
"Fishing boat"));
216 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
217 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
218 make_hash_ERI(1510, _(
"Unit carrier maritime"));
219 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
220 make_hash_ERI(1530, _(
"Tanker"));
221 make_hash_ERI(1540, _(
"Liquified gas tanker"));
222 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
223 make_hash_ERI(1900, _(
"Fast ship"));
224 make_hash_ERI(1910, _(
"Hydrofoil"));
228static wxString DecodeDSEExpansionCharacters(
const wxString &dseData) {
230 char lookupTable[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
' ',
231 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
232 'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
233 'W',
'X',
'Y',
'Z',
'.',
',',
'-',
'/',
' '};
235 for (
size_t i = 0; i < dseData.length(); i += 2) {
237 lookupTable[strtol(dseData.Mid(i, 2).data(),
nullptr, 10)]);
242static void getMmsiProperties(std::shared_ptr<AisTargetData> &pTargetData) {
246 pTargetData->b_isFollower = props->m_bFollower;
247 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
248 if (TRACKTYPE_NEVER == props->TrackType) {
249 pTargetData->b_show_track =
false;
250 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
251 pTargetData->b_show_track =
true;
262 const std::shared_ptr<AisTargetData> &ptd) {
263 bool parse_result =
false;
264 bool b_posn_report =
false;
266 wxDateTime now = wxDateTime::Now();
268 int message_ID = bstr->
GetInt(1, 6);
269 ptd->MID = message_ID;
272 int met_mmsi = ptd->MMSI;
275 ptd->MMSI = bstr->
GetInt(9, 30);
277 switch (message_ID) {
283 ptd->NavStatus = bstr->
GetInt(39, 4);
284 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
286 int lon = bstr->
GetInt(62, 28);
287 if (lon & 0x08000000)
289 double lon_tentative = lon / 600000.;
291 int lat = bstr->
GetInt(90, 27);
292 if (lat & 0x04000000)
294 double lat_tentative = lat / 600000.;
296 if ((lon_tentative <= 180.) &&
300 ptd->Lon = lon_tentative;
301 ptd->Lat = lat_tentative;
302 ptd->b_positionDoubtful =
false;
303 ptd->b_positionOnceValid =
true;
304 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
305 ptd->PositionReportTicks = now.GetTicks();
307 ptd->b_positionDoubtful =
true;
310 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
311 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
313 ptd->ROTAIS = bstr->
GetInt(43, 8);
314 double rot_dir = 1.0;
316 if (ptd->ROTAIS == 128)
318 else if ((ptd->ROTAIS & 0x80) == 0x80) {
319 ptd->ROTAIS = ptd->ROTAIS - 256;
323 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
326 ptd->m_utc_sec = bstr->
GetInt(138, 6);
328 if ((1 == message_ID) ||
331 ptd->SyncState = bstr->
GetInt(151, 2);
332 ptd->SlotTO = bstr->
GetInt(153, 2);
333 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
335 ptd->m_utc_hour = bstr->
GetInt(155, 5);
337 ptd->m_utc_min = bstr->
GetInt(160, 7);
339 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
340 (ptd->m_utc_sec < 60)) {
341 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
342 rx_ticks = rx_time.GetTicks();
344 first_rx_ticks = rx_ticks;
352 ptd->blue_paddle = bstr->
GetInt(144, 2);
353 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
355 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
358 int mmsi_start = ptd->MMSI / 10000000;
360 if (mmsi_start == 97) {
361 ptd->Class = AIS_SART;
362 ptd->StaticReportTicks =
377 b_posn_report =
true;
378 ptd->b_VDM_secure_Pos =
true;
387 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
389 int lon = bstr->
GetInt(58, 28);
390 if (lon & 0x08000000)
392 double lon_tentative = lon / 600000.;
394 int lat = bstr->
GetInt(86, 27);
395 if (lat & 0x04000000)
397 double lat_tentative = lat / 600000.;
399 if ((lon_tentative <= 180.) &&
403 ptd->Lon = lon_tentative;
404 ptd->Lat = lat_tentative;
405 ptd->b_positionDoubtful =
false;
406 ptd->b_positionOnceValid =
true;
407 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
408 ptd->PositionReportTicks = now.GetTicks();
410 ptd->b_positionDoubtful =
true;
412 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
413 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
415 ptd->m_utc_sec = bstr->
GetInt(134, 6);
417 if (!ptd->b_isDSCtarget) {
418 if (!isBuoyMmsi(ptd->MMSI))
419 ptd->Class = AIS_CLASS_B;
421 ptd->Class = AIS_BUOY;
424 b_posn_report =
true;
425 ptd->b_VDM_secure_Pos =
true;
433 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
434 int lon = bstr->
GetInt(58, 28);
435 if (lon & 0x08000000)
437 double lon_tentative = lon / 600000.;
439 int lat = bstr->
GetInt(86, 27);
440 if (lat & 0x04000000)
442 double lat_tentative = lat / 600000.;
444 if ((lon_tentative <= 180.) &&
448 ptd->Lon = lon_tentative;
449 ptd->Lat = lat_tentative;
450 ptd->b_positionDoubtful =
false;
451 ptd->b_positionOnceValid =
true;
452 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
453 ptd->PositionReportTicks = now.GetTicks();
455 ptd->b_positionDoubtful =
true;
457 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
458 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
459 ptd->m_utc_sec = bstr->
GetInt(134, 6);
461 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
462 ptd->b_nameValid =
true;
463 if (!ptd->b_isDSCtarget) {
464 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
466 ptd->DimA = bstr->
GetInt(272, 9);
467 ptd->DimB = bstr->
GetInt(281, 9);
468 ptd->DimC = bstr->
GetInt(290, 6);
469 ptd->DimD = bstr->
GetInt(296, 6);
471 if (!ptd->b_isDSCtarget) {
473 if (!isBuoyMmsi(ptd->MMSI))
474 ptd->Class = AIS_CLASS_B;
476 ptd->Class = AIS_BUOY;
479 b_posn_report =
true;
480 ptd->b_VDM_secure_Pos =
true;
492 if (ptd->b_VDM_secure_Pos)
break;
497 int bitCorrection = 10;
501 double lon_tentative = 181.;
502 double lat_tentative = 91.;
505 printf(
"AIS Message 27 - received:\r\n");
506 printf(
"MMSI : %i\r\n", ptd->MMSI);
512 ptd->Class == AIS_CLASS_A ? bstr->
GetInt(41, 4) : UNDEFINED;
514 int lon = bstr->
GetInt(45, 18);
515 int lat = bstr->
GetInt(63, 17);
521 if (lat >= (0x4000000 >> bitCorrection)) {
522 lat_tentative = (0x8000000 >> bitCorrection) - lat;
527 if (lon >= (0x8000000 >> bitCorrection)) {
528 lon_tentative = (0x10000000 >> bitCorrection) - lon;
533 lat_tentative = lat_tentative / resolution / 60.0;
534 lon_tentative = lon_tentative / resolution / 60.0;
537 printf(
"Latitude : %f\r\n", lat_tentative);
538 printf(
"Longitude : %f\r\n", lon_tentative);
542 int positionLatency = bstr->
GetInt(95, 1);
544 if ((lon_tentative <= 180.) &&
548 ptd->Lon = lon_tentative;
549 ptd->Lat = lat_tentative;
550 ptd->b_positionDoubtful =
false;
551 ptd->b_positionOnceValid =
true;
552 if (positionLatency == 0) {
555 printf(
"Low latency position report.\r\n");
557 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
558 ptd->PositionReportTicks = now.GetTicks();
561 ptd->b_positionDoubtful =
true;
563 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
564 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
566 b_posn_report =
true;
572 if (bstr->GetBitCount() >= 424) {
574 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
580 int AIS_version_indicator = bstr->
GetInt(39, 2);
581 if (AIS_version_indicator < 4) {
582 ptd->IMO = bstr->
GetInt(41, 30);
584 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
585 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
586 ptd->b_nameValid =
true;
587 if (!ptd->b_isDSCtarget) {
588 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
591 ptd->DimA = bstr->
GetInt(241, 9);
592 ptd->DimB = bstr->
GetInt(250, 9);
593 ptd->DimC = bstr->
GetInt(259, 6);
594 ptd->DimD = bstr->
GetInt(265, 6);
596 ptd->ETA_Mo = bstr->
GetInt(275, 4);
597 ptd->ETA_Day = bstr->
GetInt(279, 5);
598 ptd->ETA_Hr = bstr->
GetInt(284, 5);
599 ptd->ETA_Min = bstr->
GetInt(289, 6);
601 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
603 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
605 ptd->StaticReportTicks = now.GetTicks();
616 if (!ptd->b_positionOnceValid)
break;
618 int part_number = bstr->
GetInt(39, 2);
619 if (0 == part_number) {
620 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
621 ptd->b_nameValid =
true;
624 }
else if (1 == part_number) {
625 if (!ptd->b_isDSCtarget) {
626 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
628 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
630 ptd->DimA = bstr->
GetInt(133, 9);
631 ptd->DimB = bstr->
GetInt(142, 9);
632 ptd->DimC = bstr->
GetInt(151, 6);
633 ptd->DimD = bstr->
GetInt(157, 6);
640 ptd->Class = AIS_BASE;
642 ptd->m_utc_hour = bstr->
GetInt(62, 5);
643 ptd->m_utc_min = bstr->
GetInt(67, 6);
644 ptd->m_utc_sec = bstr->
GetInt(73, 6);
646 int lon = bstr->
GetInt(80, 28);
647 if (lon & 0x08000000)
649 double lon_tentative = lon / 600000.;
651 int lat = bstr->
GetInt(108, 27);
652 if (lat & 0x04000000)
654 double lat_tentative = lat / 600000.;
656 if ((lon_tentative <= 180.) &&
660 ptd->Lon = lon_tentative;
661 ptd->Lat = lat_tentative;
662 ptd->b_positionDoubtful =
false;
663 ptd->b_positionOnceValid =
true;
664 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
665 ptd->PositionReportTicks = now.GetTicks();
667 ptd->b_positionDoubtful =
true;
674 b_posn_report =
true;
680 ptd->SOG = bstr->
GetInt(51, 10);
682 int lon = bstr->
GetInt(62, 28);
683 if (lon & 0x08000000)
685 double lon_tentative = lon / 600000.;
687 int lat = bstr->
GetInt(90, 27);
688 if (lat & 0x04000000)
690 double lat_tentative = lat / 600000.;
692 if ((lon_tentative <= 180.) &&
696 ptd->Lon = lon_tentative;
697 ptd->Lat = lat_tentative;
698 ptd->b_positionDoubtful =
false;
699 ptd->b_positionOnceValid =
true;
700 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
701 ptd->PositionReportTicks = now.GetTicks();
703 ptd->b_positionDoubtful =
true;
706 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
708 int alt_tent = bstr->
GetInt(39, 12);
709 ptd->altitude = alt_tent;
711 ptd->b_SarAircraftPosnReport =
true;
714 b_posn_report =
true;
720 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
726 ptd->DimA = bstr->
GetInt(220, 9);
727 ptd->DimB = bstr->
GetInt(229, 9);
728 ptd->DimC = bstr->
GetInt(238, 6);
729 ptd->DimD = bstr->
GetInt(244, 6);
732 ptd->m_utc_sec = bstr->
GetInt(254, 6);
734 int offpos = bstr->
GetInt(260, 1);
735 int virt = bstr->
GetInt(270, 1);
738 ptd->NavStatus = ATON_VIRTUAL;
740 ptd->NavStatus = ATON_REAL;
741 if (ptd->m_utc_sec <= 59 ) {
743 if (offpos) ptd->NavStatus += 1;
746 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
749 if (bstr->GetBitCount() > 276) {
750 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
751 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
752 ptd->ShipNameExtension[14] = 0;
754 ptd->ShipNameExtension[0] = 0;
757 ptd->b_nameValid =
true;
761 ptd->Class = AIS_ATON;
763 int lon = bstr->
GetInt(165, 28);
765 if (lon & 0x08000000)
767 double lon_tentative = lon / 600000.;
769 int lat = bstr->
GetInt(193, 27);
771 if (lat & 0x04000000)
773 double lat_tentative = lat / 600000.;
775 if ((lon_tentative <= 180.) &&
779 ptd->Lon = lon_tentative;
780 ptd->Lat = lat_tentative;
781 ptd->b_positionDoubtful =
false;
782 ptd->b_positionOnceValid =
true;
783 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
784 ptd->PositionReportTicks = now.GetTicks();
786 ptd->b_positionDoubtful =
true;
788 b_posn_report =
true;
793 int dac = bstr->
GetInt(41, 10);
794 int fi = bstr->
GetInt(51, 6);
800 ptd->b_isEuroInland =
true;
802 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
803 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
804 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
805 ptd->UN_shiptype = bstr->
GetInt(128, 14);
806 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
810 if (dac == 1 || dac == 366)
814 if (bstr->GetBitCount() >= 111) {
816 an.link_id = bstr->
GetInt(57, 10);
817 an.notice_type = bstr->
GetInt(67, 7);
818 an.month = bstr->
GetInt(74, 4);
819 an.day = bstr->
GetInt(78, 5);
820 an.hour = bstr->
GetInt(83, 5);
821 an.minute = bstr->
GetInt(88, 6);
822 an.duration_minutes = bstr->
GetInt(94, 18);
824 wxDateTime now_ = wxDateTime::Now();
827 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
828 now_.GetYear(), an.hour, an.minute);
833 if (an.start_time > now_ + wxTimeSpan::Hours(48))
834 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
835 now_.GetYear() - 1, an.hour, an.minute);
838 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
843 if (an.expiry_time < now_ - wxTimeSpan::Hours(24)) {
844 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
845 now_.GetYear() + 1, an.hour, an.minute);
847 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
852 int subarea_len = 87;
855 float pos_scale = 60000.0;
863 pos_scale = 600000.0;
868 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
869 for (
int i = 0; i < subarea_count; ++i) {
870 int base = 111 + i * subarea_len;
872 sa.shape = bstr->
GetInt(base + 1, 3);
873 int scale_factor = 1;
874 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
877 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
878 sa.text = wxString(t, wxConvUTF8);
880 int scale_multipliers[4] = {1, 10, 100, 1000};
881 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
883 case AIS8_001_22_SHAPE_SECTOR:
884 sa.left_bound_deg = bstr->
GetInt(
885 base + 6 + lon_len + lat_len + prec_size + 12, 9);
886 sa.right_bound_deg = bstr->
GetInt(
887 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
888 case AIS8_001_22_SHAPE_CIRCLE:
890 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
893 case AIS8_001_22_SHAPE_RECT:
895 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
897 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
900 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
905 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
907 sa.orient_deg = bstr->
GetInt(
908 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
910 case AIS8_001_22_SHAPE_POLYLINE:
911 case AIS8_001_22_SHAPE_POLYGON:
912 for (
int j = 0; j < 4; ++j) {
913 sa.angles[j] = bstr->
GetInt(base + 6 + j * 20, 10) * 0.5;
915 bstr->
GetInt(base + 16 + j * 20, 10) * scale_factor;
919 an.sub_areas.push_back(sa);
921 ptd->area_notices[an.link_id] = an;
928 if (bstr->GetBitCount() >= 360) {
930 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
933 double lon_tentative = 181.;
934 double lat_tentative = 91.;
936 int lon = bstr->
GetInt(57, 25);
937 int lat = bstr->
GetInt(82, 24);
939 if (lon & 0x01000000)
941 lon_tentative = lon / 60000.;
943 if (lat & 0x00800000)
945 lat_tentative = lat / 60000.;
947 ptd->Lon = lon_tentative;
948 ptd->Lat = lat_tentative;
951 wxString x = ptd->ShipName;
952 if (x.Find(
"METEO") == wxNOT_FOUND) {
954 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
955 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
958 wxString nameID =
"METEO ";
959 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
960 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
963 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
964 ptd->met_data.day = bstr->
GetInt(107, 5);
965 ptd->met_data.hour = bstr->
GetInt(112, 5);
966 ptd->met_data.minute = bstr->
GetInt(117, 6);
967 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
968 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
969 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
970 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
972 int tmp = bstr->
GetInt(155, 11);
973 if (tmp & 0x00000400)
975 ptd->met_data.air_temp = tmp / 10.;
976 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
977 int dew = bstr->
GetInt(173, 10);
978 if (dew & 0x00000200)
980 ptd->met_data.dew_point = dew / 10.;
985 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
986 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
988 int horVis = bstr->
GetInt(194, 8);
989 if (horVis & 0x80u) {
991 ptd->met_data.hor_vis_GT =
true;
993 ptd->met_data.hor_vis_GT =
false;
995 ptd->met_data.hor_vis = horVis / 10.0;
997 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
998 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
999 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
1000 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
1001 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
1002 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
1003 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
1004 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
1005 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
1006 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
1007 ptd->met_data.seastate = bstr->
GetInt(323, 4);
1009 int wt = bstr->
GetInt(327, 10);
1010 if (wt & 0x00000200)
1012 ptd->met_data.water_temp = wt / 10.;
1014 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
1015 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
1016 ptd->met_data.ice = bstr->
GetInt(349, 2);
1018 ptd->Class = AIS_METEO;
1022 ptd->b_NoTrack =
true;
1023 ptd->b_show_track =
false;
1024 ptd->b_positionDoubtful =
false;
1025 ptd->b_positionOnceValid =
true;
1026 b_posn_report =
true;
1027 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1028 ptd->PositionReportTicks = now.GetTicks();
1029 ptd->b_nameValid =
true;
1031 parse_result =
true;
1037 if (dac == 367 && fi == 33) {
1041 const int size = bstr->GetBitCount();
1044 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
1045 const int startbits = 56;
1046 const int slotsize = 112;
1047 const int slots_count = (size - startbits) / slotsize;
1049 for (
int slot = 0; slot < slots_count; slot++) {
1050 slotbit = slot * slotsize;
1051 int type = bstr->
GetInt(slotbit + 57, 4);
1052 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
1053 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
1054 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
1057 if (!ptd->b_nameValid) {
1058 wxString nameID =
"METEO Site: ";
1060 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
1061 ptd->b_nameValid =
true;
1065 int lon = bstr->
GetInt(slotbit + 90, 28);
1066 if (lon & 0x08000000)
1068 ptd->Lon = lon / 600000.;
1070 int lat = bstr->
GetInt(slotbit + 118, 27);
1071 if (lat & 0x04000000)
1073 ptd->Lat = lat / 600000.;
1074 ptd->b_positionOnceValid =
true;
1076 }
else if (type == 1) {
1077 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
1078 ptd->b_nameValid =
true;
1080 }
else if (type == 2) {
1082 int descr = bstr->
GetInt(slotbit + 116, 3);
1083 if (descr == 1 || descr == 2) {
1084 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1085 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1086 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1087 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
1090 }
else if (type == 3) {
1092 int descr = bstr->
GetInt(slotbit + 108, 3);
1093 if (descr == 1 || descr == 2) {
1094 int wltype = bstr->
GetInt(slotbit + 84, 1);
1095 int wl = bstr->
GetInt(slotbit + 85, 16);
1096 if (wl & 0x00004000)
1100 ptd->met_data.water_level = wl / 100.;
1102 ptd->met_data.water_lev_dev = wl / 100.;
1104 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
1105 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
1107 }
else if (type == 6) {
1108 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
1109 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
1110 }
else if (type == 7) {
1112 bstr->
GetInt(slotbit + 111, 3);
1113 if (swell_descr == 1 || swell_descr == 2) {
1114 ptd->met_data.swell_height =
1115 bstr->
GetInt(slotbit + 84, 8) / 10.0;
1116 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
1117 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
1119 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
1120 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
1121 if (wt_descr == 1 || wt_descr == 2)
1122 ptd->met_data.water_temp =
1123 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
1125 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
1126 if (wawe_descr == 1 || wawe_descr == 2) {
1127 ptd->met_data.wave_height =
1128 bstr->
GetInt(slotbit + 134, 8) / 10.0;
1129 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
1130 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
1132 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
1134 }
else if (type == 8) {
1135 ptd->met_data.water_temp =
1136 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
1137 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
1139 }
else if (type == 9) {
1140 int tmp = bstr->
GetInt(slotbit + 84, 11);
1141 if (tmp & 0x00000400)
1143 ptd->met_data.air_temp = tmp / 10.;
1144 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
1155 ptd->met_data.precipitation = pp;
1156 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
1157 ptd->met_data.dew_point =
1158 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
1159 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
1160 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
1161 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
1163 }
else if (type == 11) {
1165 int descr = bstr->
GetInt(slotbit + 113, 3);
1166 if (descr == 1 || descr == 2) {
1167 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1168 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1169 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1174 if (ptd->b_positionOnceValid) {
1175 ptd->Class = AIS_METEO;
1179 ptd->b_NoTrack =
true;
1180 ptd->b_show_track =
false;
1181 ptd->b_positionDoubtful =
false;
1182 b_posn_report =
true;
1183 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1184 ptd->PositionReportTicks = now.GetTicks();
1185 ptd->b_nameValid =
true;
1187 parse_result =
true;
1197 char msg_14_text[968];
1198 if (bstr->GetBitCount() > 40) {
1199 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
1200 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
1202 nd = wxMin(nd, 967);
1203 msg_14_text[nd] = 0;
1204 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
1206 parse_result =
true;
1219 if (b_posn_report) ptd->b_lost =
false;
1221 if (
true == parse_result) {
1223 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
1224 ptd->b_active =
true;
1227 return parse_result;
1231 : m_signalk_selfid(
""), m_callbacks(callbacks) {
1233 AISTargetNamesC =
new AIS_Target_Name_Hash;
1234 AISTargetNamesNC =
new AIS_Target_Name_Hash;
1236 if (g_benableAISNameCache) {
1240 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
1241 wxString line = infile.GetFirstLine();
1242 while (!infile.Eof()) {
1243 if (line.IsSameAs(
"+++==Confirmed Entry's==+++"))
1244 HashFile = AISTargetNamesC;
1246 if (line.IsSameAs(
"+++==Non Confirmed Entry's==+++"))
1247 HashFile = AISTargetNamesNC;
1249 wxStringTokenizer tokenizer(line,
",");
1250 int mmsi = wxAtoi(tokenizer.GetNextToken());
1251 wxString name = tokenizer.GetNextToken().Trim();
1252 (*HashFile)[mmsi] = name;
1255 line = infile.GetNextLine();
1262 BuildERIShipTypeHash();
1264 g_pais_alert_dialog_active =
nullptr;
1265 m_bAIS_Audio_Alert_On =
false;
1269 m_bAIS_AlertPlaying =
false;
1271 TimerAIS.SetOwner(
this, TIMER_AIS1);
1272 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
1274 m_ptentative_dsctarget =
nullptr;
1275 m_dsc_timer.SetOwner(
this, TIMER_DSC);
1282 InitCommListeners();
1285AisDecoder::~AisDecoder() {
1295 wxString content =
"+++==Confirmed Entry's==+++";
1296 AIS_Target_Name_Hash::iterator it;
1297 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
1298 content.append(
"\r\n");
1299 content.append(wxString::Format(
"%i", it->first));
1300 content.append(
",").append(it->second);
1302 content.append(
"\r\n");
1303 content.append(
"+++==Non Confirmed Entry's==+++");
1304 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
1305 content.append(
"\r\n");
1306 content.append(wxString::Format(
"%i", it->first));
1307 content.append(
",").append(it->second);
1309 outfile.Write(content);
1313 AISTargetNamesC->clear();
1314 delete AISTargetNamesC;
1315 AISTargetNamesNC->clear();
1316 delete AISTargetNamesNC;
1321 m_AIS_Audio_Alert_Timer.Stop();
1326 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
1328 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
1332bool IsTargetOnTheIgnoreList(
const int &mmsi) {
1337 if (props->m_bignore) {
1345void AisDecoder::InitCommListeners() {
1351 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
1354 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1355 HandleN0183_AIS(n0183_msg);
1360 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
1362 Bind(EVT_N0183_FRPOS, [&](
const ObservedEvt &ev) {
1364 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1365 HandleN0183_AIS(n0183_msg);
1370 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
1371 Bind(EVT_N0183_CDDSC, [&](
const ObservedEvt &ev) {
1373 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1374 HandleN0183_AIS(n0183_msg);
1379 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
1380 Bind(EVT_N0183_CDDSE, [&](
const ObservedEvt &ev) {
1382 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1383 HandleN0183_AIS(n0183_msg);
1388 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
1392 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1393 HandleN0183_AIS(n0183_msg);
1398 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
1401 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1402 HandleN0183_AIS(n0183_msg);
1407 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
1410 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1411 HandleN0183_AIS(n0183_msg);
1416 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
1419 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1420 HandleN0183_AIS(n0183_msg);
1425 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
1427 HandleSignalK(obs::UnpackEvtPointer<SignalkMsg>(ev));
1432 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
1433 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
1435 HandleN2K_129038(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1440 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
1441 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
1443 HandleN2K_129039(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1448 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
1449 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
1451 HandleN2K_129041(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1456 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
1457 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
1459 HandleN2K_129794(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1464 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
1465 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
1467 HandleN2K_129809(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1472 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
1473 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
1475 HandleN2K_129810(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1480 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
1481 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
1483 HandleN2K_129793(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1487bool AisDecoder::HandleN0183_AIS(
const N0183MsgPtr &n0183_msg) {
1488 std::string str = n0183_msg->payload;
1489 wxString sentence(str.c_str());
1490 DecodeN0183(sentence);
1495bool AisDecoder::HandleN2K_129038(
const N2000MsgPtr &n2k_msg) {
1496 std::vector<unsigned char> v = n2k_msg->payload;
1499 tN2kAISRepeat Repeat;
1510 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
1511 tN2kAISTransceiverInformation AISTransceiverInformation;
1513 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
1514 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
1515 NavStat, AISTransceiverInformation)) {
1516 unsigned mmsi = UserID;
1518 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1522 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1523 bool bnewtarget =
false;
1525 auto it = AISTargetList.find(mmsi);
1526 if (it == AISTargetList.end())
1528 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1532 pTargetData = it->second;
1535 wxDateTime now = wxDateTime::Now();
1539 pTargetData->MMSI = mmsi;
1540 pTargetData->MID = MessageID;
1541 pTargetData->MMSI = mmsi;
1542 pTargetData->Class = AIS_CLASS_A;
1544 if (97 == pTargetData->MMSI / 10000000) {
1545 pTargetData->Class = AIS_SART;
1547 pTargetData->StaticReportTicks = now.GetTicks();
1549 pTargetData->NavStatus =
static_cast<ais_nav_status
>(NavStat);
1550 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1551 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1552 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1553 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1554 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1556 pTargetData->ROTAIS = ROT;
1568 pTargetData->b_OwnShip =
1569 AISTransceiverInformation ==
1570 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1571 pTargetData->b_active =
true;
1572 pTargetData->b_lost =
false;
1573 pTargetData->b_positionOnceValid =
true;
1574 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1575 pTargetData->PositionReportTicks = now.GetTicks();
1577 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1578 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1587bool AisDecoder::HandleN2K_129039(
const N2000MsgPtr &n2k_msg) {
1588 std::vector<unsigned char> v = n2k_msg->payload;
1603 tN2kAISRepeat Repeat;
1613 tN2kAISTransceiverInformation AISTransceiverInformation;
1615 bool DSC, Band, Msg22, State, Display;
1618 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
1619 Accuracy, RAIM, Seconds, COG, SOG,
1620 AISTransceiverInformation, Heading, Unit, Display, DSC,
1621 Band, Msg22, Mode, State)) {
1622 uint32_t mmsi = UserID;
1624 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1628 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1629 bool bnewtarget =
false;
1631 auto it = AISTargetList.find(mmsi);
1632 if (it == AISTargetList.end())
1634 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1638 pTargetData = it->second;
1641 wxDateTime now = wxDateTime::Now();
1645 pTargetData->MMSI = mmsi;
1646 pTargetData->MID = MessageID;
1647 if (!isBuoyMmsi(mmsi))
1648 pTargetData->Class = AIS_CLASS_B;
1650 pTargetData->Class = AIS_BUOY;
1652 pTargetData->NavStatus = UNDEFINED;
1653 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1654 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1655 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1656 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1657 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1659 pTargetData->b_positionOnceValid =
true;
1660 pTargetData->b_active =
true;
1661 pTargetData->b_lost =
false;
1662 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1663 pTargetData->PositionReportTicks = now.GetTicks();
1664 pTargetData->b_OwnShip =
1665 AISTransceiverInformation ==
1666 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1668 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1669 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1679bool AisDecoder::HandleN2K_129041(
const N2000MsgPtr &n2k_msg) {
1680 std::vector<unsigned char> v = n2k_msg->payload;
1682 tN2kAISAtoNReportData data;
1685 struct tN2kAISAtoNReportData {
1687 tN2kAISRepeat Repeat;
1696 double PositionReferenceStarboard ;
1697 double PositionReferenceTrueNorth;
1698 tN2kAISAtoNType AtoNType;
1699 bool OffPositionIndicator;
1700 bool VirtualAtoNFlag;
1701 bool AssignedModeFlag;
1702 tN2kGNSStype GNSSType;
1704 tN2kAISTransceiverInformation AISTransceiverInformation;
1705 char AtoNName[34 + 1];
1708 if (ParseN2kPGN129041(v, data)) {
1709 uint32_t mmsi = data.UserID;
1711 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1715 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1716 bool bnewtarget =
false;
1718 auto it = AISTargetList.find(mmsi);
1719 if (it == AISTargetList.end())
1721 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1725 pTargetData = it->second;
1729 pTargetData->MMSI = mmsi;
1731 wxDateTime now = wxDateTime::Now();
1734 int offpos = data.OffPositionIndicator;
1735 int virt = data.VirtualAtoNFlag;
1738 pTargetData->NavStatus = ATON_VIRTUAL;
1740 pTargetData->NavStatus = ATON_REAL;
1742 pTargetData->m_utc_sec = data.Seconds;
1744 if (pTargetData->m_utc_sec <= 59) {
1745 pTargetData->NavStatus += 1;
1746 if (offpos) pTargetData->NavStatus += 1;
1749 data.AtoNName[34] = 0;
1750 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
1751 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1752 pTargetData->b_nameValid =
true;
1753 pTargetData->MID = 124;
1755 pTargetData->ShipType = data.AtoNType;
1756 pTargetData->Class = AIS_ATON;
1758 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
1759 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
1760 pTargetData->b_positionDoubtful =
false;
1761 pTargetData->b_positionOnceValid =
true;
1762 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1763 pTargetData->PositionReportTicks = now.GetTicks();
1767 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1768 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1770 touch_state.Notify();
1777bool AisDecoder::HandleN2K_129794(
const N2000MsgPtr &n2k_msg) {
1778 std::vector<unsigned char> v = n2k_msg->payload;
1781 tN2kAISRepeat Repeat;
1785 char Name[SHIP_NAME_LEN];
1794 char Destination[DESTINATION_LEN];
1795 tN2kAISVersion AISversion;
1796 tN2kGNSStype GNSStype;
1798 tN2kAISTranceiverInfo AISinfo;
1800 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
1801 VesselType, Length, Beam, PosRefStbd, PosRefBow,
1802 ETAdate, ETAtime, Draught, Destination, AISversion,
1803 GNSStype, DTE, AISinfo)) {
1804 uint32_t mmsi = UserID;
1806 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1810 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1811 bool bnewtarget =
false;
1813 auto it = AISTargetList.find(mmsi);
1814 if (it == AISTargetList.end())
1816 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1820 pTargetData = it->second;
1824 pTargetData->MMSI = mmsi;
1825 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1826 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1827 Name[
sizeof(Name) - 1] = 0;
1828 pTargetData->b_nameValid =
true;
1829 pTargetData->MID = 124;
1831 pTargetData->b_OwnShip =
1833 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
1835 pTargetData->DimA = PosRefBow;
1836 pTargetData->DimB = Length - PosRefBow;
1837 pTargetData->DimC = Beam - PosRefStbd;
1838 pTargetData->DimD = PosRefStbd;
1839 pTargetData->Draft = Draught;
1840 pTargetData->IMO = IMOnumber;
1841 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1842 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1843 pTargetData->ShipType = (
unsigned char)VesselType;
1844 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
1845 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
1846 Destination[
sizeof(Destination) - 1] = 0;
1848 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
1849 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
1850 wxDateTime t((time_t)secs);
1852 wxDateTime tz = t.ToUTC();
1853 pTargetData->ETA_Mo = tz.GetMonth() + 1;
1854 pTargetData->ETA_Day = tz.GetDay();
1855 pTargetData->ETA_Hr = tz.GetHour();
1856 pTargetData->ETA_Min = tz.GetMinute();
1860 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1861 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1869bool AisDecoder::HandleN2K_129809(
const N2000MsgPtr &n2k_msg) {
1870 std::vector<unsigned char> v = n2k_msg->payload;
1873 tN2kAISRepeat Repeat;
1877 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
1878 unsigned mmsi = UserID;
1880 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1884 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1885 bool bnewtarget =
false;
1887 auto it = AISTargetList.find(mmsi);
1888 if (it == AISTargetList.end())
1890 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1894 pTargetData = it->second;
1898 pTargetData->MMSI = mmsi;
1899 Name[
sizeof(Name) - 1] = 0;
1900 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1901 pTargetData->b_nameValid =
true;
1902 pTargetData->MID = 124;
1904 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1905 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1915bool AisDecoder::HandleN2K_129810(
const N2000MsgPtr &n2k_msg) {
1916 std::vector<unsigned char> v = n2k_msg->payload;
1919 tN2kAISRepeat Repeat;
1928 uint32_t MothershipID;
1930 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
1931 Callsign, Length, Beam, PosRefStbd, PosRefBow,
1933 unsigned mmsi = UserID;
1935 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1939 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1940 bool bnewtarget =
false;
1942 auto it = AISTargetList.find(mmsi);
1943 if (it == AISTargetList.end())
1945 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1949 pTargetData = it->second;
1953 pTargetData->MMSI = mmsi;
1954 pTargetData->DimA = PosRefBow;
1955 pTargetData->DimB = Length - PosRefBow;
1956 pTargetData->DimC = Beam - PosRefStbd;
1957 pTargetData->DimD = PosRefStbd;
1958 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1959 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1960 pTargetData->ShipType = (
unsigned char)VesselType;
1962 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
1963 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1972bool AisDecoder::HandleN2K_129793(
const N2000MsgPtr &n2k_msg) {
1973 std::vector<unsigned char> v = n2k_msg->payload;
1976 tN2kAISRepeat Repeat;
1980 unsigned int SecondsSinceMidnight;
1981 unsigned int DaysSinceEpoch;
1983 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
1984 SecondsSinceMidnight, DaysSinceEpoch)) {
1985 wxDateTime now = wxDateTime::Now();
1991 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1992 bool bnewtarget =
false;
1994 auto it = AISTargetList.find(mmsi);
1995 if (it == AISTargetList.end())
1997 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2001 pTargetData = it->second;
2005 pTargetData->MMSI = mmsi;
2006 pTargetData->Class = AIS_BASE;
2008 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
2009 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
2010 pTargetData->b_positionDoubtful =
false;
2011 pTargetData->b_positionOnceValid =
true;
2012 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2013 pTargetData->PositionReportTicks = now.GetTicks();
2017 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
2018 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
2037void AisDecoder::HandleSignalK(
const SignalKMsgPtr &sK_msg) {
2038 rapidjson::Document root;
2040 root.Parse(sK_msg->raw_message);
2042 if (root.HasParseError())
return;
2044 if (root.HasMember(
"self")) {
2050 if (m_signalk_selfid.IsEmpty()) {
2055 int meteo_SiteID = 0;
2056 if (root.HasMember(
"context") && root[
"context"].IsString()) {
2057 wxString context = root[
"context"].GetString();
2058 if (context == m_signalk_selfid) {
2060 wxLogMessage(
"** Ignore context own ship..");
2064 wxString mmsi_string;
2065 if (context.StartsWith(
"vessels.urn:mrn:imo:mmsi:", &mmsi_string) ||
2066 context.StartsWith(
"atons.urn:mrn:imo:mmsi:", &mmsi_string) ||
2067 context.StartsWith(
"aircraft.urn:mrn:imo:mmsi:", &mmsi_string)) {
2070 if (mmsi_string.ToLong(&mmsi)) {
2075 }
else if (context.StartsWith(
"meteo.urn:mrn:imo:mmsi:", &mmsi_string)) {
2077 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
2078 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
2081 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
2092 if (g_pMUX && g_pMUX->IsLogActive()) {
2094 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
2099 if (IsTargetOnTheIgnoreList(mmsi))
return;
2105 wxLogDebug(
"AisDecoder::OnEvtSignalK: %s", root.dump().c_str(); );
2107 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2108 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2109 bool bnewtarget =
false;
2110 int last_report_ticks;
2112 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
2115 pTargetData->MMSI = mmsi;
2116 getMmsiProperties(pTargetData);
2117 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
2118 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
2119 itr != root[
"updates"].End(); ++itr) {
2120 handleUpdate(pTargetData, bnewtarget, *itr);
2125 if (97 == mmsi / 10000000) {
2126 pTargetData->Class = AIS_SART;
2127 }
else if (1994 == mmsi / 100000) {
2129 pTargetData->Class = AIS_METEO;
2130 pTargetData->met_data.original_mmsi = origin_mmsi;
2131 pTargetData->met_data.stationID = meteo_SiteID;
2134 wxString met_name = pTargetData->ShipName;
2135 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
2138 s_id << meteo_SiteID;
2139 id1 = wxAtoi(s_id.Mid(1, 3));
2140 id2 = wxAtoi(s_id.Mid(4, 3));
2141 met_name =
"METEO ";
2142 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
2143 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
2145 pTargetData->b_nameValid =
true;
2146 pTargetData->MID = 123;
2147 pTargetData->COG = -1.;
2148 pTargetData->HDG = 511;
2149 pTargetData->SOG = -1.;
2150 pTargetData->b_NoTrack =
true;
2151 pTargetData->b_show_track =
false;
2153 pTargetData->b_OwnShip =
false;
2154 AISTargetList[pTargetData->MMSI] = pTargetData;
2158void AisDecoder::handleUpdate(
const std::shared_ptr<AisTargetData> &pTargetData,
2159 bool bnewtarget,
const rapidjson::Value &update) {
2160 wxString sfixtime =
"";
2162 if (update.HasMember(
"timestamp")) {
2163 sfixtime = update[
"timestamp"].GetString();
2165 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
2166 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
2167 itr != update[
"values"].End(); ++itr) {
2168 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
2171 wxDateTime now = wxDateTime::Now();
2172 pTargetData->m_utc_hour = now.ToUTC().GetHour();
2173 pTargetData->m_utc_min = now.ToUTC().GetMinute();
2174 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
2176 pTargetData->b_active =
true;
2177 pTargetData->b_lost =
false;
2179 if (pTargetData->b_positionOnceValid) {
2180 long mmsi_long = pTargetData->MMSI;
2182 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2183 (
void *)mmsi_long, SELTYPE_AISTARGET);
2184 pSel->SetUserData(pTargetData->MMSI);
2186 UpdateOneCPA(pTargetData.get());
2187 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2190void AisDecoder::updateItem(
const std::shared_ptr<AisTargetData> &pTargetData,
2191 bool bnewtarget,
const rapidjson::Value &item,
2192 wxString &sfixtime)
const {
2193 if (item.HasMember(
"path") && item.HasMember(
"value")) {
2194 const wxString &update_path = item[
"path"].GetString();
2195 if (update_path ==
"navigation.position") {
2196 if (item[
"value"].HasMember(
"latitude") &&
2197 item[
"value"].HasMember(
"longitude")) {
2198 wxDateTime now = wxDateTime::Now();
2200 double lat = item[
"value"][
"latitude"].GetDouble();
2201 double lon = item[
"value"][
"longitude"].GetDouble();
2202 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2203 pTargetData->PositionReportTicks = now.GetTicks();
2204 pTargetData->StaticReportTicks = now.GetTicks();
2205 pTargetData->Lat = lat;
2206 pTargetData->Lon = lon;
2207 pTargetData->b_positionOnceValid =
true;
2208 pTargetData->b_positionDoubtful =
false;
2215 }
else if (update_path ==
"navigation.speedOverGround" &&
2216 item[
"value"].IsNumber()) {
2217 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
2218 }
else if (update_path ==
"navigation.courseOverGroundTrue" &&
2219 item[
"value"].IsNumber()) {
2220 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2221 }
else if (update_path ==
"navigation.headingTrue" &&
2222 item[
"value"].IsNumber()) {
2223 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2224 }
else if (update_path ==
"navigation.rateOfTurn" &&
2225 item[
"value"].IsNumber()) {
2226 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
2227 }
else if (update_path ==
"design.aisShipType") {
2228 if (item[
"value"].HasMember(
"id")) {
2229 if (!pTargetData->b_isDSCtarget) {
2230 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2233 }
else if (update_path ==
"atonType") {
2234 if (item[
"value"].HasMember(
"id")) {
2235 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2237 }
else if (update_path ==
"virtual") {
2238 if (item[
"value"].GetBool()) {
2239 pTargetData->NavStatus = ATON_VIRTUAL;
2241 pTargetData->NavStatus = ATON_REAL;
2243 }
else if (update_path ==
"offPosition") {
2244 if (item[
"value"].GetBool()) {
2245 if (ATON_REAL == pTargetData->NavStatus) {
2246 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
2247 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
2248 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
2251 }
else if (update_path ==
"design.draft") {
2252 if (item[
"value"].HasMember(
"maximum") &&
2253 item[
"value"][
"maximum"].IsNumber()) {
2254 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
2255 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
2257 if (item[
"value"].HasMember(
"current") &&
2258 item[
"value"][
"current"].IsNumber()) {
2259 double draft = item[
"value"][
"current"].GetDouble();
2261 pTargetData->Draft = draft;
2262 pTargetData->Euro_Draft = draft;
2265 }
else if (update_path ==
"design.length") {
2266 if (pTargetData->DimB == 0) {
2267 if (item[
"value"].HasMember(
"overall")) {
2268 if (item[
"value"][
"overall"].IsNumber()) {
2269 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
2270 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
2272 pTargetData->DimB = 0;
2275 }
else if (update_path ==
"sensors.ais.class") {
2276 wxString aisclass = item[
"value"].GetString();
2277 if (aisclass ==
"A") {
2278 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
2279 }
else if (aisclass ==
"B") {
2280 if (!pTargetData->b_isDSCtarget) {
2281 if (!isBuoyMmsi(pTargetData->MMSI))
2282 pTargetData->Class = AIS_CLASS_B;
2284 pTargetData->Class = AIS_BUOY;
2287 pTargetData->NavStatus = UNDEFINED;
2289 }
else if (aisclass ==
"BASE") {
2290 pTargetData->Class = AIS_BASE;
2291 }
else if (aisclass ==
"ATON") {
2292 pTargetData->Class = AIS_ATON;
2294 }
else if (update_path ==
"sensors.ais.fromBow") {
2295 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
2296 int length = pTargetData->DimA;
2297 if (item[
"value"].IsNumber()) {
2298 pTargetData->DimA = item[
"value"].GetDouble();
2299 pTargetData->DimB = length - item[
"value"].GetDouble();
2302 }
else if (update_path ==
"design.beam") {
2303 if (pTargetData->DimD == 0) {
2304 if (item[
"value"].IsNumber()) {
2305 pTargetData->Euro_Beam = item[
"value"].GetDouble();
2306 pTargetData->DimC = item[
"value"].GetDouble();
2308 pTargetData->DimD = 0;
2310 }
else if (update_path ==
"sensors.ais.fromCenter") {
2311 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
2312 int beam = pTargetData->DimC;
2313 int center = beam / 2;
2314 if (item[
"value"].IsNumber()) {
2317 pTargetData->DimC = center + item[
"value"].GetDouble();
2318 pTargetData->DimD = beam - pTargetData->DimC;
2321 }
else if (update_path ==
"navigation.state") {
2322 wxString state = item[
"value"].GetString();
2323 if (state ==
"motoring") {
2324 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
2325 }
else if (state ==
"anchored") {
2326 pTargetData->NavStatus = AT_ANCHOR;
2327 }
else if (state ==
"not under command") {
2328 pTargetData->NavStatus = NOT_UNDER_COMMAND;
2329 }
else if (state ==
"restricted manouverability") {
2330 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
2331 }
else if (state ==
"constrained by draft") {
2332 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
2333 }
else if (state ==
"moored") {
2334 pTargetData->NavStatus = MOORED;
2335 }
else if (state ==
"aground") {
2336 pTargetData->NavStatus = AGROUND;
2337 }
else if (state ==
"fishing") {
2338 pTargetData->NavStatus = FISHING;
2339 }
else if (state ==
"sailing") {
2340 pTargetData->NavStatus = UNDERWAY_SAILING;
2341 }
else if (state ==
"hazardous material high speed") {
2342 pTargetData->NavStatus = HSC;
2343 }
else if (state ==
"hazardous material wing in ground") {
2344 pTargetData->NavStatus = WIG;
2345 }
else if (state ==
"ais-sart") {
2346 pTargetData->NavStatus = RESERVED_14;
2348 pTargetData->NavStatus = UNDEFINED;
2350 }
else if (update_path ==
"navigation.destination.commonName") {
2351 const wxString &destination = item[
"value"].GetString();
2352 pTargetData->Destination[0] =
'\0';
2353 strncpy(pTargetData->Destination, destination.c_str(),
2354 DESTINATION_LEN - 1);
2355 }
else if (update_path ==
"navigation.destination.eta") {
2356 const wxString &eta = item[
"value"].GetString();
2361 pTargetData->ETA_Mo = tz.GetMonth() + 1;
2362 pTargetData->ETA_Day = tz.GetDay();
2363 pTargetData->ETA_Hr = tz.GetHour();
2364 pTargetData->ETA_Min = tz.GetMinute();
2366 }
else if (update_path ==
"navigation.specialManeuver") {
2367 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
2368 pTargetData->IMO < 1) {
2369 const wxString &bluesign = item[
"value"].GetString();
2370 if (
"not engaged" == bluesign) {
2371 pTargetData->blue_paddle = 1;
2373 if (
"engaged" == bluesign) {
2374 pTargetData->blue_paddle = 2;
2376 pTargetData->b_blue_paddle =
2377 pTargetData->blue_paddle == 2 ? true :
false;
2379 }
else if (update_path ==
"sensors.ais.designatedAreaCode") {
2380 if (item[
"value"].GetInt() == 200) {
2381 pTargetData->b_hasInlandDac =
true;
2383 }
else if (update_path ==
"sensors.ais.functionalId") {
2384 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
2386 pTargetData->b_isEuroInland =
true;
2390 }
else if (update_path ==
"environment.date") {
2391 const wxString &issued = item[
"value"].GetString();
2396 pTargetData->met_data.day = tz.GetDay();
2397 pTargetData->met_data.hour = tz.GetHour();
2398 pTargetData->met_data.minute = tz.GetMinute();
2400 }
else if (update_path ==
"environment.wind.averageSpeed" &&
2401 item[
"value"].IsNumber()) {
2402 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
2403 }
else if (update_path ==
"environment.wind.gust" &&
2404 item[
"value"].IsNumber()) {
2405 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
2406 }
else if (update_path ==
"environment.wind.directionTrue" &&
2407 item[
"value"].IsNumber()) {
2408 pTargetData->met_data.wind_dir =
2409 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2410 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
2411 item[
"value"].IsNumber()) {
2412 pTargetData->met_data.wind_gust_dir =
2413 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2414 }
else if (update_path ==
"environment.outside.temperature" &&
2415 item[
"value"].IsNumber()) {
2416 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
2417 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
2418 item[
"value"].IsNumber()) {
2419 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
2420 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
2421 item[
"value"].IsNumber()) {
2422 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
2423 }
else if (update_path ==
"environment.outside.pressure" &&
2424 item[
"value"].IsNumber()) {
2425 pTargetData->met_data.airpress =
2426 static_cast<int>(item[
"value"].GetDouble() / 100);
2427 }
else if (update_path ==
"environment.water.level" &&
2428 item[
"value"].IsNumber()) {
2429 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
2430 }
else if (update_path ==
"environment.water.current.drift" &&
2431 item[
"value"].IsNumber()) {
2432 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
2433 }
else if (update_path ==
"environment.water.current.set" &&
2434 item[
"value"].IsNumber()) {
2435 pTargetData->met_data.curr_dir =
2436 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2437 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
2438 item[
"value"].IsNumber()) {
2439 pTargetData->met_data.water_lev_trend =
2440 static_cast<int>(item[
"value"].GetDouble());
2441 }
else if (update_path ==
"environment.water.levelTendency") {
2443 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
2444 item[
"value"].IsNumber()) {
2445 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
2446 }
else if (update_path ==
"environment.water.waves.period" &&
2447 item[
"value"].IsNumber()) {
2448 pTargetData->met_data.wave_period =
2449 static_cast<int>(item[
"value"].GetDouble());
2450 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
2451 item[
"value"].IsNumber()) {
2452 pTargetData->met_data.wave_dir =
2453 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2454 }
else if (update_path ==
"environment.water.swell.height" &&
2455 item[
"value"].IsNumber()) {
2456 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
2457 }
else if (update_path ==
"environment.water.swell.period" &&
2458 item[
"value"].IsNumber()) {
2459 pTargetData->met_data.swell_per =
2460 static_cast<int>(item[
"value"].GetDouble());
2461 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
2462 item[
"value"].IsNumber()) {
2463 pTargetData->met_data.swell_dir =
2464 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2465 }
else if (update_path ==
"environment.water.temperature" &&
2466 item[
"value"].IsNumber()) {
2467 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
2468 }
else if (update_path ==
"environment.water.salinity" &&
2469 item[
"value"].IsNumber()) {
2470 pTargetData->met_data.salinity = item[
"value"].GetDouble();
2471 }
else if (update_path ==
"environment.water.ice") {
2473 }
else if (update_path ==
"environment.water.iceValue" &&
2474 item[
"value"].IsNumber()) {
2475 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
2476 }
else if (update_path ==
"environment.water.seaStateValue" &&
2477 item[
"value"].IsNumber()) {
2478 pTargetData->met_data.seastate =
2479 static_cast<int>(item[
"value"].GetDouble());
2480 }
else if (update_path ==
"environment.water.seaState") {
2482 }
else if (update_path ==
"environment.outside.precipitation") {
2484 }
else if (update_path ==
"environment.outside.precipitationValue" &&
2485 item[
"value"].IsNumber()) {
2486 pTargetData->met_data.precipitation =
2487 static_cast<int>(item[
"value"].GetDouble());
2488 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
2489 item[
"value"].IsNumber()) {
2490 pTargetData->met_data.airpress_tend =
2491 static_cast<int>(item[
"value"].GetDouble());
2492 }
else if (update_path ==
"environment.outside.pressureTendency") {
2494 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
2495 item[
"value"].IsNumber()) {
2496 pTargetData->met_data.hor_vis =
2497 GEODESIC_METERS2NM(item[
"value"].GetDouble());
2498 }
else if (update_path ==
2499 "environment.outside.horizontalVisibility.overRange") {
2500 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
2501 }
else if (update_path.empty()) {
2502 if (item[
"value"].HasMember(
"name")) {
2503 const wxString &name = item[
"value"][
"name"].GetString();
2504 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
2505 pTargetData->b_nameValid =
true;
2506 pTargetData->MID = 123;
2507 }
else if (item[
"value"].HasMember(
"registrations")) {
2508 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
2509 pTargetData->IMO = wxAtoi(imo.Right(7));
2510 }
else if (item[
"value"].HasMember(
"communication")) {
2511 const wxString &callsign =
2512 item[
"value"][
"communication"][
"callsignVhf"].GetString();
2513 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
2515 if (item[
"value"].HasMember(
"mmsi") &&
2516 1994 != (pTargetData->MMSI) / 100000) {
2518 wxString tmp = item[
"value"][
"mmsi"].GetString();
2519 if (tmp.ToLong(&mmsi)) {
2520 pTargetData->MMSI = mmsi;
2522 if (97 == mmsi / 10000000) {
2523 pTargetData->Class = AIS_SART;
2525 if (111 == mmsi / 1000000) {
2526 pTargetData->b_SarAircraftPosnReport =
true;
2529 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2534 wxLogMessage(wxString::Format(
2535 "** AisDecoder::updateItem: unhandled path %s", update_path));
2537 rapidjson::StringBuffer buffer;
2538 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2539 item.Accept(writer);
2540 wxString msg(
"update: ");
2541 msg.append(buffer.GetString());
2551AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
2552 wxString *accumulator) {
2554 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2556 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
2558 if (!pos)
return AIS_GENERIC_ERROR;
2560 if (!accumulator)
return AIS_GENERIC_ERROR;
2563 if (!str.Mid(1, 5).IsSameAs(
"AIVDO"))
return AIS_GENERIC_ERROR;
2566 wxStringTokenizer tkz(str,
",");
2569 token = tkz.GetNextToken();
2571 token = tkz.GetNextToken();
2572 int nsentences = atoi(token.mb_str());
2574 token = tkz.GetNextToken();
2575 int isentence = atoi(token.mb_str());
2577 token = tkz.GetNextToken();
2578 token = tkz.GetNextToken();
2580 wxString string_to_parse;
2581 string_to_parse.Clear();
2594 if ((1 == nsentences) && (1 == isentence)) {
2595 string_to_parse = tkz.GetNextToken();
2598 else if (nsentences > 1) {
2599 if (1 == isentence) {
2600 *accumulator = tkz.GetNextToken();
2604 accumulator->Append(tkz.GetNextToken());
2607 if (isentence == nsentences) {
2608 string_to_parse = *accumulator;
2612 if (string_to_parse.IsEmpty() &&
2614 return AIS_INCOMPLETE_MULTIPART;
2623 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2625 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
2627 if (bdecode_result) {
2628 switch (TargetData->MID) {
2633 if (!TargetData->b_positionDoubtful) {
2634 pos->
kLat = TargetData->Lat;
2635 pos->
kLon = TargetData->Lon;
2641 if (TargetData->COG == 360.0)
2644 pos->
kCog = TargetData->COG;
2646 if (TargetData->SOG > 102.2)
2649 pos->
kSog = TargetData->SOG;
2651 if ((
int)TargetData->HDG == 511)
2654 pos->
kHdt = TargetData->HDG;
2662 return AIS_GENERIC_ERROR;
2667 return AIS_GENERIC_ERROR;
2675AisError AisDecoder::DecodeN0183(
const wxString &str) {
2676 AisError ret = AIS_GENERIC_ERROR;
2677 wxString string_to_parse;
2679 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
2680 double gpsg_cog, gpsg_sog, gpsg_utc_time;
2681 int gpsg_utc_hour = 0;
2682 int gpsg_utc_min = 0;
2683 int gpsg_utc_sec = 0;
2684 char gpsg_name_str[21];
2687 bool bdecode_result =
false;
2694 long arpa_tgt_num = 0;
2695 double arpa_sog = 0.;
2696 double arpa_cog = 0.;
2697 double arpa_lat = 0.;
2698 double arpa_lon = 0.;
2699 double arpa_dist = 0.;
2700 double arpa_brg = 0.;
2701 wxString arpa_brgunit;
2702 wxString arpa_status;
2703 wxString arpa_distunit;
2704 wxString arpa_cogunit;
2705 double arpa_mins, arpa_degs;
2706 double arpa_utc_time;
2707 int arpa_utc_hour = 0;
2708 int arpa_utc_min = 0;
2709 int arpa_utc_sec = 0;
2710 char arpa_name_str[21];
2711 bool arpa_lost =
true;
2712 bool arpa_nottracked =
false;
2714 double aprs_lat = 0.;
2715 double aprs_lon = 0.;
2716 char aprs_name_str[21];
2717 double aprs_mins, aprs_degs;
2719 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2720 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2721 bool bnewtarget =
false;
2722 int last_report_ticks;
2726 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2728 if (!NMEACheckSumOK(str)) {
2729 return AIS_NMEAVDX_CHECKSUM_BAD;
2731 if (str.Mid(1, 2).IsSameAs(
"CD")) {
2734 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
2738 wxStringTokenizer tkz(str,
",*");
2741 token = tkz.GetNextToken();
2742 token = tkz.GetNextToken();
2743 token.ToLong(&arpa_tgt_num);
2744 token = tkz.GetNextToken();
2745 token.ToDouble(&arpa_dist);
2746 token = tkz.GetNextToken();
2747 token.ToDouble(&arpa_brg);
2748 arpa_brgunit = tkz.GetNextToken();
2749 if (arpa_brgunit ==
"R") {
2750 if (std::isnan(arpa_ref_hdg)) {
2751 if (!std::isnan(
gHdt))
2756 arpa_brg += arpa_ref_hdg;
2757 if (arpa_brg >= 360.) arpa_brg -= 360.;
2759 token = tkz.GetNextToken();
2760 token.ToDouble(&arpa_sog);
2761 token = tkz.GetNextToken();
2762 token.ToDouble(&arpa_cog);
2763 arpa_cogunit = tkz.GetNextToken();
2764 if (arpa_cogunit ==
"R") {
2765 if (std::isnan(arpa_ref_hdg)) {
2766 if (!std::isnan(
gHdt))
2771 arpa_cog += arpa_ref_hdg;
2772 if (arpa_cog >= 360.) arpa_cog -= 360.;
2774 token = tkz.GetNextToken();
2775 token = tkz.GetNextToken();
2777 arpa_distunit = tkz.GetNextToken();
2778 token = tkz.GetNextToken();
2779 if (token ==
"") token = wxString::Format(
"ARPA %ld", arpa_tgt_num);
2780 int len = wxMin(token.Length(), 20);
2781 strncpy(arpa_name_str, token.mb_str(), len);
2782 arpa_name_str[len] = 0;
2783 arpa_status = tkz.GetNextToken();
2784 if (arpa_status !=
"L") {
2786 }
else if (arpa_status !=
"")
2787 arpa_nottracked =
true;
2788 wxString arpa_reftarget = tkz.GetNextToken();
2789 if (tkz.HasMoreTokens()) {
2790 token = tkz.GetNextToken();
2791 token.ToDouble(&arpa_utc_time);
2792 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2793 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2795 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2797 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
2798 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
2799 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
2802 if (arpa_distunit ==
"K") {
2805 }
else if (arpa_distunit ==
"S") {
2810 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2814 }
else if (str.Mid(3, 3).IsSameAs(
"TLL")) {
2817 wxStringTokenizer tkz(str,
",*");
2820 wxString aprs_tll_str = tkz.GetNextToken();
2821 token = tkz.GetNextToken();
2822 token.ToLong(&arpa_tgt_num);
2823 token = tkz.GetNextToken();
2824 token.ToDouble(&arpa_lat);
2825 arpa_degs = (int)(arpa_lat / 100.0);
2826 arpa_mins = arpa_lat - arpa_degs * 100.0;
2827 arpa_lat = arpa_degs + arpa_mins / 60.0;
2828 token = tkz.GetNextToken();
2829 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2830 token.Mid(0, 1).Contains(
"s") ==
true)
2831 arpa_lat = 0. - arpa_lat;
2832 token = tkz.GetNextToken();
2833 token.ToDouble(&arpa_lon);
2834 arpa_degs = (int)(arpa_lon / 100.0);
2835 arpa_mins = arpa_lon - arpa_degs * 100.0;
2836 arpa_lon = arpa_degs + arpa_mins / 60.0;
2837 token = tkz.GetNextToken();
2838 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2839 token.Mid(0, 1).Contains(
"w") ==
true)
2840 arpa_lon = 0. - arpa_lon;
2841 token = tkz.GetNextToken();
2842 if (token ==
"") token = wxString::Format(
"ARPA %d", arpa_tgt_num);
2843 int len = wxMin(token.Length(), 20);
2844 strncpy(arpa_name_str, token.mb_str(), len);
2845 arpa_name_str[len] = 0;
2846 token = tkz.GetNextToken();
2847 token.ToDouble(&arpa_utc_time);
2848 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2849 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2851 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2856 if (arpa_status !=
"L")
2858 else if (arpa_status !=
"")
2859 arpa_nottracked =
true;
2860 wxString arpa_reftarget = tkz.GetNextToken();
2861 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2865 }
else if (str.Mid(3, 3).IsSameAs(
"OSD")) {
2867 wxStringTokenizer tkz(str,
",*");
2870 token = tkz.GetNextToken();
2871 token = tkz.GetNextToken();
2872 token.ToDouble(&arpa_ref_hdg);
2882 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(
"WPL")) {
2884 wxStringTokenizer tkz(str,
",*");
2887 token = tkz.GetNextToken();
2888 token = tkz.GetNextToken();
2889 token.ToDouble(&aprs_lat);
2890 aprs_degs = (int)(aprs_lat / 100.0);
2891 aprs_mins = aprs_lat - aprs_degs * 100.0;
2892 aprs_lat = aprs_degs + aprs_mins / 60.0;
2893 token = tkz.GetNextToken();
2894 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2895 token.Mid(0, 1).Contains(
"s") ==
true)
2896 aprs_lat = 0. - aprs_lat;
2897 token = tkz.GetNextToken();
2898 token.ToDouble(&aprs_lon);
2899 aprs_degs = (int)(aprs_lon / 100.0);
2900 aprs_mins = aprs_lon - aprs_degs * 100.0;
2901 aprs_lon = aprs_degs + aprs_mins / 60.0;
2902 token = tkz.GetNextToken();
2903 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2904 token.Mid(0, 1).Contains(
"w") ==
true)
2905 aprs_lon = 0. - aprs_lon;
2906 token = tkz.GetNextToken();
2907 int len = wxMin(token.Length(), 20);
2908 strncpy(aprs_name_str, token.mb_str(), len + 1);
2909 if (0 == g_WplAction) {
2911 aprs_name_str[len] = 0;
2912 for (i = 0; i < len; i++) {
2914 hash += (int)(aprs_name_str[i]);
2915 while (hash >= 100000) hash = hash / 100000;
2917 mmsi = aprs_mmsi = 199300000 + hash;
2921 }
else if (1 == g_WplAction) {
2923 new RoutePoint(aprs_lat, aprs_lon,
"", aprs_name_str,
"",
false);
2924 pWP->m_bIsolatedMark =
true;
2925 InsertWpt(pWP,
true);
2927 }
else if (str.Mid(1, 5).IsSameAs(
"FRPOS")) {
2931 wxStringTokenizer tkz(str,
",*");
2934 token = tkz.GetNextToken();
2936 token = tkz.GetNextToken();
2937 token.ToDouble(&gpsg_lat);
2938 gpsg_degs = (int)(gpsg_lat / 100.0);
2939 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
2940 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
2942 token = tkz.GetNextToken();
2943 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2944 token.Mid(0, 1).Contains(
"s") ==
true)
2945 gpsg_lat = 0. - gpsg_lat;
2947 token = tkz.GetNextToken();
2948 token.ToDouble(&gpsg_lon);
2949 gpsg_degs = (int)(gpsg_lon / 100.0);
2950 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
2951 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
2953 token = tkz.GetNextToken();
2954 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2955 token.Mid(0, 1).Contains(
"w") ==
true)
2956 gpsg_lon = 0. - gpsg_lon;
2958 token = tkz.GetNextToken();
2961 token = tkz.GetNextToken();
2962 token.ToDouble(&gpsg_sog);
2964 token = tkz.GetNextToken();
2965 token.ToDouble(&gpsg_cog);
2967 token = tkz.GetNextToken();
2970 token = tkz.GetNextToken();
2971 token.ToDouble(&gpsg_utc_time);
2972 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
2973 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
2975 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
2979 token = tkz.GetNextToken();
2980 int i, len, hash = 0;
2981 len = wxMin(wxStrlen(token), 20);
2982 strncpy(gpsg_name_str, token.mb_str(), len);
2983 gpsg_name_str[len] = 0;
2984 for (i = 0; i < len; i++) {
2986 hash += (int)(token[i]);
2987 while (hash >= 100000) hash = hash / 100000;
2990 gpsg_mmsi = 199000000 + hash;
2992 }
else if (!str.Mid(3, 2).IsSameAs(
"VD")) {
2993 return AIS_NMEAVDX_BAD;
2999 string_to_parse.Clear();
3001 if (str.Mid(3, 2).IsSameAs(
"VD")) {
3002 wxStringTokenizer tkz(str,
",");
3005 token = tkz.GetNextToken();
3007 token = tkz.GetNextToken();
3008 nsentences = atoi(token.mb_str());
3010 token = tkz.GetNextToken();
3011 isentence = atoi(token.mb_str());
3013 token = tkz.GetNextToken();
3014 long lsequence_id = 0;
3015 token.ToLong(&lsequence_id);
3017 token = tkz.GetNextToken();
3019 token.ToLong(&lchannel);
3024 if ((1 == nsentences) && (1 == isentence)) {
3025 string_to_parse = tkz.GetNextToken();
3028 else if (nsentences > 1) {
3029 if (1 == isentence) {
3030 sentence_accumulator = tkz.GetNextToken();
3034 sentence_accumulator += tkz.GetNextToken();
3037 if (isentence == nsentences) {
3038 string_to_parse = sentence_accumulator;
3043 if (mmsi || (!string_to_parse.IsEmpty() &&
3044 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
3046 wxCharBuffer abuf = string_to_parse.ToUTF8();
3048 return AIS_GENERIC_ERROR;
3053 if (!mmsi) mmsi = strbit.GetInt(9, 30);
3054 long mmsi_long = mmsi;
3057 int origin_mmsi = 0;
3058 int messID = strbit.GetInt(1, 6);
3060 if (messID < 1 || messID > 63)
return AIS_GENERIC_ERROR;
3062 int dac = strbit.GetInt(41, 10);
3063 int fi = strbit.GetInt(51, 6);
3065 int met_lon, met_lat;
3066 if (dac == 001 && fi == 31) {
3068 met_lon = strbit.GetInt(57, 25);
3069 met_lat = strbit.GetInt(82, 24);
3070 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
3073 }
else if (dac == 367 && fi == 33) {
3075 const int size = strbit.GetBitCount();
3076 if (size < 168)
return AIS_GENERIC_ERROR;
3077 const int startb = 56;
3078 const int slot_size = 112;
3079 const int extra_bits = (size - startb) % slot_size;
3080 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
3082 int mes_type = strbit.GetInt(57, 4);
3083 int site_ID = strbit.GetInt(77, 7);
3084 if (mes_type == 0) {
3086 met_lon = strbit.GetInt(90, 28);
3087 met_lat = strbit.GetInt(118, 27);
3088 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
3093 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
3099 return AIS_GENERIC_ERROR;
3108 auto it = AISTargetList.find(mmsi);
3109 if (it == AISTargetList.end())
3111 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3116 pTargetData->MMSI = mmsi;
3117 pTargetData->met_data.original_mmsi = origin_mmsi;
3120 pTargetData = it->second;
3123 pStaleTarget = pTargetData;
3125 pTargetData->MMSI = mmsi;
3126 pTargetData->met_data.original_mmsi = origin_mmsi;
3131 if (mmsi ==
static_cast<unsigned>(props->MMSI)) {
3133 if (TRACKTYPE_NEVER == props->TrackType) {
3134 pTargetData->b_show_track =
false;
3135 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
3136 pTargetData->b_show_track =
true;
3141 if (props->m_bignore)
return AIS_NoError;
3144 else if (props->m_bVDM) {
3146 if (str.Mid(3, 9).IsSameAs(
"VDM,1,1,,")) {
3147 int message_ID = strbit.GetInt(1, 6);
3150 if ((message_ID <= 3) || (message_ID == 18)) {
3152 pTargetData->b_OwnShip =
true;
3154 wxString aivdostr = str;
3155 aivdostr.replace(1, 5,
"AIVDO");
3156 unsigned char calculated_checksum = 0;
3157 wxString::iterator j;
3158 for (j = aivdostr.begin() + 1; j != aivdostr.end() && *j !=
'*';
3160 calculated_checksum ^=
static_cast<unsigned char>(*j);
3163 if (j <= aivdostr.end() - 3)
3166 wxString::Format(_(
"%02X"), calculated_checksum));
3168 gps_watchdog_timeout_ticks =
3171 std::string full_sentence = aivdostr.ToStdString();
3172 auto address = std::make_shared<NavAddr0183>(
"virtual");
3175 auto msg = std::make_shared<const Nmea0183Msg>(
3176 "AIVDO", full_sentence, address);
3177 NavMsgBus::GetInstance().
Notify(std::move(msg));
3187 wxDateTime now = wxDateTime::Now();
3191 last_report_ticks = pStaleTarget->PositionReportTicks;
3193 last_report_ticks = now.GetTicks();
3197 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3201 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3202 pTargetData->PositionReportTicks = now.GetTicks();
3203 pTargetData->StaticReportTicks = now.GetTicks();
3204 pTargetData->m_utc_hour = gpsg_utc_hour;
3205 pTargetData->m_utc_min = gpsg_utc_min;
3206 pTargetData->m_utc_sec = gpsg_utc_sec;
3207 pTargetData->m_date_string = gpsg_date;
3208 pTargetData->MMSI = gpsg_mmsi;
3209 pTargetData->NavStatus = 0;
3210 pTargetData->Lat = gpsg_lat;
3211 pTargetData->Lon = gpsg_lon;
3212 pTargetData->b_positionOnceValid =
true;
3213 pTargetData->COG = gpsg_cog;
3214 pTargetData->SOG = gpsg_sog;
3215 pTargetData->ShipType = 52;
3216 pTargetData->Class = AIS_GPSG_BUDDY;
3217 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
3218 pTargetData->b_nameValid =
true;
3219 pTargetData->b_active =
true;
3220 pTargetData->b_lost =
false;
3222 bdecode_result =
true;
3223 }
else if (arpa_mmsi) {
3224 pTargetData->m_utc_hour = arpa_utc_hour;
3225 pTargetData->m_utc_min = arpa_utc_min;
3226 pTargetData->m_utc_sec = arpa_utc_sec;
3227 pTargetData->MMSI = arpa_mmsi;
3228 pTargetData->NavStatus = 15;
3229 if (str.Mid(3, 3).IsSameAs(
"TLL")) {
3232 (now.GetTicks() - pTargetData->PositionReportTicks);
3233 if (age_of_last > 0) {
3234 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
3235 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
3236 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3239 pTargetData->Lat = arpa_lat;
3240 pTargetData->Lon = arpa_lon;
3241 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
3242 if (arpa_dist != 0.)
3243 ll_gc_ll(
gLat,
gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
3247 pTargetData->COG = arpa_cog;
3248 pTargetData->SOG = arpa_sog;
3250 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3251 pTargetData->PositionReportTicks = now.GetTicks();
3252 pTargetData->StaticReportTicks = now.GetTicks();
3253 pTargetData->b_positionOnceValid =
true;
3254 pTargetData->ShipType = 55;
3255 pTargetData->Class = AIS_ARPA;
3257 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
3258 if (arpa_status !=
"Q")
3259 pTargetData->b_nameValid =
true;
3261 pTargetData->b_nameValid =
false;
3262 pTargetData->b_active = !arpa_lost;
3263 pTargetData->b_lost = arpa_nottracked;
3265 bdecode_result =
true;
3266 }
else if (aprs_mmsi) {
3267 pTargetData->m_utc_hour = now.GetHour();
3268 pTargetData->m_utc_min = now.GetMinute();
3269 pTargetData->m_utc_sec = now.GetSecond();
3270 pTargetData->MMSI = aprs_mmsi;
3271 pTargetData->NavStatus = 15;
3273 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
3274 if (age_of_last > 0) {
3275 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
3276 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
3277 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3280 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3281 pTargetData->PositionReportTicks = now.GetTicks();
3282 pTargetData->StaticReportTicks = now.GetTicks();
3283 pTargetData->Lat = aprs_lat;
3284 pTargetData->Lon = aprs_lon;
3285 pTargetData->b_positionOnceValid =
true;
3286 pTargetData->ShipType = 56;
3287 pTargetData->Class = AIS_APRS;
3288 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
3289 pTargetData->b_nameValid =
true;
3290 pTargetData->b_active =
true;
3291 pTargetData->b_lost =
false;
3293 bdecode_result =
true;
3297 Parse_VDXBitstring(&strbit, pTargetData);
3301 getMmsiProperties(pTargetData);
3304 pTargetData->RecentPeriod =
3305 pTargetData->PositionReportTicks - last_report_ticks;
3310 pTargetData =
nullptr;
3315 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
3320 if ((n_msgs % 10000) == 0)
3321 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
3322 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
3328void AisDecoder::CommitAISTarget(
3329 const std::shared_ptr<AisTargetData> &pTargetData,
const wxString &str,
3330 bool message_valid,
bool new_target) {
3331 m_pLatestTargetData = pTargetData;
3333 if (!str.IsEmpty()) {
3334 if (str.Mid(3, 3).IsSameAs(
"VDO"))
3335 pTargetData->b_OwnShip =
true;
3337 pTargetData->b_OwnShip =
false;
3340 if (!pTargetData->b_OwnShip) {
3342 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
3344 pTargetData->b_PersistTrack =
false;
3349 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
3356 pTargetData->b_NoTrack =
false;
3361 if (message_valid) {
3363 if (pTargetData->MMSI) {
3364 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
3367 AISTargetList[pTargetData->MMSI] =
3370 if (!pTargetData->area_notices.empty()) {
3371 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
3372 if (it == AIS_AreaNotice_Sources.end())
3373 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
3378 if (!pTargetData->b_OwnShip) {
3379 if (pTargetData->b_positionOnceValid) {
3380 long mmsi_long = pTargetData->MMSI;
3382 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3384 pSel->SetUserData(pTargetData->MMSI);
3388 UpdateOneCPA(pTargetData.get());
3391 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3404 if (!pTargetData->b_OwnShip) {
3405 if (pTargetData->b_positionOnceValid) {
3406 long mmsi_long = pTargetData->MMSI;
3408 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3410 pSel->SetUserData(pTargetData->MMSI);
3417void AisDecoder::getAISTarget(
long mmsi,
3418 std::shared_ptr<AisTargetData> &pTargetData,
3419 std::shared_ptr<AisTargetData> &pStaleTarget,
3420 bool &bnewtarget,
int &last_report_ticks,
3422 now = wxDateTime::Now();
3423 auto it = AISTargetList.find(mmsi);
3424 if (it == AISTargetList.end())
3426 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3430 pTargetData = it->second;
3431 pStaleTarget = pTargetData;
3438 last_report_ticks = pStaleTarget->PositionReportTicks;
3440 last_report_ticks = now.GetTicks();
3444 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
3447std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
3449 double dsc_lat = 0.;
3450 double dsc_lon = 0.;
3451 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
3453 double dse_lat = 0.;
3454 double dse_lon = 0.;
3455 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
3458 int dsc_tx_mmsi = 0;
3460 double dse_cog = 0.;
3461 double dse_sog = 0.;
3462 wxString dse_shipName =
"";
3467 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
3471 wxStringTokenizer tkz(str,
",*");
3474 token = tkz.GetNextToken();
3476 if (str.Mid(3, 3).IsSameAs(
"DSC")) {
3477 m_dsc_last_string = str;
3479 token = tkz.GetNextToken();
3483 token = tkz.GetNextToken();
3485 if (dsc_fmt == 12 || dsc_fmt == 16) {
3486 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3488 token.ToDouble(&dsc_addr);
3489 dsc_mmsi = 0 - (int)(dsc_addr / 10);
3492 token = tkz.GetNextToken();
3493 token.ToLong(&dsc_cat);
3495 token = tkz.GetNextToken();
3496 if (!token.IsSameAs(
"")) {
3497 token.ToLong(&dsc_nature);
3501 token = tkz.GetNextToken();
3503 token = tkz.GetNextToken();
3504 token.ToDouble(&dsc_tmp);
3506 token = tkz.GetNextToken();
3507 token = tkz.GetNextToken();
3508 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(
"")) {
3510 dsc_tx_mmsi = dsc_mmsi;
3511 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3513 token = tkz.GetNextToken();
3514 if (dsc_fmt == 16 && dsc_cat == 12) {
3515 if (!token.IsSameAs(
"")) {
3516 token.ToLong(&dsc_nature);
3520 token = tkz.GetNextToken();
3521 token = tkz.GetNextToken();
3523 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
3525 if (dsc_quadrant > 3)
3528 dsc_lat = (int)(dsc_tmp / 100000.0);
3529 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
3530 dsc_lat = dsc_lat - dsc_quadrant * 10000;
3531 dsc_degs = (int)(dsc_lat / 100.0);
3532 dsc_mins = dsc_lat - dsc_degs * 100.0;
3533 dsc_lat = dsc_degs + dsc_mins / 60.0;
3535 dsc_degs = (int)(dsc_lon / 100.0);
3536 dsc_mins = dsc_lon - dsc_degs * 100.0;
3537 dsc_lon = dsc_degs + dsc_mins / 60.0;
3538 switch (dsc_quadrant) {
3554 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
3556 }
else if (str.Mid(3, 3).IsSameAs(
"DSE")) {
3557 token = tkz.GetNextToken();
3558 token = tkz.GetNextToken();
3559 token = tkz.GetNextToken();
3560 token = tkz.GetNextToken();
3561 dse_mmsi = wxAtoi(token.Mid(
3567 token = tkz.GetNextToken();
3570 token.ToDouble(&dse_tmp);
3571 dse_lat = (int)(dse_tmp / 10000.0);
3572 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3573 dse_lat = dse_lat / 600000.0;
3574 dse_lon = dse_lon / 600000.0;
3577 while (tkz.HasMoreTokens()) {
3578 dseSymbol = tkz.GetNextToken();
3579 token = tkz.GetNextToken();
3580 if (dseSymbol.IsSameAs(
"00")) {
3581 token.ToDouble(&dse_tmp);
3582 dse_lat = (int)(dse_tmp / 10000.0);
3583 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3584 dse_lat = dse_lat / 600000.0;
3585 dse_lon = dse_lon / 600000.0;
3586 }
else if (dseSymbol.IsSameAs(
"01")) {
3587 }
else if (dseSymbol.IsSameAs(
"02")) {
3588 token.ToDouble(&dse_tmp);
3589 dse_sog = dse_tmp / 10.0;
3590 }
else if (dseSymbol.IsSameAs(
"03")) {
3591 token.ToDouble(&dse_tmp);
3592 dse_cog = dse_tmp / 10.0;
3593 }
else if (dseSymbol.IsSameAs(
"04")) {
3594 dse_shipName = DecodeDSEExpansionCharacters(token);
3595 }
else if (dseSymbol.IsSameAs(
"05")) {
3596 }
else if (dseSymbol.IsSameAs(
"06")) {
3599 mmsi = abs((
int)dse_mmsi);
3603 wxDateTime now = wxDateTime::Now();
3605 int last_report_ticks = now.GetTicks();
3608 auto it = AISTargetList.find(mmsi);
3609 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
3610 if (it == AISTargetList.end()) {
3612 pStaleTarget = it->second;
3613 last_report_ticks = pStaleTarget->PositionReportTicks;
3619 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
3621 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
3622 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
3624 m_ptentative_dsctarget->MMSI = mmsi;
3625 m_ptentative_dsctarget->NavStatus =
3627 m_ptentative_dsctarget->Lat = dsc_lat;
3628 m_ptentative_dsctarget->Lon = dsc_lon;
3629 m_ptentative_dsctarget->b_positionOnceValid =
true;
3630 m_ptentative_dsctarget->COG = 0;
3631 m_ptentative_dsctarget->SOG = 0;
3632 m_ptentative_dsctarget->ShipType = dsc_fmt;
3633 m_ptentative_dsctarget->Class = AIS_DSC;
3634 m_ptentative_dsctarget->b_isDSCtarget =
true;
3635 m_ptentative_dsctarget->b_nameValid =
true;
3636 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
3637 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %u",
3639 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
3640 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
3642 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %u",
3646 m_ptentative_dsctarget->b_active =
true;
3647 m_ptentative_dsctarget->b_lost =
false;
3648 m_ptentative_dsctarget->RecentPeriod =
3649 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3652 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
3657 if (dse_mmsi || b_take_dsc) {
3658 if (m_ptentative_dsctarget) {
3664 m_ptentative_dsctarget->Lat =
3665 m_ptentative_dsctarget->Lat +
3666 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
3667 m_ptentative_dsctarget->Lon =
3668 m_ptentative_dsctarget->Lon +
3669 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
3670 if (!dse_shipName.empty()) {
3671 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
3672 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
3673 "%s", dse_shipName.ToAscii().data());
3675 m_ptentative_dsctarget->COG = dse_cog;
3676 m_ptentative_dsctarget->SOG = dse_sog;
3680 m_ptentative_dsctarget->RecentPeriod =
3681 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3686 auto found = AISTargetList.find(mmsi);
3687 if (found == AISTargetList.end()) {
3688 pTargetData = m_ptentative_dsctarget;
3690 pTargetData = found->second;
3691 std::vector<AISTargetTrackPoint> ptrack =
3692 std::move(pTargetData->m_ptrack);
3693 pTargetData->CloneFrom(
3694 m_ptentative_dsctarget
3697 pTargetData->m_ptrack =
3704 m_ptentative_dsctarget =
nullptr;
3706 m_pLatestTargetData = pTargetData;
3708 AISTargetList[pTargetData->MMSI] =
3711 long mmsi_long = pTargetData->MMSI;
3715 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3718 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
3719 (
void *)mmsi_long, SELTYPE_AISTARGET);
3720 pSel->SetUserData(pTargetData->MMSI);
3723 UpdateOneCPA(pTargetData.get());
3726 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3733bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3734 unsigned char checksum_value = 0;
3735 int sentence_hex_sum;
3737 wxCharBuffer buf = str_in.ToUTF8();
3738 if (!buf.data())
return false;
3740 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3741 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3742 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3744 int string_length = strlen(str_ascii);
3746 int payload_length = 0;
3747 while ((payload_length < string_length) &&
3748 (str_ascii[payload_length] !=
'*'))
3751 if (payload_length == string_length)
3756 while (index < payload_length) {
3757 checksum_value ^= str_ascii[index];
3761 if (string_length > 4) {
3763 scanstr[0] = str_ascii[payload_length + 1];
3764 scanstr[1] = str_ascii[payload_length + 2];
3766 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3768 if (sentence_hex_sum == checksum_value)
return true;
3774void AisDecoder::UpdateAllCPA() {
3776 for (
const auto &it : GetTargetList()) {
3777 std::shared_ptr<AisTargetData> td = it.second;
3779 if (
nullptr != td) UpdateOneCPA(td.get());
3783void AisDecoder::UpdateAllTracks() {
3784 for (
const auto &it : GetTargetList()) {
3785 std::shared_ptr<AisTargetData> td = it.second;
3786 if (td) UpdateOneTrack(td.get());
3792 if (!ptarget->b_positionOnceValid)
return;
3794 if (!ptarget->m_ptrack.empty()) {
3796 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3797 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3800 ptarget->m_ptrack.pop_back();
3801 ptarget->b_positionDoubtful =
true;
3808 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3811 ptrackpoint.m_lat = ptarget->Lat;
3812 ptrackpoint.m_lon = ptarget->Lon;
3813 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3815 ptarget->m_ptrack.push_back(ptrackpoint);
3817 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3819 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3821 t->SetName(wxString::Format(
3822 "AIS %s (%u) %s %s", ptarget->GetFullName().c_str(), ptarget->MMSI,
3823 wxDateTime::Now().FormatISODate().c_str(),
3824 wxDateTime::Now().FormatISOTime().c_str()));
3827 m_persistent_tracks[ptarget->MMSI] = t;
3829 t = m_persistent_tracks[ptarget->MMSI];
3832 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3834 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3837 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3838 tp1->m_lon, tp, tp1, t);
3848 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3850 ptarget->m_ptrack.erase(
3851 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3853 return track.m_time < test_time;
3855 ptarget->m_ptrack.end());
3860void AisDecoder::DeletePersistentTrack(
const Track *track) {
3861 for (
auto it = m_persistent_tracks.begin(); it != m_persistent_tracks.end();
3863 if (it->second == track) {
3864 unsigned mmsi = it->first;
3865 m_persistent_tracks.erase(it);
3867 if (0 == m_persistent_tracks.count(mmsi)) {
3871 if (props->m_bPersistentTrack) {
3875 std::shared_ptr<AisTargetData> td =
3876 Get_Target_Data_From_MMSI(mmsi);
3878 props->m_bPersistentTrack =
false;
3879 td->b_mPropPersistTrack =
false;
3881 if (!m_callbacks.confirm_stop_track()) {
3882 props->m_bPersistentTrack =
true;
3894void AisDecoder::UpdateAllAlarms() {
3895 m_bGeneralAlert =
false;
3898 for (
const auto &it : GetTargetList()) {
3899 std::shared_ptr<AisTargetData> td = it.second;
3903 if (!m_bGeneralAlert) {
3905 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3906 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3907 m_bGeneralAlert =
true;
3910 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3911 m_bGeneralAlert =
false;
3914 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3915 m_bGeneralAlert =
false;
3918 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3921 if (td->Class == AIS_SART && td->NavStatus == 14)
3922 m_bGeneralAlert =
true;
3925 if ((td->Class == AIS_DSC) &&
3926 ((td->ShipType == 12) || (td->ShipType == 16)))
3927 m_bGeneralAlert =
true;
3930 ais_alert_type this_alarm = AIS_NO_ALERT;
3933 if (td->Class == AIS_SART && td->NavStatus == 14)
3934 this_alarm = AIS_ALERT_SET;
3937 if ((td->Class == AIS_DSC) &&
3938 ((td->ShipType == 12) || (td->ShipType == 16)))
3939 this_alarm = AIS_ALERT_SET;
3941 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3942 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3945 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3946 td->n_alert_state = AIS_NO_ALERT;
3952 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3953 (td->SOG <= g_ShowMoored_Kts)) {
3954 td->n_alert_state = AIS_NO_ALERT;
3960 if (td->Range_NM > g_CPAMax_NM) {
3961 td->n_alert_state = AIS_NO_ALERT;
3966 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3967 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3968 (td->Class != AIS_METEO)) {
3970 if (td->TCPA < g_TCPA_Max) {
3971 if (td->b_isFollower)
3972 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3974 this_alarm = AIS_ALERT_SET;
3977 if (td->b_isFollower)
3978 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3980 this_alarm = AIS_ALERT_SET;
3987 if (td->Class == AIS_SART) {
3988 if (td->b_in_ack_timeout) {
3989 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3991 if (delta.GetMinutes() >= 10) td->b_in_ack_timeout =
false;
3993 }
else if (g_bAIS_ACK_Timeout ||
3994 ((td->Class == AIS_DSC) &&
3995 ((td->ShipType == 12) || (td->ShipType == 16)))) {
3996 if (td->b_in_ack_timeout) {
3997 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3998 if (delta.GetMinutes() > g_AckTimeout_Mins)
3999 td->b_in_ack_timeout =
false;
4005 if (td->b_in_ack_timeout) {
4006 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
4010 td->n_alert_state = this_alarm;
4016 ptarget->Range_NM = -1.;
4025 DistanceBearingMercator(ptarget->Lat, ptarget->Lon,
gLat,
gLon, &brg, &dist);
4026 ptarget->Range_NM = dist;
4029 if (dist <= 1e-5) ptarget->Brg = -1.0;
4031 if (!ptarget->b_positionOnceValid || !
bGPSValid) {
4032 ptarget->bCPA_Valid =
false;
4036 if (ptarget->Class == AIS_METEO) {
4037 ptarget->bCPA_Valid =
false;
4046 if (ptarget->b_OwnShip) {
4048 ptarget->TCPA = -100;
4049 ptarget->bCPA_Valid =
false;
4053 double cpa_calc_ownship_cog =
gCog;
4054 double cpa_calc_target_cog = ptarget->COG;
4057 if (std::isnan(
gSog) || (
gSog > 102.2)) {
4058 ptarget->bCPA_Valid =
false;
4063 if (std::isnan(
gCog) ||
gCog == 360.0) {
4065 cpa_calc_ownship_cog =
4069 ptarget->bCPA_Valid =
false;
4075 if (ptarget->COG == 360.0) {
4076 if (ptarget->SOG > 102.2) {
4077 ptarget->bCPA_Valid =
false;
4079 }
else if (ptarget->SOG < .01) {
4080 cpa_calc_target_cog = 0.;
4083 ptarget->bCPA_Valid =
false;
4089 double v0 =
gSog * 1852.;
4090 double v1 = ptarget->SOG * 1852.;
4092 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4096 ptarget->bCPA_Valid =
false;
4103 double east1 = (ptarget->Lon -
gLon) * 60 * 1852;
4104 double north1 = (ptarget->Lat -
gLat) * 60 * 1852;
4106 double east = east1 * (cos(
gLat * PI / 180.));
4108 double north = north1;
4111 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4112 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4113 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4114 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4117 double fc = (v0 * cosa) - (v1 * cosb);
4118 double fs = (v0 * sina) - (v1 * sinb);
4120 double d = (fc * fc) + (fs * fs);
4128 tcpa = ((fc * east) + (fs * north)) / d;
4131 ptarget->TCPA = tcpa * 60.;
4136 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4138 ll_gc_ll(
gLat,
gLon, cpa_calc_ownship_cog,
gSog * tcpa, &OwnshipLatCPA,
4140 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4141 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4144 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4147 ptarget->bCPA_Valid =
true;
4149 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4153void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4156 if (m_ptentative_dsctarget) {
4157 ProcessDSx(m_dsc_last_string,
true);
4161void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4166 wxDateTime now = wxDateTime::Now();
4169 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4172 auto it = current_targets.begin();
4173 std::vector<int> remove_array;
4175 while (it != current_targets.end()) {
4179 current_targets.erase(it);
4184 std::shared_ptr<AisTargetData> xtd = it->second;
4186 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4187 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4199 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4201 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4202 double iECD_LostTimeOut = 0.0;
4206 if (xtd->Class == AIS_CLASS_A) {
4207 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4209 iECD_LostTimeOut = 18 * 60;
4211 iECD_LostTimeOut = 60;
4213 iECD_LostTimeOut = 60;
4214 }
else if (xtd->Class == AIS_CLASS_B) {
4216 iECD_LostTimeOut = 18 * 60;
4218 iECD_LostTimeOut = 180;
4219 }
else if (xtd->Class == AIS_ATON || xtd->Class == AIS_BASE)
4220 iECD_LostTimeOut = 18 * 60;
4222 if ((target_posn_age > iECD_LostTimeOut) &&
4223 (xtd->Class != AIS_GPSG_BUDDY))
4224 xtd->b_active =
false;
4226 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4228 }
else if (g_bMarkLost) {
4229 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4230 (xtd->Class != AIS_GPSG_BUDDY) && (xtd->Class != AIS_SART))
4231 xtd->b_active =
false;
4234 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4235 removelost_Mins = 18.0;
4239 if (g_bRemoveLost || g_bInlandEcdis) {
4241 (xtd->Class == AIS_ARPA &&
4243 if (((target_posn_age > removelost_Mins * 60) &&
4244 (xtd->Class != AIS_GPSG_BUDDY)) ||
4249 xtd->b_positionOnceValid =
false;
4257 long mmsi_long = xtd->MMSI;
4258 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4263 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4264 xtd->b_removed =
true;
4266 remove_array.push_back(xtd->MMSI);
4275 if (xtd->MMSI == props->MMSI) {
4276 if (props->m_bignore) {
4277 remove_array.push_back(xtd->MMSI);
4278 xtd->b_removed =
true;
4287 remove_array.push_back(xtd->MMSI);
4288 xtd->b_removed =
true;
4296 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4297 auto itd = current_targets.find(remove_array[i]);
4298 if (itd != current_targets.end()) {
4299 std::shared_ptr<AisTargetData> td = itd->second;
4300 current_targets.erase(itd);
4309 m_bSuppressed =
false;
4310 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4311 (g_bShowScaled && g_bAllowShowScaled))
4312 m_bSuppressed =
true;
4314 m_bAIS_Audio_Alert_On =
false;
4323 std::shared_ptr<AisTargetData> palert_target =
nullptr;
4325 if (!g_pais_alert_dialog_active) {
4327 double tcpa_min = 1e6;
4328 double sart_range = 1e6;
4329 std::shared_ptr<AisTargetData> palert_target_cpa =
nullptr;
4330 std::shared_ptr<AisTargetData> palert_target_sart =
nullptr;
4331 std::shared_ptr<AisTargetData> palert_target_dsc =
nullptr;
4333 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4334 std::shared_ptr<AisTargetData> td = it->second;
4336 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4337 if (g_bAIS_CPA_Alert && td->b_active) {
4338 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4339 if (td->TCPA < tcpa_min) {
4340 tcpa_min = td->TCPA;
4341 palert_target_cpa = td;
4345 }
else if ((td->Class == AIS_DSC) &&
4346 ((td->ShipType == 12) || (td->ShipType == 16))) {
4348 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4349 palert_target_dsc = td;
4352 td->b_isDSCtarget =
false;
4357 else if (td->Class == AIS_SART) {
4359 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4360 if (td->Range_NM < sart_range) {
4361 tcpa_min = sart_range;
4362 palert_target_sart = td;
4372 palert_target = palert_target_cpa;
4374 if (palert_target_sart) {
4375 palert_target = palert_target_sart;
4378 if (palert_target_dsc) {
4379 palert_target = palert_target_dsc;
4383 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4388 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4391std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
4393 if (AISTargetList.find(mmsi) == AISTargetList.end())
return nullptr;
4394 return AISTargetList[mmsi];
4401MmsiProperties::MmsiProperties(wxString &spec) {
4403 wxStringTokenizer tkz(spec,
";");
4406 s = tkz.GetNextToken();
4411 s = tkz.GetNextToken();
4413 if (s.Upper() ==
"ALWAYS")
4414 TrackType = TRACKTYPE_ALWAYS;
4415 else if (s.Upper() ==
"NEVER")
4416 TrackType = TRACKTYPE_NEVER;
4419 s = tkz.GetNextToken();
4421 if (s.Upper() ==
"IGNORE") m_bignore =
true;
4424 s = tkz.GetNextToken();
4426 if (s.Upper() ==
"MOB") m_bMOB =
true;
4429 s = tkz.GetNextToken();
4431 if (s.Upper() ==
"VDM") m_bVDM =
true;
4434 s = tkz.GetNextToken();
4436 if (s.Upper() ==
"FOLLOWER") m_bFollower =
true;
4439 s = tkz.GetNextToken();
4441 if (s.Upper() ==
"PERSIST") m_bPersistentTrack =
true;
4444 s = tkz.GetNextToken();
4446 m_ShipName = s.Upper();
4450MmsiProperties::~MmsiProperties() =
default;
4452void MmsiProperties::Init() {
4454 TrackType = TRACKTYPE_DEFAULT;
4458 m_bFollower =
false;
4459 m_bPersistentTrack =
false;
4463wxString MmsiProperties::Serialize() {
4467 sMMSI.Printf(
"%d", MMSI);
4471 if (TRACKTYPE_ALWAYS == TrackType)
4473 else if (TRACKTYPE_NEVER == TrackType)
4494 sMMSI <<
"Follower";
4498 if (m_bPersistentTrack) {
4503 if (m_ShipName ==
"") {
4504 m_ShipName = GetShipNameFromFile(MMSI);
4506 sMMSI << m_ShipName;
4511 AIS_Target_Name_Hash *AISTargetNamesC,
4512 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4513 if (g_benableAISNameCache) {
4514 wxString ship_name =
"";
4517 if (!pTargetData->b_nameValid) {
4518 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4519 if (it != AISTargetNamesC->end()) {
4520 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4521 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4522 ship_name.length() + 1);
4523 pTargetData->b_nameValid =
true;
4524 pTargetData->b_nameFromCache =
true;
4525 }
else if (!g_bUseOnlyConfirmedAISName) {
4526 it = AISTargetNamesNC->find(mmsi);
4527 if (it != AISTargetNamesNC->end()) {
4528 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4529 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4530 ship_name.length() + 1);
4531 pTargetData->b_nameValid =
true;
4532 pTargetData->b_nameFromCache =
true;
4537 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4538 (pTargetData->MID == 19) ||
4539 (pTargetData->MID == 123) ||
4540 (pTargetData->MID == 124)) {
4542 pTargetData->b_nameFromCache =
false;
4543 ship_name = trimAISField(pTargetData->ShipName);
4544 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4545 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4547 AISTargetNamesC->end()) {
4548 if ((*AISTargetNamesC)[mmsi] ==
4550 if (itNC != AISTargetNamesNC->end()) {
4552 AISTargetNamesNC->erase(itNC);
4555 if (itNC != AISTargetNamesNC->end()) {
4557 if ((*AISTargetNamesNC)[mmsi] ==
4560 (*AISTargetNamesC)[mmsi] = ship_name;
4562 AISTargetNamesNC->erase(itNC);
4565 (*AISTargetNamesNC)[mmsi] = ship_name;
4567 if (g_bUseOnlyConfirmedAISName)
4568 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4569 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4571 (*AISTargetNamesNC)[mmsi] = ship_name;
4576 AISTargetNamesNC->end()) {
4577 if ((*AISTargetNamesNC)[mmsi] ==
4580 (*AISTargetNamesC)[mmsi] = ship_name;
4582 AISTargetNamesNC->erase(itNC);
4584 (*AISTargetNamesNC)[mmsi] = ship_name;
4587 (*AISTargetNamesNC)[mmsi] = ship_name;
4589 if (g_bUseOnlyConfirmedAISName) {
4590 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4591 strncpy(pTargetData->ShipName,
"Unknown ",
4599wxString GetShipNameFromFile(
int nmmsi) {
4601 if (g_benableAISNameCache) {
4605 while (getline(infile, line)) {
4606 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4607 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4608 name = tokenizer.GetNextToken().Trim();
4611 tokenizer.GetNextToken();
4619void AisDecoder::UpdateMMSItoNameFile(
const wxString &mmsi,
4620 const wxString &name) {
4624 std::map<wxString, wxString> mmsi_name_map;
4630 while (getline(infile, line)) {
4631 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4632 wxString file_mmsi = tokenizer.GetNextToken();
4633 wxString file_name = tokenizer.GetNextToken().Trim();
4634 mmsi_name_map[file_mmsi] = file_name;
4640 mmsi_name_map[mmsi] = name.Upper();
4645 for (
const auto &pair : mmsi_name_map) {
4646 std::string line = std::string(pair.first.mb_str()) +
"," +
4647 std::string(pair.second.mb_str()) +
"\n";
4654wxString AisDecoder::GetMMSItoNameEntry(
const wxString &mmsi) {
4655 return GetShipNameFromFile(wxAtoi(mmsi));
4659int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4663 if ((!lon_bits || lon_bits == 999) && siteID) {
4666 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4667 if (points.size()) {
4668 for (
const auto &point : points) {
4670 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4672 new_mmsi = point.mmsi;
4678 if (!found && !lon_bits) {
4683 double lon_tentative = 181.;
4684 double lat_tentative = 91.;
4686 if (lon_bits == 25) {
4687 if (m_lon & 0x01000000)
4688 m_lon |= 0xFE000000;
4689 lon_tentative = m_lon / 60000.;
4691 if (m_lat & 0x00800000)
4692 m_lat |= 0xFF000000;
4693 lat_tentative = m_lat / 60000.;
4695 }
else if (lon_bits == 28) {
4696 if (m_lon & 0x08000000)
4697 m_lon |= 0xf0000000;
4698 lon_tentative = m_lon / 600000.;
4700 if (m_lat & 0x04000000)
4701 m_lat |= 0xf8000000;
4702 lat_tentative = m_lat / 600000.;
4707 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4708 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4716 static int nextMeteommsi = 199400000;
4717 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4719 if (lon_bits != 999 && !points.empty()) {
4720 wxString t_lat, t_lon;
4721 for (
const auto &point : points) {
4723 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4725 new_mmsi = point.mmsi;
4734 points.emplace_back(
4735 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4736 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.