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;
386 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
388 int lon = bstr->
GetInt(58, 28);
389 if (lon & 0x08000000)
391 double lon_tentative = lon / 600000.;
393 int lat = bstr->
GetInt(86, 27);
394 if (lat & 0x04000000)
396 double lat_tentative = lat / 600000.;
398 if ((lon_tentative <= 180.) &&
402 ptd->Lon = lon_tentative;
403 ptd->Lat = lat_tentative;
404 ptd->b_positionDoubtful =
false;
405 ptd->b_positionOnceValid =
true;
406 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
407 ptd->PositionReportTicks = now.GetTicks();
409 ptd->b_positionDoubtful =
true;
411 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
412 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
414 ptd->m_utc_sec = bstr->
GetInt(134, 6);
416 if (!ptd->b_isDSCtarget) {
417 if (!isBuoyMmsi(ptd->MMSI))
418 ptd->Class = AIS_CLASS_B;
420 ptd->Class = AIS_BUOY;
423 b_posn_report =
true;
431 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
432 int lon = bstr->
GetInt(58, 28);
433 if (lon & 0x08000000)
435 double lon_tentative = lon / 600000.;
437 int lat = bstr->
GetInt(86, 27);
438 if (lat & 0x04000000)
440 double lat_tentative = lat / 600000.;
442 if ((lon_tentative <= 180.) &&
446 ptd->Lon = lon_tentative;
447 ptd->Lat = lat_tentative;
448 ptd->b_positionDoubtful =
false;
449 ptd->b_positionOnceValid =
true;
450 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
451 ptd->PositionReportTicks = now.GetTicks();
453 ptd->b_positionDoubtful =
true;
455 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
456 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
457 ptd->m_utc_sec = bstr->
GetInt(134, 6);
459 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
460 ptd->b_nameValid =
true;
461 if (!ptd->b_isDSCtarget) {
462 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
464 ptd->DimA = bstr->
GetInt(272, 9);
465 ptd->DimB = bstr->
GetInt(281, 9);
466 ptd->DimC = bstr->
GetInt(290, 6);
467 ptd->DimD = bstr->
GetInt(296, 6);
469 if (!ptd->b_isDSCtarget) {
471 if (!isBuoyMmsi(ptd->MMSI))
472 ptd->Class = AIS_CLASS_B;
474 ptd->Class = AIS_BUOY;
477 b_posn_report =
true;
490 int bitCorrection = 10;
494 double lon_tentative = 181.;
495 double lat_tentative = 91.;
498 printf(
"AIS Message 27 - received:\r\n");
499 printf(
"MMSI : %i\r\n", ptd->MMSI);
505 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
507 ptd->NavStatus = bstr->
GetInt(39, 4);
509 int lon = bstr->
GetInt(45, 18);
510 int lat = bstr->
GetInt(63, 17);
516 if (lat >= (0x4000000 >> bitCorrection)) {
517 lat_tentative = (0x8000000 >> bitCorrection) - lat;
522 if (lon >= (0x8000000 >> bitCorrection)) {
523 lon_tentative = (0x10000000 >> bitCorrection) - lon;
528 lat_tentative = lat_tentative / resolution / 60.0;
529 lon_tentative = lon_tentative / resolution / 60.0;
532 printf(
"Latitude : %f\r\n", lat_tentative);
533 printf(
"Longitude : %f\r\n", lon_tentative);
537 int positionLatency = bstr->
GetInt(95, 1);
539 if ((lon_tentative <= 180.) &&
543 ptd->Lon = lon_tentative;
544 ptd->Lat = lat_tentative;
545 ptd->b_positionDoubtful =
false;
546 ptd->b_positionOnceValid =
true;
547 if (positionLatency == 0) {
550 printf(
"Low latency position report.\r\n");
552 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
553 ptd->PositionReportTicks = now.GetTicks();
556 ptd->b_positionDoubtful =
true;
558 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
559 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
561 b_posn_report =
true;
567 if (bstr->GetBitCount() >= 424) {
569 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
575 int AIS_version_indicator = bstr->
GetInt(39, 2);
576 if (AIS_version_indicator < 4) {
577 ptd->IMO = bstr->
GetInt(41, 30);
579 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
580 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
581 ptd->b_nameValid =
true;
582 if (!ptd->b_isDSCtarget) {
583 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
586 ptd->DimA = bstr->
GetInt(241, 9);
587 ptd->DimB = bstr->
GetInt(250, 9);
588 ptd->DimC = bstr->
GetInt(259, 6);
589 ptd->DimD = bstr->
GetInt(265, 6);
591 ptd->ETA_Mo = bstr->
GetInt(275, 4);
592 ptd->ETA_Day = bstr->
GetInt(279, 5);
593 ptd->ETA_Hr = bstr->
GetInt(284, 5);
594 ptd->ETA_Min = bstr->
GetInt(289, 6);
596 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
598 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
600 ptd->StaticReportTicks = now.GetTicks();
609 int part_number = bstr->
GetInt(39, 2);
610 if (0 == part_number) {
611 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
612 ptd->b_nameValid =
true;
615 }
else if (1 == part_number) {
616 if (!ptd->b_isDSCtarget) {
617 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
619 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
621 ptd->DimA = bstr->
GetInt(133, 9);
622 ptd->DimB = bstr->
GetInt(142, 9);
623 ptd->DimC = bstr->
GetInt(151, 6);
624 ptd->DimD = bstr->
GetInt(157, 6);
631 ptd->Class = AIS_BASE;
633 ptd->m_utc_hour = bstr->
GetInt(62, 5);
634 ptd->m_utc_min = bstr->
GetInt(67, 6);
635 ptd->m_utc_sec = bstr->
GetInt(73, 6);
637 int lon = bstr->
GetInt(80, 28);
638 if (lon & 0x08000000)
640 double lon_tentative = lon / 600000.;
642 int lat = bstr->
GetInt(108, 27);
643 if (lat & 0x04000000)
645 double lat_tentative = lat / 600000.;
647 if ((lon_tentative <= 180.) &&
651 ptd->Lon = lon_tentative;
652 ptd->Lat = lat_tentative;
653 ptd->b_positionDoubtful =
false;
654 ptd->b_positionOnceValid =
true;
655 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
656 ptd->PositionReportTicks = now.GetTicks();
658 ptd->b_positionDoubtful =
true;
665 b_posn_report =
true;
671 ptd->SOG = bstr->
GetInt(51, 10);
673 int lon = bstr->
GetInt(62, 28);
674 if (lon & 0x08000000)
676 double lon_tentative = lon / 600000.;
678 int lat = bstr->
GetInt(90, 27);
679 if (lat & 0x04000000)
681 double lat_tentative = lat / 600000.;
683 if ((lon_tentative <= 180.) &&
687 ptd->Lon = lon_tentative;
688 ptd->Lat = lat_tentative;
689 ptd->b_positionDoubtful =
false;
690 ptd->b_positionOnceValid =
true;
691 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
692 ptd->PositionReportTicks = now.GetTicks();
694 ptd->b_positionDoubtful =
true;
697 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
699 int alt_tent = bstr->
GetInt(39, 12);
700 ptd->altitude = alt_tent;
702 ptd->b_SarAircraftPosnReport =
true;
705 b_posn_report =
true;
711 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
717 ptd->DimA = bstr->
GetInt(220, 9);
718 ptd->DimB = bstr->
GetInt(229, 9);
719 ptd->DimC = bstr->
GetInt(238, 6);
720 ptd->DimD = bstr->
GetInt(244, 6);
723 ptd->m_utc_sec = bstr->
GetInt(254, 6);
725 int offpos = bstr->
GetInt(260, 1);
726 int virt = bstr->
GetInt(270, 1);
729 ptd->NavStatus = ATON_VIRTUAL;
731 ptd->NavStatus = ATON_REAL;
732 if (ptd->m_utc_sec <= 59 ) {
734 if (offpos) ptd->NavStatus += 1;
737 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
740 if (bstr->GetBitCount() > 276) {
741 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
742 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
743 ptd->ShipNameExtension[14] = 0;
745 ptd->ShipNameExtension[0] = 0;
748 ptd->b_nameValid =
true;
752 ptd->Class = AIS_ATON;
754 int lon = bstr->
GetInt(165, 28);
756 if (lon & 0x08000000)
758 double lon_tentative = lon / 600000.;
760 int lat = bstr->
GetInt(193, 27);
762 if (lat & 0x04000000)
764 double lat_tentative = lat / 600000.;
766 if ((lon_tentative <= 180.) &&
770 ptd->Lon = lon_tentative;
771 ptd->Lat = lat_tentative;
772 ptd->b_positionDoubtful =
false;
773 ptd->b_positionOnceValid =
true;
774 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
775 ptd->PositionReportTicks = now.GetTicks();
777 ptd->b_positionDoubtful =
true;
779 b_posn_report =
true;
784 int dac = bstr->
GetInt(41, 10);
785 int fi = bstr->
GetInt(51, 6);
791 ptd->b_isEuroInland =
true;
793 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
794 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
795 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
796 ptd->UN_shiptype = bstr->
GetInt(128, 14);
797 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
801 if (dac == 1 || dac == 366)
805 if (bstr->GetBitCount() >= 111) {
807 an.link_id = bstr->
GetInt(57, 10);
808 an.notice_type = bstr->
GetInt(67, 7);
809 an.month = bstr->
GetInt(74, 4);
810 an.day = bstr->
GetInt(78, 5);
811 an.hour = bstr->
GetInt(83, 5);
812 an.minute = bstr->
GetInt(88, 6);
813 an.duration_minutes = bstr->
GetInt(94, 18);
815 wxDateTime now_ = wxDateTime::Now();
818 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
819 now_.GetYear(), an.hour, an.minute);
824 if (an.start_time > now_ + wxTimeSpan::Hours(48))
825 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
826 now_.GetYear() - 1, an.hour, an.minute);
829 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
834 if (an.expiry_time < now_ - wxTimeSpan::Hours(24)) {
835 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
836 now_.GetYear() + 1, an.hour, an.minute);
838 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
843 int subarea_len = 87;
846 float pos_scale = 60000.0;
854 pos_scale = 600000.0;
859 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
860 for (
int i = 0; i < subarea_count; ++i) {
861 int base = 111 + i * subarea_len;
863 sa.shape = bstr->
GetInt(base + 1, 3);
864 int scale_factor = 1;
865 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
868 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
869 sa.text = wxString(t, wxConvUTF8);
871 int scale_multipliers[4] = {1, 10, 100, 1000};
872 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
874 case AIS8_001_22_SHAPE_SECTOR:
875 sa.left_bound_deg = bstr->
GetInt(
876 base + 6 + lon_len + lat_len + prec_size + 12, 9);
877 sa.right_bound_deg = bstr->
GetInt(
878 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
879 case AIS8_001_22_SHAPE_CIRCLE:
881 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
884 case AIS8_001_22_SHAPE_RECT:
886 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
888 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
891 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
896 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
898 sa.orient_deg = bstr->
GetInt(
899 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
901 case AIS8_001_22_SHAPE_POLYLINE:
902 case AIS8_001_22_SHAPE_POLYGON:
903 for (
int j = 0; j < 4; ++j) {
904 sa.angles[j] = bstr->
GetInt(base + 6 + j * 20, 10) * 0.5;
906 bstr->
GetInt(base + 16 + j * 20, 10) * scale_factor;
910 an.sub_areas.push_back(sa);
912 ptd->area_notices[an.link_id] = an;
919 if (bstr->GetBitCount() >= 360) {
921 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
924 double lon_tentative = 181.;
925 double lat_tentative = 91.;
927 int lon = bstr->
GetInt(57, 25);
928 int lat = bstr->
GetInt(82, 24);
930 if (lon & 0x01000000)
932 lon_tentative = lon / 60000.;
934 if (lat & 0x00800000)
936 lat_tentative = lat / 60000.;
938 ptd->Lon = lon_tentative;
939 ptd->Lat = lat_tentative;
942 wxString x = ptd->ShipName;
943 if (x.Find(
"METEO") == wxNOT_FOUND) {
945 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
946 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
949 wxString nameID =
"METEO ";
950 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
951 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
954 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
955 ptd->met_data.day = bstr->
GetInt(107, 5);
956 ptd->met_data.hour = bstr->
GetInt(112, 5);
957 ptd->met_data.minute = bstr->
GetInt(117, 6);
958 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
959 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
960 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
961 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
963 int tmp = bstr->
GetInt(155, 11);
964 if (tmp & 0x00000400)
966 ptd->met_data.air_temp = tmp / 10.;
967 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
968 int dew = bstr->
GetInt(173, 10);
969 if (dew & 0x00000200)
971 ptd->met_data.dew_point = dew / 10.;
976 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
977 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
979 int horVis = bstr->
GetInt(194, 8);
980 if (horVis & 0x80u) {
982 ptd->met_data.hor_vis_GT =
true;
984 ptd->met_data.hor_vis_GT =
false;
986 ptd->met_data.hor_vis = horVis / 10.0;
988 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
989 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
990 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
991 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
992 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
993 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
994 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
995 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
996 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
997 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
998 ptd->met_data.seastate = bstr->
GetInt(323, 4);
1000 int wt = bstr->
GetInt(327, 10);
1001 if (wt & 0x00000200)
1003 ptd->met_data.water_temp = wt / 10.;
1005 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
1006 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
1007 ptd->met_data.ice = bstr->
GetInt(349, 2);
1009 ptd->Class = AIS_METEO;
1013 ptd->b_NoTrack =
true;
1014 ptd->b_show_track =
false;
1015 ptd->b_positionDoubtful =
false;
1016 ptd->b_positionOnceValid =
true;
1017 b_posn_report =
true;
1018 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1019 ptd->PositionReportTicks = now.GetTicks();
1020 ptd->b_nameValid =
true;
1022 parse_result =
true;
1028 if (dac == 367 && fi == 33) {
1032 const int size = bstr->GetBitCount();
1035 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
1036 const int startbits = 56;
1037 const int slotsize = 112;
1038 const int slots_count = (size - startbits) / slotsize;
1040 for (
int slot = 0; slot < slots_count; slot++) {
1041 slotbit = slot * slotsize;
1042 int type = bstr->
GetInt(slotbit + 57, 4);
1043 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
1044 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
1045 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
1048 if (!ptd->b_nameValid) {
1049 wxString nameID =
"METEO Site: ";
1051 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
1052 ptd->b_nameValid =
true;
1056 int lon = bstr->
GetInt(slotbit + 90, 28);
1057 if (lon & 0x08000000)
1059 ptd->Lon = lon / 600000.;
1061 int lat = bstr->
GetInt(slotbit + 118, 27);
1062 if (lat & 0x04000000)
1064 ptd->Lat = lat / 600000.;
1065 ptd->b_positionOnceValid =
true;
1067 }
else if (type == 1) {
1068 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
1069 ptd->b_nameValid =
true;
1071 }
else if (type == 2) {
1073 int descr = bstr->
GetInt(slotbit + 116, 3);
1074 if (descr == 1 || descr == 2) {
1075 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1076 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1077 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1078 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
1081 }
else if (type == 3) {
1083 int descr = bstr->
GetInt(slotbit + 108, 3);
1084 if (descr == 1 || descr == 2) {
1085 int wltype = bstr->
GetInt(slotbit + 84, 1);
1086 int wl = bstr->
GetInt(slotbit + 85, 16);
1087 if (wl & 0x00004000)
1091 ptd->met_data.water_level = wl / 100.;
1093 ptd->met_data.water_lev_dev = wl / 100.;
1095 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
1096 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
1098 }
else if (type == 6) {
1099 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
1100 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
1101 }
else if (type == 7) {
1103 bstr->
GetInt(slotbit + 111, 3);
1104 if (swell_descr == 1 || swell_descr == 2) {
1105 ptd->met_data.swell_height =
1106 bstr->
GetInt(slotbit + 84, 8) / 10.0;
1107 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
1108 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
1110 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
1111 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
1112 if (wt_descr == 1 || wt_descr == 2)
1113 ptd->met_data.water_temp =
1114 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
1116 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
1117 if (wawe_descr == 1 || wawe_descr == 2) {
1118 ptd->met_data.wave_height =
1119 bstr->
GetInt(slotbit + 134, 8) / 10.0;
1120 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
1121 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
1123 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
1125 }
else if (type == 8) {
1126 ptd->met_data.water_temp =
1127 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
1128 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
1130 }
else if (type == 9) {
1131 int tmp = bstr->
GetInt(slotbit + 84, 11);
1132 if (tmp & 0x00000400)
1134 ptd->met_data.air_temp = tmp / 10.;
1135 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
1146 ptd->met_data.precipitation = pp;
1147 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
1148 ptd->met_data.dew_point =
1149 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
1150 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
1151 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
1152 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
1154 }
else if (type == 11) {
1156 int descr = bstr->
GetInt(slotbit + 113, 3);
1157 if (descr == 1 || descr == 2) {
1158 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1159 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1160 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1165 if (ptd->b_positionOnceValid) {
1166 ptd->Class = AIS_METEO;
1170 ptd->b_NoTrack =
true;
1171 ptd->b_show_track =
false;
1172 ptd->b_positionDoubtful =
false;
1173 b_posn_report =
true;
1174 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1175 ptd->PositionReportTicks = now.GetTicks();
1176 ptd->b_nameValid =
true;
1178 parse_result =
true;
1188 char msg_14_text[968];
1189 if (bstr->GetBitCount() > 40) {
1190 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
1191 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
1193 nd = wxMin(nd, 967);
1194 msg_14_text[nd] = 0;
1195 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
1197 parse_result =
true;
1210 if (b_posn_report) ptd->b_lost =
false;
1212 if (
true == parse_result) {
1214 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
1215 ptd->b_active =
true;
1218 return parse_result;
1222 : m_signalk_selfid(
""), m_callbacks(callbacks) {
1224 AISTargetNamesC =
new AIS_Target_Name_Hash;
1225 AISTargetNamesNC =
new AIS_Target_Name_Hash;
1227 if (g_benableAISNameCache) {
1231 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
1232 wxString line = infile.GetFirstLine();
1233 while (!infile.Eof()) {
1234 if (line.IsSameAs(
"+++==Confirmed Entry's==+++"))
1235 HashFile = AISTargetNamesC;
1237 if (line.IsSameAs(
"+++==Non Confirmed Entry's==+++"))
1238 HashFile = AISTargetNamesNC;
1240 wxStringTokenizer tokenizer(line,
",");
1241 int mmsi = wxAtoi(tokenizer.GetNextToken());
1242 wxString name = tokenizer.GetNextToken().Trim();
1243 (*HashFile)[mmsi] = name;
1246 line = infile.GetNextLine();
1253 BuildERIShipTypeHash();
1255 g_pais_alert_dialog_active =
nullptr;
1256 m_bAIS_Audio_Alert_On =
false;
1260 m_bAIS_AlertPlaying =
false;
1262 TimerAIS.SetOwner(
this, TIMER_AIS1);
1263 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
1265 m_ptentative_dsctarget =
nullptr;
1266 m_dsc_timer.SetOwner(
this, TIMER_DSC);
1273 InitCommListeners();
1276AisDecoder::~AisDecoder() {
1286 wxString content =
"+++==Confirmed Entry's==+++";
1287 AIS_Target_Name_Hash::iterator it;
1288 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
1289 content.append(
"\r\n");
1290 content.append(wxString::Format(
"%i", it->first));
1291 content.append(
",").append(it->second);
1293 content.append(
"\r\n");
1294 content.append(
"+++==Non Confirmed Entry's==+++");
1295 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
1296 content.append(
"\r\n");
1297 content.append(wxString::Format(
"%i", it->first));
1298 content.append(
",").append(it->second);
1300 outfile.Write(content);
1304 AISTargetNamesC->clear();
1305 delete AISTargetNamesC;
1306 AISTargetNamesNC->clear();
1307 delete AISTargetNamesNC;
1312 m_AIS_Audio_Alert_Timer.Stop();
1317 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
1319 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
1323bool IsTargetOnTheIgnoreList(
const int &mmsi) {
1328 if (props->m_bignore) {
1336void AisDecoder::InitCommListeners() {
1342 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
1345 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1346 HandleN0183_AIS(n0183_msg);
1351 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
1353 Bind(EVT_N0183_FRPOS, [&](
const ObservedEvt &ev) {
1355 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1356 HandleN0183_AIS(n0183_msg);
1361 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
1362 Bind(EVT_N0183_CDDSC, [&](
const ObservedEvt &ev) {
1364 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1365 HandleN0183_AIS(n0183_msg);
1370 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
1371 Bind(EVT_N0183_CDDSE, [&](
const ObservedEvt &ev) {
1373 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1374 HandleN0183_AIS(n0183_msg);
1379 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
1383 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1384 HandleN0183_AIS(n0183_msg);
1389 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
1392 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1393 HandleN0183_AIS(n0183_msg);
1398 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
1401 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1402 HandleN0183_AIS(n0183_msg);
1407 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
1410 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1411 HandleN0183_AIS(n0183_msg);
1416 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
1418 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
1423 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
1424 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
1426 HandleN2K_129038(UnpackEvtPointer<Nmea2000Msg>(ev));
1431 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
1432 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
1434 HandleN2K_129039(UnpackEvtPointer<Nmea2000Msg>(ev));
1439 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
1440 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
1442 HandleN2K_129041(UnpackEvtPointer<Nmea2000Msg>(ev));
1447 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
1448 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
1450 HandleN2K_129794(UnpackEvtPointer<Nmea2000Msg>(ev));
1455 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
1456 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
1458 HandleN2K_129809(UnpackEvtPointer<Nmea2000Msg>(ev));
1463 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
1464 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
1466 HandleN2K_129810(UnpackEvtPointer<Nmea2000Msg>(ev));
1471 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
1472 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
1474 HandleN2K_129793(UnpackEvtPointer<Nmea2000Msg>(ev));
1478bool AisDecoder::HandleN0183_AIS(
const N0183MsgPtr &n0183_msg) {
1479 std::string str = n0183_msg->payload;
1480 wxString sentence(str.c_str());
1481 DecodeN0183(sentence);
1486bool AisDecoder::HandleN2K_129038(
const N2000MsgPtr &n2k_msg) {
1487 std::vector<unsigned char> v = n2k_msg->payload;
1490 tN2kAISRepeat Repeat;
1501 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
1502 tN2kAISTransceiverInformation AISTransceiverInformation;
1504 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
1505 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
1506 NavStat, AISTransceiverInformation)) {
1507 unsigned mmsi = UserID;
1509 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1513 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1514 bool bnewtarget =
false;
1516 auto it = AISTargetList.find(mmsi);
1517 if (it == AISTargetList.end())
1519 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1523 pTargetData = it->second;
1526 wxDateTime now = wxDateTime::Now();
1530 pTargetData->MMSI = mmsi;
1531 pTargetData->MID = MessageID;
1532 pTargetData->MMSI = mmsi;
1533 pTargetData->Class = AIS_CLASS_A;
1535 if (97 == pTargetData->MMSI / 10000000) {
1536 pTargetData->Class = AIS_SART;
1538 pTargetData->StaticReportTicks = now.GetTicks();
1540 pTargetData->NavStatus =
static_cast<ais_nav_status
>(NavStat);
1541 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1542 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1543 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1544 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1545 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1547 pTargetData->ROTAIS = ROT;
1559 pTargetData->b_OwnShip =
1560 AISTransceiverInformation ==
1561 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1562 pTargetData->b_active =
true;
1563 pTargetData->b_lost =
false;
1564 pTargetData->b_positionOnceValid =
true;
1565 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1566 pTargetData->PositionReportTicks = now.GetTicks();
1568 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1569 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1578bool AisDecoder::HandleN2K_129039(
const N2000MsgPtr &n2k_msg) {
1579 std::vector<unsigned char> v = n2k_msg->payload;
1594 tN2kAISRepeat Repeat;
1604 tN2kAISTransceiverInformation AISTransceiverInformation;
1606 bool DSC, Band, Msg22, State, Display;
1609 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
1610 Accuracy, RAIM, Seconds, COG, SOG,
1611 AISTransceiverInformation, Heading, Unit, Display, DSC,
1612 Band, Msg22, Mode, State)) {
1613 uint32_t mmsi = UserID;
1615 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1619 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1620 bool bnewtarget =
false;
1622 auto it = AISTargetList.find(mmsi);
1623 if (it == AISTargetList.end())
1625 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1629 pTargetData = it->second;
1632 wxDateTime now = wxDateTime::Now();
1636 pTargetData->MMSI = mmsi;
1637 pTargetData->MID = MessageID;
1638 if (!isBuoyMmsi(mmsi))
1639 pTargetData->Class = AIS_CLASS_B;
1641 pTargetData->Class = AIS_BUOY;
1643 pTargetData->NavStatus = UNDEFINED;
1644 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1645 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1646 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1647 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1648 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1650 pTargetData->b_positionOnceValid =
true;
1651 pTargetData->b_active =
true;
1652 pTargetData->b_lost =
false;
1653 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1654 pTargetData->PositionReportTicks = now.GetTicks();
1655 pTargetData->b_OwnShip =
1656 AISTransceiverInformation ==
1657 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1659 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1660 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1670bool AisDecoder::HandleN2K_129041(
const N2000MsgPtr &n2k_msg) {
1671 std::vector<unsigned char> v = n2k_msg->payload;
1673 tN2kAISAtoNReportData data;
1676 struct tN2kAISAtoNReportData {
1678 tN2kAISRepeat Repeat;
1687 double PositionReferenceStarboard ;
1688 double PositionReferenceTrueNorth;
1689 tN2kAISAtoNType AtoNType;
1690 bool OffPositionIndicator;
1691 bool VirtualAtoNFlag;
1692 bool AssignedModeFlag;
1693 tN2kGNSStype GNSSType;
1695 tN2kAISTransceiverInformation AISTransceiverInformation;
1696 char AtoNName[34 + 1];
1699 if (ParseN2kPGN129041(v, data)) {
1700 uint32_t mmsi = data.UserID;
1702 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1706 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1707 bool bnewtarget =
false;
1709 auto it = AISTargetList.find(mmsi);
1710 if (it == AISTargetList.end())
1712 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1716 pTargetData = it->second;
1720 pTargetData->MMSI = mmsi;
1722 wxDateTime now = wxDateTime::Now();
1725 int offpos = data.OffPositionIndicator;
1726 int virt = data.VirtualAtoNFlag;
1729 pTargetData->NavStatus = ATON_VIRTUAL;
1731 pTargetData->NavStatus = ATON_REAL;
1733 pTargetData->m_utc_sec = data.Seconds;
1735 if (pTargetData->m_utc_sec <= 59) {
1736 pTargetData->NavStatus += 1;
1737 if (offpos) pTargetData->NavStatus += 1;
1740 data.AtoNName[34] = 0;
1741 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
1742 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1743 pTargetData->b_nameValid =
true;
1744 pTargetData->MID = 124;
1746 pTargetData->ShipType = data.AtoNType;
1747 pTargetData->Class = AIS_ATON;
1749 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
1750 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
1751 pTargetData->b_positionDoubtful =
false;
1752 pTargetData->b_positionOnceValid =
true;
1753 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1754 pTargetData->PositionReportTicks = now.GetTicks();
1758 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1759 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1761 touch_state.Notify();
1768bool AisDecoder::HandleN2K_129794(
const N2000MsgPtr &n2k_msg) {
1769 std::vector<unsigned char> v = n2k_msg->payload;
1772 tN2kAISRepeat Repeat;
1776 char Name[SHIP_NAME_LEN];
1785 char Destination[DESTINATION_LEN];
1786 tN2kAISVersion AISversion;
1787 tN2kGNSStype GNSStype;
1789 tN2kAISTranceiverInfo AISinfo;
1791 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
1792 VesselType, Length, Beam, PosRefStbd, PosRefBow,
1793 ETAdate, ETAtime, Draught, Destination, AISversion,
1794 GNSStype, DTE, AISinfo)) {
1795 uint32_t mmsi = UserID;
1797 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1801 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1802 bool bnewtarget =
false;
1804 auto it = AISTargetList.find(mmsi);
1805 if (it == AISTargetList.end())
1807 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1811 pTargetData = it->second;
1815 pTargetData->MMSI = mmsi;
1816 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1817 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1818 Name[
sizeof(Name) - 1] = 0;
1819 pTargetData->b_nameValid =
true;
1820 pTargetData->MID = 124;
1822 pTargetData->b_OwnShip =
1824 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
1826 pTargetData->DimA = PosRefBow;
1827 pTargetData->DimB = Length - PosRefBow;
1828 pTargetData->DimC = Beam - PosRefStbd;
1829 pTargetData->DimD = PosRefStbd;
1830 pTargetData->Draft = Draught;
1831 pTargetData->IMO = IMOnumber;
1832 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1833 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1834 pTargetData->ShipType = (
unsigned char)VesselType;
1835 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
1836 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
1837 Destination[
sizeof(Destination) - 1] = 0;
1839 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
1840 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
1841 wxDateTime t((time_t)secs);
1843 wxDateTime tz = t.ToUTC();
1844 pTargetData->ETA_Mo = tz.GetMonth() + 1;
1845 pTargetData->ETA_Day = tz.GetDay();
1846 pTargetData->ETA_Hr = tz.GetHour();
1847 pTargetData->ETA_Min = tz.GetMinute();
1851 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1852 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1860bool AisDecoder::HandleN2K_129809(
const N2000MsgPtr &n2k_msg) {
1861 std::vector<unsigned char> v = n2k_msg->payload;
1864 tN2kAISRepeat Repeat;
1868 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
1869 unsigned mmsi = UserID;
1871 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1875 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1876 bool bnewtarget =
false;
1878 auto it = AISTargetList.find(mmsi);
1879 if (it == AISTargetList.end())
1881 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1885 pTargetData = it->second;
1889 pTargetData->MMSI = mmsi;
1890 Name[
sizeof(Name) - 1] = 0;
1891 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1892 pTargetData->b_nameValid =
true;
1893 pTargetData->MID = 124;
1895 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1896 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1906bool AisDecoder::HandleN2K_129810(
const N2000MsgPtr &n2k_msg) {
1907 std::vector<unsigned char> v = n2k_msg->payload;
1910 tN2kAISRepeat Repeat;
1919 uint32_t MothershipID;
1921 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
1922 Callsign, Length, Beam, PosRefStbd, PosRefBow,
1924 unsigned mmsi = UserID;
1926 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1930 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1931 bool bnewtarget =
false;
1933 auto it = AISTargetList.find(mmsi);
1934 if (it == AISTargetList.end())
1936 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1940 pTargetData = it->second;
1944 pTargetData->MMSI = mmsi;
1945 pTargetData->DimA = PosRefBow;
1946 pTargetData->DimB = Length - PosRefBow;
1947 pTargetData->DimC = Beam - PosRefStbd;
1948 pTargetData->DimD = PosRefStbd;
1949 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1950 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1951 pTargetData->ShipType = (
unsigned char)VesselType;
1953 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
1954 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1963bool AisDecoder::HandleN2K_129793(
const N2000MsgPtr &n2k_msg) {
1964 std::vector<unsigned char> v = n2k_msg->payload;
1967 tN2kAISRepeat Repeat;
1971 unsigned int SecondsSinceMidnight;
1972 unsigned int DaysSinceEpoch;
1974 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
1975 SecondsSinceMidnight, DaysSinceEpoch)) {
1976 wxDateTime now = wxDateTime::Now();
1982 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1983 bool bnewtarget =
false;
1985 auto it = AISTargetList.find(mmsi);
1986 if (it == AISTargetList.end())
1988 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1992 pTargetData = it->second;
1996 pTargetData->MMSI = mmsi;
1997 pTargetData->Class = AIS_BASE;
1999 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
2000 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
2001 pTargetData->b_positionDoubtful =
false;
2002 pTargetData->b_positionOnceValid =
true;
2003 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2004 pTargetData->PositionReportTicks = now.GetTicks();
2008 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
2009 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
2028void AisDecoder::HandleSignalK(
const SignalKMsgPtr &sK_msg) {
2029 rapidjson::Document root;
2031 root.Parse(sK_msg->raw_message);
2033 if (root.HasParseError())
return;
2035 if (root.HasMember(
"self")) {
2041 if (m_signalk_selfid.IsEmpty()) {
2046 int meteo_SiteID = 0;
2047 if (root.HasMember(
"context") && root[
"context"].IsString()) {
2048 wxString context = root[
"context"].GetString();
2049 if (context == m_signalk_selfid) {
2051 wxLogMessage(
"** Ignore context own ship..");
2055 wxString mmsi_string;
2056 if (context.StartsWith(
"vessels.urn:mrn:imo:mmsi:", &mmsi_string) ||
2057 context.StartsWith(
"atons.urn:mrn:imo:mmsi:", &mmsi_string) ||
2058 context.StartsWith(
"aircraft.urn:mrn:imo:mmsi:", &mmsi_string)) {
2061 if (mmsi_string.ToLong(&mmsi)) {
2066 }
else if (context.StartsWith(
"meteo.urn:mrn:imo:mmsi:", &mmsi_string)) {
2068 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
2069 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
2072 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
2083 if (g_pMUX && g_pMUX->IsLogActive()) {
2085 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
2090 if (IsTargetOnTheIgnoreList(mmsi))
return;
2098 writer.
Write(root, dbg);
2100 wxString msg(
"AisDecoder::OnEvtSignalK: " );
2104 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2105 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2106 bool bnewtarget =
false;
2107 int last_report_ticks;
2109 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
2112 pTargetData->MMSI = mmsi;
2113 getMmsiProperties(pTargetData);
2114 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
2115 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
2116 itr != root[
"updates"].End(); ++itr) {
2117 handleUpdate(pTargetData, bnewtarget, *itr);
2122 if (97 == mmsi / 10000000) {
2123 pTargetData->Class = AIS_SART;
2124 }
else if (1994 == mmsi / 100000) {
2126 pTargetData->Class = AIS_METEO;
2127 pTargetData->met_data.original_mmsi = origin_mmsi;
2128 pTargetData->met_data.stationID = meteo_SiteID;
2131 wxString met_name = pTargetData->ShipName;
2132 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
2135 s_id << meteo_SiteID;
2136 id1 = wxAtoi(s_id.Mid(1, 3));
2137 id2 = wxAtoi(s_id.Mid(4, 3));
2138 met_name =
"METEO ";
2139 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
2140 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
2142 pTargetData->b_nameValid =
true;
2143 pTargetData->MID = 123;
2144 pTargetData->COG = -1.;
2145 pTargetData->HDG = 511;
2146 pTargetData->SOG = -1.;
2147 pTargetData->b_NoTrack =
true;
2148 pTargetData->b_show_track =
false;
2150 pTargetData->b_OwnShip =
false;
2151 AISTargetList[pTargetData->MMSI] = pTargetData;
2155void AisDecoder::handleUpdate(
const std::shared_ptr<AisTargetData> &pTargetData,
2156 bool bnewtarget,
const rapidjson::Value &update) {
2157 wxString sfixtime =
"";
2159 if (update.HasMember(
"timestamp")) {
2160 sfixtime = update[
"timestamp"].GetString();
2162 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
2163 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
2164 itr != update[
"values"].End(); ++itr) {
2165 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
2168 wxDateTime now = wxDateTime::Now();
2169 pTargetData->m_utc_hour = now.ToUTC().GetHour();
2170 pTargetData->m_utc_min = now.ToUTC().GetMinute();
2171 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
2173 pTargetData->b_active =
true;
2174 pTargetData->b_lost =
false;
2176 if (pTargetData->b_positionOnceValid) {
2177 long mmsi_long = pTargetData->MMSI;
2179 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2180 (
void *)mmsi_long, SELTYPE_AISTARGET);
2181 pSel->SetUserData(pTargetData->MMSI);
2183 UpdateOneCPA(pTargetData.get());
2184 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2187void AisDecoder::updateItem(
const std::shared_ptr<AisTargetData> &pTargetData,
2188 bool bnewtarget,
const rapidjson::Value &item,
2189 wxString &sfixtime)
const {
2190 if (item.HasMember(
"path") && item.HasMember(
"value")) {
2191 const wxString &update_path = item[
"path"].GetString();
2192 if (update_path ==
"navigation.position") {
2193 if (item[
"value"].HasMember(
"latitude") &&
2194 item[
"value"].HasMember(
"longitude")) {
2195 wxDateTime now = wxDateTime::Now();
2197 double lat = item[
"value"][
"latitude"].GetDouble();
2198 double lon = item[
"value"][
"longitude"].GetDouble();
2199 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2200 pTargetData->PositionReportTicks = now.GetTicks();
2201 pTargetData->StaticReportTicks = now.GetTicks();
2202 pTargetData->Lat = lat;
2203 pTargetData->Lon = lon;
2204 pTargetData->b_positionOnceValid =
true;
2205 pTargetData->b_positionDoubtful =
false;
2212 }
else if (update_path ==
"navigation.speedOverGround" &&
2213 item[
"value"].IsNumber()) {
2214 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
2215 }
else if (update_path ==
"navigation.courseOverGroundTrue" &&
2216 item[
"value"].IsNumber()) {
2217 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2218 }
else if (update_path ==
"navigation.headingTrue" &&
2219 item[
"value"].IsNumber()) {
2220 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2221 }
else if (update_path ==
"navigation.rateOfTurn" &&
2222 item[
"value"].IsNumber()) {
2223 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
2224 }
else if (update_path ==
"design.aisShipType") {
2225 if (item[
"value"].HasMember(
"id")) {
2226 if (!pTargetData->b_isDSCtarget) {
2227 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2230 }
else if (update_path ==
"atonType") {
2231 if (item[
"value"].HasMember(
"id")) {
2232 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2234 }
else if (update_path ==
"virtual") {
2235 if (item[
"value"].GetBool()) {
2236 pTargetData->NavStatus = ATON_VIRTUAL;
2238 pTargetData->NavStatus = ATON_REAL;
2240 }
else if (update_path ==
"offPosition") {
2241 if (item[
"value"].GetBool()) {
2242 if (ATON_REAL == pTargetData->NavStatus) {
2243 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
2244 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
2245 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
2248 }
else if (update_path ==
"design.draft") {
2249 if (item[
"value"].HasMember(
"maximum") &&
2250 item[
"value"][
"maximum"].IsNumber()) {
2251 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
2252 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
2254 if (item[
"value"].HasMember(
"current") &&
2255 item[
"value"][
"current"].IsNumber()) {
2256 double draft = item[
"value"][
"current"].GetDouble();
2258 pTargetData->Draft = draft;
2259 pTargetData->Euro_Draft = draft;
2262 }
else if (update_path ==
"design.length") {
2263 if (pTargetData->DimB == 0) {
2264 if (item[
"value"].HasMember(
"overall")) {
2265 if (item[
"value"][
"overall"].IsNumber()) {
2266 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
2267 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
2269 pTargetData->DimB = 0;
2272 }
else if (update_path ==
"sensors.ais.class") {
2273 wxString aisclass = item[
"value"].GetString();
2274 if (aisclass ==
"A") {
2275 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
2276 }
else if (aisclass ==
"B") {
2277 if (!pTargetData->b_isDSCtarget) {
2278 if (!isBuoyMmsi(pTargetData->MMSI))
2279 pTargetData->Class = AIS_CLASS_B;
2281 pTargetData->Class = AIS_BUOY;
2284 pTargetData->NavStatus = UNDEFINED;
2286 }
else if (aisclass ==
"BASE") {
2287 pTargetData->Class = AIS_BASE;
2288 }
else if (aisclass ==
"ATON") {
2289 pTargetData->Class = AIS_ATON;
2291 }
else if (update_path ==
"sensors.ais.fromBow") {
2292 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
2293 int length = pTargetData->DimA;
2294 if (item[
"value"].IsNumber()) {
2295 pTargetData->DimA = item[
"value"].GetDouble();
2296 pTargetData->DimB = length - item[
"value"].GetDouble();
2299 }
else if (update_path ==
"design.beam") {
2300 if (pTargetData->DimD == 0) {
2301 if (item[
"value"].IsNumber()) {
2302 pTargetData->Euro_Beam = item[
"value"].GetDouble();
2303 pTargetData->DimC = item[
"value"].GetDouble();
2305 pTargetData->DimD = 0;
2307 }
else if (update_path ==
"sensors.ais.fromCenter") {
2308 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
2309 int beam = pTargetData->DimC;
2310 int center = beam / 2;
2311 if (item[
"value"].IsNumber()) {
2314 pTargetData->DimC = center + item[
"value"].GetDouble();
2315 pTargetData->DimD = beam - pTargetData->DimC;
2318 }
else if (update_path ==
"navigation.state") {
2319 wxString state = item[
"value"].GetString();
2320 if (state ==
"motoring") {
2321 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
2322 }
else if (state ==
"anchored") {
2323 pTargetData->NavStatus = AT_ANCHOR;
2324 }
else if (state ==
"not under command") {
2325 pTargetData->NavStatus = NOT_UNDER_COMMAND;
2326 }
else if (state ==
"restricted manouverability") {
2327 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
2328 }
else if (state ==
"constrained by draft") {
2329 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
2330 }
else if (state ==
"moored") {
2331 pTargetData->NavStatus = MOORED;
2332 }
else if (state ==
"aground") {
2333 pTargetData->NavStatus = AGROUND;
2334 }
else if (state ==
"fishing") {
2335 pTargetData->NavStatus = FISHING;
2336 }
else if (state ==
"sailing") {
2337 pTargetData->NavStatus = UNDERWAY_SAILING;
2338 }
else if (state ==
"hazardous material high speed") {
2339 pTargetData->NavStatus = HSC;
2340 }
else if (state ==
"hazardous material wing in ground") {
2341 pTargetData->NavStatus = WIG;
2342 }
else if (state ==
"ais-sart") {
2343 pTargetData->NavStatus = RESERVED_14;
2345 pTargetData->NavStatus = UNDEFINED;
2347 }
else if (update_path ==
"navigation.destination.commonName") {
2348 const wxString &destination = item[
"value"].GetString();
2349 pTargetData->Destination[0] =
'\0';
2350 strncpy(pTargetData->Destination, destination.c_str(),
2351 DESTINATION_LEN - 1);
2352 }
else if (update_path ==
"navigation.destination.eta") {
2353 const wxString &eta = item[
"value"].GetString();
2358 pTargetData->ETA_Mo = tz.GetMonth() + 1;
2359 pTargetData->ETA_Day = tz.GetDay();
2360 pTargetData->ETA_Hr = tz.GetHour();
2361 pTargetData->ETA_Min = tz.GetMinute();
2363 }
else if (update_path ==
"navigation.specialManeuver") {
2364 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
2365 pTargetData->IMO < 1) {
2366 const wxString &bluesign = item[
"value"].GetString();
2367 if (
"not engaged" == bluesign) {
2368 pTargetData->blue_paddle = 1;
2370 if (
"engaged" == bluesign) {
2371 pTargetData->blue_paddle = 2;
2373 pTargetData->b_blue_paddle =
2374 pTargetData->blue_paddle == 2 ? true :
false;
2376 }
else if (update_path ==
"sensors.ais.designatedAreaCode") {
2377 if (item[
"value"].GetInt() == 200) {
2378 pTargetData->b_hasInlandDac =
true;
2380 }
else if (update_path ==
"sensors.ais.functionalId") {
2381 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
2383 pTargetData->b_isEuroInland =
true;
2387 }
else if (update_path ==
"environment.date") {
2388 const wxString &issued = item[
"value"].GetString();
2393 pTargetData->met_data.day = tz.GetDay();
2394 pTargetData->met_data.hour = tz.GetHour();
2395 pTargetData->met_data.minute = tz.GetMinute();
2397 }
else if (update_path ==
"environment.wind.averageSpeed" &&
2398 item[
"value"].IsNumber()) {
2399 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
2400 }
else if (update_path ==
"environment.wind.gust" &&
2401 item[
"value"].IsNumber()) {
2402 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
2403 }
else if (update_path ==
"environment.wind.directionTrue" &&
2404 item[
"value"].IsNumber()) {
2405 pTargetData->met_data.wind_dir =
2406 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2407 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
2408 item[
"value"].IsNumber()) {
2409 pTargetData->met_data.wind_gust_dir =
2410 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2411 }
else if (update_path ==
"environment.outside.temperature" &&
2412 item[
"value"].IsNumber()) {
2413 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
2414 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
2415 item[
"value"].IsNumber()) {
2416 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
2417 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
2418 item[
"value"].IsNumber()) {
2419 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
2420 }
else if (update_path ==
"environment.outside.pressure" &&
2421 item[
"value"].IsNumber()) {
2422 pTargetData->met_data.airpress =
2423 static_cast<int>(item[
"value"].GetDouble() / 100);
2424 }
else if (update_path ==
"environment.water.level" &&
2425 item[
"value"].IsNumber()) {
2426 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
2427 }
else if (update_path ==
"environment.water.current.drift" &&
2428 item[
"value"].IsNumber()) {
2429 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
2430 }
else if (update_path ==
"environment.water.current.set" &&
2431 item[
"value"].IsNumber()) {
2432 pTargetData->met_data.curr_dir =
2433 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2434 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
2435 item[
"value"].IsNumber()) {
2436 pTargetData->met_data.water_lev_trend =
2437 static_cast<int>(item[
"value"].GetDouble());
2438 }
else if (update_path ==
"environment.water.levelTendency") {
2440 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
2441 item[
"value"].IsNumber()) {
2442 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
2443 }
else if (update_path ==
"environment.water.waves.period" &&
2444 item[
"value"].IsNumber()) {
2445 pTargetData->met_data.wave_period =
2446 static_cast<int>(item[
"value"].GetDouble());
2447 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
2448 item[
"value"].IsNumber()) {
2449 pTargetData->met_data.wave_dir =
2450 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2451 }
else if (update_path ==
"environment.water.swell.height" &&
2452 item[
"value"].IsNumber()) {
2453 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
2454 }
else if (update_path ==
"environment.water.swell.period" &&
2455 item[
"value"].IsNumber()) {
2456 pTargetData->met_data.swell_per =
2457 static_cast<int>(item[
"value"].GetDouble());
2458 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
2459 item[
"value"].IsNumber()) {
2460 pTargetData->met_data.swell_dir =
2461 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2462 }
else if (update_path ==
"environment.water.temperature" &&
2463 item[
"value"].IsNumber()) {
2464 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
2465 }
else if (update_path ==
"environment.water.salinity" &&
2466 item[
"value"].IsNumber()) {
2467 pTargetData->met_data.salinity = item[
"value"].GetDouble();
2468 }
else if (update_path ==
"environment.water.ice") {
2470 }
else if (update_path ==
"environment.water.iceValue" &&
2471 item[
"value"].IsNumber()) {
2472 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
2473 }
else if (update_path ==
"environment.water.seaStateValue" &&
2474 item[
"value"].IsNumber()) {
2475 pTargetData->met_data.seastate =
2476 static_cast<int>(item[
"value"].GetDouble());
2477 }
else if (update_path ==
"environment.water.seaState") {
2479 }
else if (update_path ==
"environment.outside.precipitation") {
2481 }
else if (update_path ==
"environment.outside.precipitationValue" &&
2482 item[
"value"].IsNumber()) {
2483 pTargetData->met_data.precipitation =
2484 static_cast<int>(item[
"value"].GetDouble());
2485 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
2486 item[
"value"].IsNumber()) {
2487 pTargetData->met_data.airpress_tend =
2488 static_cast<int>(item[
"value"].GetDouble());
2489 }
else if (update_path ==
"environment.outside.pressureTendency") {
2491 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
2492 item[
"value"].IsNumber()) {
2493 pTargetData->met_data.hor_vis =
2494 GEODESIC_METERS2NM(item[
"value"].GetDouble());
2495 }
else if (update_path ==
2496 "environment.outside.horizontalVisibility.overRange") {
2497 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
2498 }
else if (update_path.empty()) {
2499 if (item[
"value"].HasMember(
"name")) {
2500 const wxString &name = item[
"value"][
"name"].GetString();
2501 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
2502 pTargetData->b_nameValid =
true;
2503 pTargetData->MID = 123;
2504 }
else if (item[
"value"].HasMember(
"registrations")) {
2505 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
2506 pTargetData->IMO = wxAtoi(imo.Right(7));
2507 }
else if (item[
"value"].HasMember(
"communication")) {
2508 const wxString &callsign =
2509 item[
"value"][
"communication"][
"callsignVhf"].GetString();
2510 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
2512 if (item[
"value"].HasMember(
"mmsi") &&
2513 1994 != (pTargetData->MMSI) / 100000) {
2515 wxString tmp = item[
"value"][
"mmsi"].GetString();
2516 if (tmp.ToLong(&mmsi)) {
2517 pTargetData->MMSI = mmsi;
2519 if (97 == mmsi / 10000000) {
2520 pTargetData->Class = AIS_SART;
2522 if (111 == mmsi / 1000000) {
2523 pTargetData->b_SarAircraftPosnReport =
true;
2526 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2531 wxLogMessage(wxString::Format(
2532 "** AisDecoder::updateItem: unhandled path %s", update_path));
2534 rapidjson::StringBuffer buffer;
2535 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2536 item.Accept(writer);
2537 wxString msg(
"update: ");
2538 msg.append(buffer.GetString());
2548AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
2549 wxString *accumulator) {
2551 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2553 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
2555 if (!pos)
return AIS_GENERIC_ERROR;
2557 if (!accumulator)
return AIS_GENERIC_ERROR;
2560 if (!str.Mid(1, 5).IsSameAs(
"AIVDO"))
return AIS_GENERIC_ERROR;
2563 wxStringTokenizer tkz(str,
",");
2566 token = tkz.GetNextToken();
2568 token = tkz.GetNextToken();
2569 int nsentences = atoi(token.mb_str());
2571 token = tkz.GetNextToken();
2572 int isentence = atoi(token.mb_str());
2574 token = tkz.GetNextToken();
2575 token = tkz.GetNextToken();
2577 wxString string_to_parse;
2578 string_to_parse.Clear();
2591 if ((1 == nsentences) && (1 == isentence)) {
2592 string_to_parse = tkz.GetNextToken();
2595 else if (nsentences > 1) {
2596 if (1 == isentence) {
2597 *accumulator = tkz.GetNextToken();
2601 accumulator->Append(tkz.GetNextToken());
2604 if (isentence == nsentences) {
2605 string_to_parse = *accumulator;
2609 if (string_to_parse.IsEmpty() &&
2611 return AIS_INCOMPLETE_MULTIPART;
2620 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2622 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
2624 if (bdecode_result) {
2625 switch (TargetData->MID) {
2630 if (!TargetData->b_positionDoubtful) {
2631 pos->
kLat = TargetData->Lat;
2632 pos->
kLon = TargetData->Lon;
2638 if (TargetData->COG == 360.0)
2641 pos->
kCog = TargetData->COG;
2643 if (TargetData->SOG > 102.2)
2646 pos->
kSog = TargetData->SOG;
2648 if ((
int)TargetData->HDG == 511)
2651 pos->
kHdt = TargetData->HDG;
2659 return AIS_GENERIC_ERROR;
2664 return AIS_GENERIC_ERROR;
2672AisError AisDecoder::DecodeN0183(
const wxString &str) {
2673 AisError ret = AIS_GENERIC_ERROR;
2674 wxString string_to_parse;
2676 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
2677 double gpsg_cog, gpsg_sog, gpsg_utc_time;
2678 int gpsg_utc_hour = 0;
2679 int gpsg_utc_min = 0;
2680 int gpsg_utc_sec = 0;
2681 char gpsg_name_str[21];
2684 bool bdecode_result =
false;
2691 long arpa_tgt_num = 0;
2692 double arpa_sog = 0.;
2693 double arpa_cog = 0.;
2694 double arpa_lat = 0.;
2695 double arpa_lon = 0.;
2696 double arpa_dist = 0.;
2697 double arpa_brg = 0.;
2698 wxString arpa_brgunit;
2699 wxString arpa_status;
2700 wxString arpa_distunit;
2701 wxString arpa_cogunit;
2702 double arpa_mins, arpa_degs;
2703 double arpa_utc_time;
2704 int arpa_utc_hour = 0;
2705 int arpa_utc_min = 0;
2706 int arpa_utc_sec = 0;
2707 char arpa_name_str[21];
2708 bool arpa_lost =
true;
2709 bool arpa_nottracked =
false;
2711 double aprs_lat = 0.;
2712 double aprs_lon = 0.;
2713 char aprs_name_str[21];
2714 double aprs_mins, aprs_degs;
2716 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2717 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2718 bool bnewtarget =
false;
2719 int last_report_ticks;
2723 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2725 if (!NMEACheckSumOK(str)) {
2726 return AIS_NMEAVDX_CHECKSUM_BAD;
2728 if (str.Mid(1, 2).IsSameAs(
"CD")) {
2731 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
2735 wxStringTokenizer tkz(str,
",*");
2738 token = tkz.GetNextToken();
2739 token = tkz.GetNextToken();
2740 token.ToLong(&arpa_tgt_num);
2741 token = tkz.GetNextToken();
2742 token.ToDouble(&arpa_dist);
2743 token = tkz.GetNextToken();
2744 token.ToDouble(&arpa_brg);
2745 arpa_brgunit = tkz.GetNextToken();
2746 if (arpa_brgunit ==
"R") {
2747 if (std::isnan(arpa_ref_hdg)) {
2748 if (!std::isnan(
gHdt))
2753 arpa_brg += arpa_ref_hdg;
2754 if (arpa_brg >= 360.) arpa_brg -= 360.;
2756 token = tkz.GetNextToken();
2757 token.ToDouble(&arpa_sog);
2758 token = tkz.GetNextToken();
2759 token.ToDouble(&arpa_cog);
2760 arpa_cogunit = tkz.GetNextToken();
2761 if (arpa_cogunit ==
"R") {
2762 if (std::isnan(arpa_ref_hdg)) {
2763 if (!std::isnan(
gHdt))
2768 arpa_cog += arpa_ref_hdg;
2769 if (arpa_cog >= 360.) arpa_cog -= 360.;
2771 token = tkz.GetNextToken();
2772 token = tkz.GetNextToken();
2774 arpa_distunit = tkz.GetNextToken();
2775 token = tkz.GetNextToken();
2776 if (token ==
"") token = wxString::Format(
"ARPA %ld", arpa_tgt_num);
2777 int len = wxMin(token.Length(), 20);
2778 strncpy(arpa_name_str, token.mb_str(), len);
2779 arpa_name_str[len] = 0;
2780 arpa_status = tkz.GetNextToken();
2781 if (arpa_status !=
"L") {
2783 }
else if (arpa_status !=
"")
2784 arpa_nottracked =
true;
2785 wxString arpa_reftarget = tkz.GetNextToken();
2786 if (tkz.HasMoreTokens()) {
2787 token = tkz.GetNextToken();
2788 token.ToDouble(&arpa_utc_time);
2789 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2790 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2792 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2794 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
2795 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
2796 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
2799 if (arpa_distunit ==
"K") {
2802 }
else if (arpa_distunit ==
"S") {
2807 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2811 }
else if (str.Mid(3, 3).IsSameAs(
"TLL")) {
2814 wxStringTokenizer tkz(str,
",*");
2817 wxString aprs_tll_str = tkz.GetNextToken();
2818 token = tkz.GetNextToken();
2819 token.ToLong(&arpa_tgt_num);
2820 token = tkz.GetNextToken();
2821 token.ToDouble(&arpa_lat);
2822 arpa_degs = (int)(arpa_lat / 100.0);
2823 arpa_mins = arpa_lat - arpa_degs * 100.0;
2824 arpa_lat = arpa_degs + arpa_mins / 60.0;
2825 token = tkz.GetNextToken();
2826 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2827 token.Mid(0, 1).Contains(
"s") ==
true)
2828 arpa_lat = 0. - arpa_lat;
2829 token = tkz.GetNextToken();
2830 token.ToDouble(&arpa_lon);
2831 arpa_degs = (int)(arpa_lon / 100.0);
2832 arpa_mins = arpa_lon - arpa_degs * 100.0;
2833 arpa_lon = arpa_degs + arpa_mins / 60.0;
2834 token = tkz.GetNextToken();
2835 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2836 token.Mid(0, 1).Contains(
"w") ==
true)
2837 arpa_lon = 0. - arpa_lon;
2838 token = tkz.GetNextToken();
2839 if (token ==
"") token = wxString::Format(
"ARPA %d", arpa_tgt_num);
2840 int len = wxMin(token.Length(), 20);
2841 strncpy(arpa_name_str, token.mb_str(), len);
2842 arpa_name_str[len] = 0;
2843 token = tkz.GetNextToken();
2844 token.ToDouble(&arpa_utc_time);
2845 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2846 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2848 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2853 if (arpa_status !=
"L")
2855 else if (arpa_status !=
"")
2856 arpa_nottracked =
true;
2857 wxString arpa_reftarget = tkz.GetNextToken();
2858 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2862 }
else if (str.Mid(3, 3).IsSameAs(
"OSD")) {
2864 wxStringTokenizer tkz(str,
",*");
2867 token = tkz.GetNextToken();
2868 token = tkz.GetNextToken();
2869 token.ToDouble(&arpa_ref_hdg);
2879 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(
"WPL")) {
2881 wxStringTokenizer tkz(str,
",*");
2884 token = tkz.GetNextToken();
2885 token = tkz.GetNextToken();
2886 token.ToDouble(&aprs_lat);
2887 aprs_degs = (int)(aprs_lat / 100.0);
2888 aprs_mins = aprs_lat - aprs_degs * 100.0;
2889 aprs_lat = aprs_degs + aprs_mins / 60.0;
2890 token = tkz.GetNextToken();
2891 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2892 token.Mid(0, 1).Contains(
"s") ==
true)
2893 aprs_lat = 0. - aprs_lat;
2894 token = tkz.GetNextToken();
2895 token.ToDouble(&aprs_lon);
2896 aprs_degs = (int)(aprs_lon / 100.0);
2897 aprs_mins = aprs_lon - aprs_degs * 100.0;
2898 aprs_lon = aprs_degs + aprs_mins / 60.0;
2899 token = tkz.GetNextToken();
2900 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2901 token.Mid(0, 1).Contains(
"w") ==
true)
2902 aprs_lon = 0. - aprs_lon;
2903 token = tkz.GetNextToken();
2904 int len = wxMin(token.Length(), 20);
2905 strncpy(aprs_name_str, token.mb_str(), len + 1);
2906 if (0 == g_WplAction) {
2908 aprs_name_str[len] = 0;
2909 for (i = 0; i < len; i++) {
2911 hash += (int)(aprs_name_str[i]);
2912 while (hash >= 100000) hash = hash / 100000;
2914 mmsi = aprs_mmsi = 199300000 + hash;
2918 }
else if (1 == g_WplAction) {
2920 new RoutePoint(aprs_lat, aprs_lon,
"", aprs_name_str,
"",
false);
2921 pWP->m_bIsolatedMark =
true;
2922 InsertWpt(pWP,
true);
2924 }
else if (str.Mid(1, 5).IsSameAs(
"FRPOS")) {
2928 wxStringTokenizer tkz(str,
",*");
2931 token = tkz.GetNextToken();
2933 token = tkz.GetNextToken();
2934 token.ToDouble(&gpsg_lat);
2935 gpsg_degs = (int)(gpsg_lat / 100.0);
2936 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
2937 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
2939 token = tkz.GetNextToken();
2940 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2941 token.Mid(0, 1).Contains(
"s") ==
true)
2942 gpsg_lat = 0. - gpsg_lat;
2944 token = tkz.GetNextToken();
2945 token.ToDouble(&gpsg_lon);
2946 gpsg_degs = (int)(gpsg_lon / 100.0);
2947 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
2948 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
2950 token = tkz.GetNextToken();
2951 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2952 token.Mid(0, 1).Contains(
"w") ==
true)
2953 gpsg_lon = 0. - gpsg_lon;
2955 token = tkz.GetNextToken();
2958 token = tkz.GetNextToken();
2959 token.ToDouble(&gpsg_sog);
2961 token = tkz.GetNextToken();
2962 token.ToDouble(&gpsg_cog);
2964 token = tkz.GetNextToken();
2967 token = tkz.GetNextToken();
2968 token.ToDouble(&gpsg_utc_time);
2969 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
2970 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
2972 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
2976 token = tkz.GetNextToken();
2977 int i, len, hash = 0;
2978 len = wxMin(wxStrlen(token), 20);
2979 strncpy(gpsg_name_str, token.mb_str(), len);
2980 gpsg_name_str[len] = 0;
2981 for (i = 0; i < len; i++) {
2983 hash += (int)(token[i]);
2984 while (hash >= 100000) hash = hash / 100000;
2987 gpsg_mmsi = 199000000 + hash;
2989 }
else if (!str.Mid(3, 2).IsSameAs(
"VD")) {
2990 return AIS_NMEAVDX_BAD;
2996 string_to_parse.Clear();
2998 if (str.Mid(3, 2).IsSameAs(
"VD")) {
2999 wxStringTokenizer tkz(str,
",");
3002 token = tkz.GetNextToken();
3004 token = tkz.GetNextToken();
3005 nsentences = atoi(token.mb_str());
3007 token = tkz.GetNextToken();
3008 isentence = atoi(token.mb_str());
3010 token = tkz.GetNextToken();
3011 long lsequence_id = 0;
3012 token.ToLong(&lsequence_id);
3014 token = tkz.GetNextToken();
3016 token.ToLong(&lchannel);
3021 if ((1 == nsentences) && (1 == isentence)) {
3022 string_to_parse = tkz.GetNextToken();
3025 else if (nsentences > 1) {
3026 if (1 == isentence) {
3027 sentence_accumulator = tkz.GetNextToken();
3031 sentence_accumulator += tkz.GetNextToken();
3034 if (isentence == nsentences) {
3035 string_to_parse = sentence_accumulator;
3040 if (mmsi || (!string_to_parse.IsEmpty() &&
3041 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
3043 wxCharBuffer abuf = string_to_parse.ToUTF8();
3045 return AIS_GENERIC_ERROR;
3050 if (!mmsi) mmsi = strbit.GetInt(9, 30);
3051 long mmsi_long = mmsi;
3054 int origin_mmsi = 0;
3055 int messID = strbit.GetInt(1, 6);
3056 int dac = strbit.GetInt(41, 10);
3057 int fi = strbit.GetInt(51, 6);
3059 int met_lon, met_lat;
3060 if (dac == 001 && fi == 31) {
3062 met_lon = strbit.GetInt(57, 25);
3063 met_lat = strbit.GetInt(82, 24);
3064 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
3067 }
else if (dac == 367 && fi == 33) {
3069 const int size = strbit.GetBitCount();
3070 if (size < 168)
return AIS_GENERIC_ERROR;
3071 const int startb = 56;
3072 const int slot_size = 112;
3073 const int extra_bits = (size - startb) % slot_size;
3074 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
3076 int mes_type = strbit.GetInt(57, 4);
3077 int site_ID = strbit.GetInt(77, 7);
3078 if (mes_type == 0) {
3080 met_lon = strbit.GetInt(90, 28);
3081 met_lat = strbit.GetInt(118, 27);
3082 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
3087 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
3093 return AIS_GENERIC_ERROR;
3102 auto it = AISTargetList.find(mmsi);
3103 if (it == AISTargetList.end())
3105 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3110 pTargetData->MMSI = mmsi;
3111 pTargetData->met_data.original_mmsi = origin_mmsi;
3114 pTargetData = it->second;
3117 pStaleTarget = pTargetData;
3119 pTargetData->MMSI = mmsi;
3120 pTargetData->met_data.original_mmsi = origin_mmsi;
3125 if (mmsi ==
static_cast<unsigned>(props->MMSI)) {
3127 if (TRACKTYPE_NEVER == props->TrackType) {
3128 pTargetData->b_show_track =
false;
3129 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
3130 pTargetData->b_show_track =
true;
3135 if (props->m_bignore)
return AIS_NoError;
3138 else if (props->m_bVDM) {
3140 if (str.Mid(3, 9).IsSameAs(
"VDM,1,1,,")) {
3141 int message_ID = strbit.GetInt(1, 6);
3144 if ((message_ID <= 3) || (message_ID == 18)) {
3146 pTargetData->b_OwnShip =
true;
3148 wxString aivdostr = str;
3149 aivdostr.replace(1, 5,
"AIVDO");
3150 unsigned char calculated_checksum = 0;
3151 wxString::iterator j;
3152 for (j = aivdostr.begin() + 1; j != aivdostr.end() && *j !=
'*';
3154 calculated_checksum ^=
static_cast<unsigned char>(*j);
3157 if (j <= aivdostr.end() - 3)
3160 wxString::Format(_(
"%02X"), calculated_checksum));
3162 gps_watchdog_timeout_ticks =
3165 std::string full_sentence = aivdostr.ToStdString();
3166 auto address = std::make_shared<NavAddr0183>(
"virtual");
3169 auto msg = std::make_shared<const Nmea0183Msg>(
3170 "AIVDO", full_sentence, address);
3171 NavMsgBus::GetInstance().
Notify(std::move(msg));
3181 wxDateTime now = wxDateTime::Now();
3185 last_report_ticks = pStaleTarget->PositionReportTicks;
3187 last_report_ticks = now.GetTicks();
3191 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3195 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3196 pTargetData->PositionReportTicks = now.GetTicks();
3197 pTargetData->StaticReportTicks = now.GetTicks();
3198 pTargetData->m_utc_hour = gpsg_utc_hour;
3199 pTargetData->m_utc_min = gpsg_utc_min;
3200 pTargetData->m_utc_sec = gpsg_utc_sec;
3201 pTargetData->m_date_string = gpsg_date;
3202 pTargetData->MMSI = gpsg_mmsi;
3203 pTargetData->NavStatus = 0;
3204 pTargetData->Lat = gpsg_lat;
3205 pTargetData->Lon = gpsg_lon;
3206 pTargetData->b_positionOnceValid =
true;
3207 pTargetData->COG = gpsg_cog;
3208 pTargetData->SOG = gpsg_sog;
3209 pTargetData->ShipType = 52;
3210 pTargetData->Class = AIS_GPSG_BUDDY;
3211 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
3212 pTargetData->b_nameValid =
true;
3213 pTargetData->b_active =
true;
3214 pTargetData->b_lost =
false;
3216 bdecode_result =
true;
3217 }
else if (arpa_mmsi) {
3218 pTargetData->m_utc_hour = arpa_utc_hour;
3219 pTargetData->m_utc_min = arpa_utc_min;
3220 pTargetData->m_utc_sec = arpa_utc_sec;
3221 pTargetData->MMSI = arpa_mmsi;
3222 pTargetData->NavStatus = 15;
3223 if (str.Mid(3, 3).IsSameAs(
"TLL")) {
3226 (now.GetTicks() - pTargetData->PositionReportTicks);
3227 if (age_of_last > 0) {
3228 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
3229 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
3230 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3233 pTargetData->Lat = arpa_lat;
3234 pTargetData->Lon = arpa_lon;
3235 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
3236 if (arpa_dist != 0.)
3237 ll_gc_ll(
gLat,
gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
3241 pTargetData->COG = arpa_cog;
3242 pTargetData->SOG = arpa_sog;
3244 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3245 pTargetData->PositionReportTicks = now.GetTicks();
3246 pTargetData->StaticReportTicks = now.GetTicks();
3247 pTargetData->b_positionOnceValid =
true;
3248 pTargetData->ShipType = 55;
3249 pTargetData->Class = AIS_ARPA;
3251 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
3252 if (arpa_status !=
"Q")
3253 pTargetData->b_nameValid =
true;
3255 pTargetData->b_nameValid =
false;
3256 pTargetData->b_active = !arpa_lost;
3257 pTargetData->b_lost = arpa_nottracked;
3259 bdecode_result =
true;
3260 }
else if (aprs_mmsi) {
3261 pTargetData->m_utc_hour = now.GetHour();
3262 pTargetData->m_utc_min = now.GetMinute();
3263 pTargetData->m_utc_sec = now.GetSecond();
3264 pTargetData->MMSI = aprs_mmsi;
3265 pTargetData->NavStatus = 15;
3267 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
3268 if (age_of_last > 0) {
3269 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
3270 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
3271 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3274 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3275 pTargetData->PositionReportTicks = now.GetTicks();
3276 pTargetData->StaticReportTicks = now.GetTicks();
3277 pTargetData->Lat = aprs_lat;
3278 pTargetData->Lon = aprs_lon;
3279 pTargetData->b_positionOnceValid =
true;
3280 pTargetData->ShipType = 56;
3281 pTargetData->Class = AIS_APRS;
3282 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
3283 pTargetData->b_nameValid =
true;
3284 pTargetData->b_active =
true;
3285 pTargetData->b_lost =
false;
3287 bdecode_result =
true;
3291 Parse_VDXBitstring(&strbit, pTargetData);
3295 getMmsiProperties(pTargetData);
3298 pTargetData->RecentPeriod =
3299 pTargetData->PositionReportTicks - last_report_ticks;
3304 pTargetData =
nullptr;
3309 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
3314 if ((n_msgs % 10000) == 0)
3315 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
3316 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
3322void AisDecoder::CommitAISTarget(
3323 const std::shared_ptr<AisTargetData> &pTargetData,
const wxString &str,
3324 bool message_valid,
bool new_target) {
3325 m_pLatestTargetData = pTargetData;
3327 if (!str.IsEmpty()) {
3328 if (str.Mid(3, 3).IsSameAs(
"VDO"))
3329 pTargetData->b_OwnShip =
true;
3331 pTargetData->b_OwnShip =
false;
3334 if (!pTargetData->b_OwnShip) {
3336 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
3338 pTargetData->b_PersistTrack =
false;
3343 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
3350 pTargetData->b_NoTrack =
false;
3355 if (message_valid) {
3357 if (pTargetData->MMSI) {
3358 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
3361 AISTargetList[pTargetData->MMSI] =
3364 if (!pTargetData->area_notices.empty()) {
3365 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
3366 if (it == AIS_AreaNotice_Sources.end())
3367 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
3372 if (!pTargetData->b_OwnShip) {
3373 if (pTargetData->b_positionOnceValid) {
3374 long mmsi_long = pTargetData->MMSI;
3376 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3378 pSel->SetUserData(pTargetData->MMSI);
3382 UpdateOneCPA(pTargetData.get());
3385 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3398 if (!pTargetData->b_OwnShip) {
3399 if (pTargetData->b_positionOnceValid) {
3400 long mmsi_long = pTargetData->MMSI;
3402 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3404 pSel->SetUserData(pTargetData->MMSI);
3411void AisDecoder::getAISTarget(
long mmsi,
3412 std::shared_ptr<AisTargetData> &pTargetData,
3413 std::shared_ptr<AisTargetData> &pStaleTarget,
3414 bool &bnewtarget,
int &last_report_ticks,
3416 now = wxDateTime::Now();
3417 auto it = AISTargetList.find(mmsi);
3418 if (it == AISTargetList.end())
3420 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3424 pTargetData = it->second;
3425 pStaleTarget = pTargetData;
3432 last_report_ticks = pStaleTarget->PositionReportTicks;
3434 last_report_ticks = now.GetTicks();
3438 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
3441std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
3443 double dsc_lat = 0.;
3444 double dsc_lon = 0.;
3445 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
3447 double dse_lat = 0.;
3448 double dse_lon = 0.;
3449 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
3452 int dsc_tx_mmsi = 0;
3454 double dse_cog = 0.;
3455 double dse_sog = 0.;
3456 wxString dse_shipName =
"";
3461 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
3465 wxStringTokenizer tkz(str,
",*");
3468 token = tkz.GetNextToken();
3470 if (str.Mid(3, 3).IsSameAs(
"DSC")) {
3471 m_dsc_last_string = str;
3473 token = tkz.GetNextToken();
3477 token = tkz.GetNextToken();
3479 if (dsc_fmt == 12 || dsc_fmt == 16) {
3480 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3482 token.ToDouble(&dsc_addr);
3483 dsc_mmsi = 0 - (int)(dsc_addr / 10);
3486 token = tkz.GetNextToken();
3487 token.ToLong(&dsc_cat);
3489 token = tkz.GetNextToken();
3490 if (!token.IsSameAs(
"")) {
3491 token.ToLong(&dsc_nature);
3495 token = tkz.GetNextToken();
3497 token = tkz.GetNextToken();
3498 token.ToDouble(&dsc_tmp);
3500 token = tkz.GetNextToken();
3501 token = tkz.GetNextToken();
3502 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(
"")) {
3504 dsc_tx_mmsi = dsc_mmsi;
3505 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3507 token = tkz.GetNextToken();
3508 if (dsc_fmt == 16 && dsc_cat == 12) {
3509 if (!token.IsSameAs(
"")) {
3510 token.ToLong(&dsc_nature);
3514 token = tkz.GetNextToken();
3515 token = tkz.GetNextToken();
3517 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
3519 if (dsc_quadrant > 3)
3522 dsc_lat = (int)(dsc_tmp / 100000.0);
3523 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
3524 dsc_lat = dsc_lat - dsc_quadrant * 10000;
3525 dsc_degs = (int)(dsc_lat / 100.0);
3526 dsc_mins = dsc_lat - dsc_degs * 100.0;
3527 dsc_lat = dsc_degs + dsc_mins / 60.0;
3529 dsc_degs = (int)(dsc_lon / 100.0);
3530 dsc_mins = dsc_lon - dsc_degs * 100.0;
3531 dsc_lon = dsc_degs + dsc_mins / 60.0;
3532 switch (dsc_quadrant) {
3548 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
3550 }
else if (str.Mid(3, 3).IsSameAs(
"DSE")) {
3551 token = tkz.GetNextToken();
3552 token = tkz.GetNextToken();
3553 token = tkz.GetNextToken();
3554 token = tkz.GetNextToken();
3555 dse_mmsi = wxAtoi(token.Mid(
3561 token = tkz.GetNextToken();
3564 token.ToDouble(&dse_tmp);
3565 dse_lat = (int)(dse_tmp / 10000.0);
3566 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3567 dse_lat = dse_lat / 600000.0;
3568 dse_lon = dse_lon / 600000.0;
3571 while (tkz.HasMoreTokens()) {
3572 dseSymbol = tkz.GetNextToken();
3573 token = tkz.GetNextToken();
3574 if (dseSymbol.IsSameAs(
"00")) {
3575 token.ToDouble(&dse_tmp);
3576 dse_lat = (int)(dse_tmp / 10000.0);
3577 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3578 dse_lat = dse_lat / 600000.0;
3579 dse_lon = dse_lon / 600000.0;
3580 }
else if (dseSymbol.IsSameAs(
"01")) {
3581 }
else if (dseSymbol.IsSameAs(
"02")) {
3582 token.ToDouble(&dse_tmp);
3583 dse_sog = dse_tmp / 10.0;
3584 }
else if (dseSymbol.IsSameAs(
"03")) {
3585 token.ToDouble(&dse_tmp);
3586 dse_cog = dse_tmp / 10.0;
3587 }
else if (dseSymbol.IsSameAs(
"04")) {
3588 dse_shipName = DecodeDSEExpansionCharacters(token);
3589 }
else if (dseSymbol.IsSameAs(
"05")) {
3590 }
else if (dseSymbol.IsSameAs(
"06")) {
3593 mmsi = abs((
int)dse_mmsi);
3597 wxDateTime now = wxDateTime::Now();
3599 int last_report_ticks = now.GetTicks();
3602 auto it = AISTargetList.find(mmsi);
3603 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
3604 if (it == AISTargetList.end()) {
3606 pStaleTarget = it->second;
3607 last_report_ticks = pStaleTarget->PositionReportTicks;
3613 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
3615 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
3616 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
3618 m_ptentative_dsctarget->MMSI = mmsi;
3619 m_ptentative_dsctarget->NavStatus =
3621 m_ptentative_dsctarget->Lat = dsc_lat;
3622 m_ptentative_dsctarget->Lon = dsc_lon;
3623 m_ptentative_dsctarget->b_positionOnceValid =
true;
3624 m_ptentative_dsctarget->COG = 0;
3625 m_ptentative_dsctarget->SOG = 0;
3626 m_ptentative_dsctarget->ShipType = dsc_fmt;
3627 m_ptentative_dsctarget->Class = AIS_DSC;
3628 m_ptentative_dsctarget->b_isDSCtarget =
true;
3629 m_ptentative_dsctarget->b_nameValid =
true;
3630 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
3631 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %u",
3633 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
3634 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
3636 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %u",
3640 m_ptentative_dsctarget->b_active =
true;
3641 m_ptentative_dsctarget->b_lost =
false;
3642 m_ptentative_dsctarget->RecentPeriod =
3643 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3646 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
3651 if (dse_mmsi || b_take_dsc) {
3652 if (m_ptentative_dsctarget) {
3658 m_ptentative_dsctarget->Lat =
3659 m_ptentative_dsctarget->Lat +
3660 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
3661 m_ptentative_dsctarget->Lon =
3662 m_ptentative_dsctarget->Lon +
3663 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
3664 if (!dse_shipName.empty()) {
3665 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
3666 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
3667 "%s", dse_shipName.ToAscii().data());
3669 m_ptentative_dsctarget->COG = dse_cog;
3670 m_ptentative_dsctarget->SOG = dse_sog;
3674 m_ptentative_dsctarget->RecentPeriod =
3675 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3680 auto found = AISTargetList.find(mmsi);
3681 if (found == AISTargetList.end()) {
3682 pTargetData = m_ptentative_dsctarget;
3684 pTargetData = found->second;
3685 std::vector<AISTargetTrackPoint> ptrack =
3686 std::move(pTargetData->m_ptrack);
3687 pTargetData->CloneFrom(
3688 m_ptentative_dsctarget
3691 pTargetData->m_ptrack =
3698 m_ptentative_dsctarget =
nullptr;
3700 m_pLatestTargetData = pTargetData;
3702 AISTargetList[pTargetData->MMSI] =
3705 long mmsi_long = pTargetData->MMSI;
3709 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3712 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
3713 (
void *)mmsi_long, SELTYPE_AISTARGET);
3714 pSel->SetUserData(pTargetData->MMSI);
3717 UpdateOneCPA(pTargetData.get());
3720 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3727bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3728 unsigned char checksum_value = 0;
3729 int sentence_hex_sum;
3731 wxCharBuffer buf = str_in.ToUTF8();
3732 if (!buf.data())
return false;
3734 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3735 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3736 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3738 int string_length = strlen(str_ascii);
3740 int payload_length = 0;
3741 while ((payload_length < string_length) &&
3742 (str_ascii[payload_length] !=
'*'))
3745 if (payload_length == string_length)
3750 while (index < payload_length) {
3751 checksum_value ^= str_ascii[index];
3755 if (string_length > 4) {
3757 scanstr[0] = str_ascii[payload_length + 1];
3758 scanstr[1] = str_ascii[payload_length + 2];
3760 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3762 if (sentence_hex_sum == checksum_value)
return true;
3768void AisDecoder::UpdateAllCPA() {
3770 for (
const auto &it : GetTargetList()) {
3771 std::shared_ptr<AisTargetData> td = it.second;
3773 if (
nullptr != td) UpdateOneCPA(td.get());
3777void AisDecoder::UpdateAllTracks() {
3778 for (
const auto &it : GetTargetList()) {
3779 std::shared_ptr<AisTargetData> td = it.second;
3780 if (td) UpdateOneTrack(td.get());
3786 if (!ptarget->b_positionOnceValid)
return;
3788 if (!ptarget->m_ptrack.empty()) {
3790 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3791 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3794 ptarget->m_ptrack.pop_back();
3795 ptarget->b_positionDoubtful =
true;
3802 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3805 ptrackpoint.m_lat = ptarget->Lat;
3806 ptrackpoint.m_lon = ptarget->Lon;
3807 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3809 ptarget->m_ptrack.push_back(ptrackpoint);
3811 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3813 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3815 t->SetName(wxString::Format(
3816 "AIS %s (%u) %s %s", ptarget->GetFullName().c_str(), ptarget->MMSI,
3817 wxDateTime::Now().FormatISODate().c_str(),
3818 wxDateTime::Now().FormatISOTime().c_str()));
3821 m_persistent_tracks[ptarget->MMSI] = t;
3823 t = m_persistent_tracks[ptarget->MMSI];
3826 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3828 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3831 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3832 tp1->m_lon, tp, tp1, t);
3842 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3844 ptarget->m_ptrack.erase(
3845 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3847 return track.m_time < test_time;
3849 ptarget->m_ptrack.end());
3854void AisDecoder::DeletePersistentTrack(
const Track *track) {
3855 for (
auto it = m_persistent_tracks.begin(); it != m_persistent_tracks.end();
3857 if (it->second == track) {
3858 unsigned mmsi = it->first;
3859 m_persistent_tracks.erase(it);
3861 if (0 == m_persistent_tracks.count(mmsi)) {
3865 if (props->m_bPersistentTrack) {
3869 std::shared_ptr<AisTargetData> td =
3870 Get_Target_Data_From_MMSI(mmsi);
3872 props->m_bPersistentTrack =
false;
3873 td->b_mPropPersistTrack =
false;
3875 if (!m_callbacks.confirm_stop_track()) {
3876 props->m_bPersistentTrack =
true;
3888void AisDecoder::UpdateAllAlarms() {
3889 m_bGeneralAlert =
false;
3892 for (
const auto &it : GetTargetList()) {
3893 std::shared_ptr<AisTargetData> td = it.second;
3897 if (!m_bGeneralAlert) {
3899 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3900 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3901 m_bGeneralAlert =
true;
3904 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3905 m_bGeneralAlert =
false;
3908 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3909 m_bGeneralAlert =
false;
3912 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3915 if (td->Class == AIS_SART && td->NavStatus == 14)
3916 m_bGeneralAlert =
true;
3919 if ((td->Class == AIS_DSC) &&
3920 ((td->ShipType == 12) || (td->ShipType == 16)))
3921 m_bGeneralAlert =
true;
3924 ais_alert_type this_alarm = AIS_NO_ALERT;
3927 if (td->Class == AIS_SART && td->NavStatus == 14)
3928 this_alarm = AIS_ALERT_SET;
3931 if ((td->Class == AIS_DSC) &&
3932 ((td->ShipType == 12) || (td->ShipType == 16)))
3933 this_alarm = AIS_ALERT_SET;
3935 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3936 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3939 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3940 td->n_alert_state = AIS_NO_ALERT;
3946 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3947 (td->SOG <= g_ShowMoored_Kts)) {
3948 td->n_alert_state = AIS_NO_ALERT;
3954 if (td->Range_NM > g_CPAMax_NM) {
3955 td->n_alert_state = AIS_NO_ALERT;
3960 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3961 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3962 (td->Class != AIS_METEO)) {
3964 if (td->TCPA < g_TCPA_Max) {
3965 if (td->b_isFollower)
3966 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3968 this_alarm = AIS_ALERT_SET;
3971 if (td->b_isFollower)
3972 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3974 this_alarm = AIS_ALERT_SET;
3981 if (td->Class == AIS_SART) {
3982 if (td->b_in_ack_timeout) {
3983 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3985 if (delta.GetMinutes() >= 10) td->b_in_ack_timeout =
false;
3987 }
else if (g_bAIS_ACK_Timeout ||
3988 ((td->Class == AIS_DSC) &&
3989 ((td->ShipType == 12) || (td->ShipType == 16)))) {
3990 if (td->b_in_ack_timeout) {
3991 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3992 if (delta.GetMinutes() > g_AckTimeout_Mins)
3993 td->b_in_ack_timeout =
false;
3999 if (td->b_in_ack_timeout) {
4000 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
4004 td->n_alert_state = this_alarm;
4010 ptarget->Range_NM = -1.;
4019 DistanceBearingMercator(ptarget->Lat, ptarget->Lon,
gLat,
gLon, &brg, &dist);
4020 ptarget->Range_NM = dist;
4023 if (dist <= 1e-5) ptarget->Brg = -1.0;
4025 if (!ptarget->b_positionOnceValid || !
bGPSValid) {
4026 ptarget->bCPA_Valid =
false;
4030 if (ptarget->Class == AIS_METEO) {
4031 ptarget->bCPA_Valid =
false;
4040 if (ptarget->b_OwnShip) {
4042 ptarget->TCPA = -100;
4043 ptarget->bCPA_Valid =
false;
4047 double cpa_calc_ownship_cog =
gCog;
4048 double cpa_calc_target_cog = ptarget->COG;
4051 if (std::isnan(
gSog) || (
gSog > 102.2)) {
4052 ptarget->bCPA_Valid =
false;
4057 if (std::isnan(
gCog) ||
gCog == 360.0) {
4059 cpa_calc_ownship_cog =
4063 ptarget->bCPA_Valid =
false;
4069 if (ptarget->COG == 360.0) {
4070 if (ptarget->SOG > 102.2) {
4071 ptarget->bCPA_Valid =
false;
4073 }
else if (ptarget->SOG < .01) {
4074 cpa_calc_target_cog = 0.;
4077 ptarget->bCPA_Valid =
false;
4083 double v0 =
gSog * 1852.;
4084 double v1 = ptarget->SOG * 1852.;
4086 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4090 ptarget->bCPA_Valid =
false;
4097 double east1 = (ptarget->Lon -
gLon) * 60 * 1852;
4098 double north1 = (ptarget->Lat -
gLat) * 60 * 1852;
4100 double east = east1 * (cos(
gLat * PI / 180.));
4102 double north = north1;
4105 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4106 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4107 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4108 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4111 double fc = (v0 * cosa) - (v1 * cosb);
4112 double fs = (v0 * sina) - (v1 * sinb);
4114 double d = (fc * fc) + (fs * fs);
4122 tcpa = ((fc * east) + (fs * north)) / d;
4125 ptarget->TCPA = tcpa * 60.;
4130 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4132 ll_gc_ll(
gLat,
gLon, cpa_calc_ownship_cog,
gSog * tcpa, &OwnshipLatCPA,
4134 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4135 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4138 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4141 ptarget->bCPA_Valid =
true;
4143 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4147void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4150 if (m_ptentative_dsctarget) {
4151 ProcessDSx(m_dsc_last_string,
true);
4155void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4160 wxDateTime now = wxDateTime::Now();
4163 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4166 auto it = current_targets.begin();
4167 std::vector<int> remove_array;
4169 while (it != current_targets.end()) {
4173 current_targets.erase(it);
4178 std::shared_ptr<AisTargetData> xtd = it->second;
4180 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4181 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4193 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4195 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4196 double iECD_LostTimeOut = 0.0;
4200 if (xtd->Class == AIS_CLASS_B) {
4201 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR))
4202 iECD_LostTimeOut = 18 * 60;
4204 iECD_LostTimeOut = 180;
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;
4216 if ((target_posn_age > iECD_LostTimeOut) &&
4217 (xtd->Class != AIS_GPSG_BUDDY))
4218 xtd->b_active =
false;
4220 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4221 }
else if (g_bMarkLost) {
4222 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4223 (xtd->Class != AIS_GPSG_BUDDY) && (xtd->Class != AIS_SART))
4224 xtd->b_active =
false;
4227 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4228 removelost_Mins = 18.0;
4232 if (g_bRemoveLost || g_bInlandEcdis) {
4234 (xtd->Class == AIS_ARPA &&
4236 if (((target_posn_age > removelost_Mins * 60) &&
4237 (xtd->Class != AIS_GPSG_BUDDY)) ||
4242 xtd->b_positionOnceValid =
false;
4250 long mmsi_long = xtd->MMSI;
4251 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4256 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4257 xtd->b_removed =
true;
4259 remove_array.push_back(xtd->MMSI);
4268 if (xtd->MMSI == props->MMSI) {
4269 if (props->m_bignore) {
4270 remove_array.push_back(xtd->MMSI);
4271 xtd->b_removed =
true;
4280 remove_array.push_back(xtd->MMSI);
4281 xtd->b_removed =
true;
4289 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4290 auto itd = current_targets.find(remove_array[i]);
4291 if (itd != current_targets.end()) {
4292 std::shared_ptr<AisTargetData> td = itd->second;
4293 current_targets.erase(itd);
4302 m_bSuppressed =
false;
4303 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4304 (g_bShowScaled && g_bAllowShowScaled))
4305 m_bSuppressed =
true;
4307 m_bAIS_Audio_Alert_On =
false;
4316 std::shared_ptr<AisTargetData> palert_target =
nullptr;
4318 if (!g_pais_alert_dialog_active) {
4320 double tcpa_min = 1e6;
4321 double sart_range = 1e6;
4322 std::shared_ptr<AisTargetData> palert_target_cpa =
nullptr;
4323 std::shared_ptr<AisTargetData> palert_target_sart =
nullptr;
4324 std::shared_ptr<AisTargetData> palert_target_dsc =
nullptr;
4326 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4327 std::shared_ptr<AisTargetData> td = it->second;
4329 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4330 if (g_bAIS_CPA_Alert && td->b_active) {
4331 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4332 if (td->TCPA < tcpa_min) {
4333 tcpa_min = td->TCPA;
4334 palert_target_cpa = td;
4338 }
else if ((td->Class == AIS_DSC) &&
4339 ((td->ShipType == 12) || (td->ShipType == 16))) {
4341 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4342 palert_target_dsc = td;
4345 td->b_isDSCtarget =
false;
4350 else if (td->Class == AIS_SART) {
4352 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4353 if (td->Range_NM < sart_range) {
4354 tcpa_min = sart_range;
4355 palert_target_sart = td;
4365 palert_target = palert_target_cpa;
4367 if (palert_target_sart) {
4368 palert_target = palert_target_sart;
4371 if (palert_target_dsc) {
4372 palert_target = palert_target_dsc;
4376 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4381 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4384std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
4386 if (AISTargetList.find(mmsi) == AISTargetList.end())
return nullptr;
4387 return AISTargetList[mmsi];
4394MmsiProperties::MmsiProperties(wxString &spec) {
4396 wxStringTokenizer tkz(spec,
";");
4399 s = tkz.GetNextToken();
4404 s = tkz.GetNextToken();
4406 if (s.Upper() ==
"ALWAYS")
4407 TrackType = TRACKTYPE_ALWAYS;
4408 else if (s.Upper() ==
"NEVER")
4409 TrackType = TRACKTYPE_NEVER;
4412 s = tkz.GetNextToken();
4414 if (s.Upper() ==
"IGNORE") m_bignore =
true;
4417 s = tkz.GetNextToken();
4419 if (s.Upper() ==
"MOB") m_bMOB =
true;
4422 s = tkz.GetNextToken();
4424 if (s.Upper() ==
"VDM") m_bVDM =
true;
4427 s = tkz.GetNextToken();
4429 if (s.Upper() ==
"FOLLOWER") m_bFollower =
true;
4432 s = tkz.GetNextToken();
4434 if (s.Upper() ==
"PERSIST") m_bPersistentTrack =
true;
4437 s = tkz.GetNextToken();
4439 m_ShipName = s.Upper();
4443MmsiProperties::~MmsiProperties() =
default;
4445void MmsiProperties::Init() {
4447 TrackType = TRACKTYPE_DEFAULT;
4451 m_bFollower =
false;
4452 m_bPersistentTrack =
false;
4456wxString MmsiProperties::Serialize() {
4460 sMMSI.Printf(
"%d", MMSI);
4464 if (TRACKTYPE_ALWAYS == TrackType)
4466 else if (TRACKTYPE_NEVER == TrackType)
4487 sMMSI <<
"Follower";
4491 if (m_bPersistentTrack) {
4496 if (m_ShipName ==
"") {
4497 m_ShipName = GetShipNameFromFile(MMSI);
4499 sMMSI << m_ShipName;
4504 AIS_Target_Name_Hash *AISTargetNamesC,
4505 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4506 if (g_benableAISNameCache) {
4507 wxString ship_name =
"";
4510 if (!pTargetData->b_nameValid) {
4511 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4512 if (it != AISTargetNamesC->end()) {
4513 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4514 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4515 ship_name.length() + 1);
4516 pTargetData->b_nameValid =
true;
4517 pTargetData->b_nameFromCache =
true;
4518 }
else if (!g_bUseOnlyConfirmedAISName) {
4519 it = AISTargetNamesNC->find(mmsi);
4520 if (it != AISTargetNamesNC->end()) {
4521 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4522 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4523 ship_name.length() + 1);
4524 pTargetData->b_nameValid =
true;
4525 pTargetData->b_nameFromCache =
true;
4530 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4531 (pTargetData->MID == 19) ||
4532 (pTargetData->MID == 123) ||
4533 (pTargetData->MID == 124)) {
4535 pTargetData->b_nameFromCache =
false;
4536 ship_name = trimAISField(pTargetData->ShipName);
4537 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4538 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4540 AISTargetNamesC->end()) {
4541 if ((*AISTargetNamesC)[mmsi] ==
4543 if (itNC != AISTargetNamesNC->end()) {
4545 AISTargetNamesNC->erase(itNC);
4548 if (itNC != AISTargetNamesNC->end()) {
4550 if ((*AISTargetNamesNC)[mmsi] ==
4553 (*AISTargetNamesC)[mmsi] = ship_name;
4555 AISTargetNamesNC->erase(itNC);
4558 (*AISTargetNamesNC)[mmsi] = ship_name;
4560 if (g_bUseOnlyConfirmedAISName)
4561 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4562 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4564 (*AISTargetNamesNC)[mmsi] = ship_name;
4569 AISTargetNamesNC->end()) {
4570 if ((*AISTargetNamesNC)[mmsi] ==
4573 (*AISTargetNamesC)[mmsi] = ship_name;
4575 AISTargetNamesNC->erase(itNC);
4577 (*AISTargetNamesNC)[mmsi] = ship_name;
4580 (*AISTargetNamesNC)[mmsi] = ship_name;
4582 if (g_bUseOnlyConfirmedAISName) {
4583 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4584 strncpy(pTargetData->ShipName,
"Unknown ",
4592wxString GetShipNameFromFile(
int nmmsi) {
4594 if (g_benableAISNameCache) {
4598 while (getline(infile, line)) {
4599 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4600 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4601 name = tokenizer.GetNextToken().Trim();
4604 tokenizer.GetNextToken();
4612void AisDecoder::UpdateMMSItoNameFile(
const wxString &mmsi,
4613 const wxString &name) {
4617 std::map<wxString, wxString> mmsi_name_map;
4623 while (getline(infile, line)) {
4624 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4625 wxString file_mmsi = tokenizer.GetNextToken();
4626 wxString file_name = tokenizer.GetNextToken().Trim();
4627 mmsi_name_map[file_mmsi] = file_name;
4633 mmsi_name_map[mmsi] = name.Upper();
4638 for (
const auto &pair : mmsi_name_map) {
4639 std::string line = std::string(pair.first.mb_str()) +
"," +
4640 std::string(pair.second.mb_str()) +
"\n";
4647wxString AisDecoder::GetMMSItoNameEntry(
const wxString &mmsi) {
4648 return GetShipNameFromFile(wxAtoi(mmsi));
4652int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4656 if ((!lon_bits || lon_bits == 999) && siteID) {
4659 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4660 if (points.size()) {
4661 for (
const auto &point : points) {
4663 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4665 new_mmsi = point.mmsi;
4671 if (!found && !lon_bits) {
4676 double lon_tentative = 181.;
4677 double lat_tentative = 91.;
4679 if (lon_bits == 25) {
4680 if (m_lon & 0x01000000)
4681 m_lon |= 0xFE000000;
4682 lon_tentative = m_lon / 60000.;
4684 if (m_lat & 0x00800000)
4685 m_lat |= 0xFF000000;
4686 lat_tentative = m_lat / 60000.;
4688 }
else if (lon_bits == 28) {
4689 if (m_lon & 0x08000000)
4690 m_lon |= 0xf0000000;
4691 lon_tentative = m_lon / 600000.;
4693 if (m_lat & 0x04000000)
4694 m_lat |= 0xf8000000;
4695 lat_tentative = m_lat / 600000.;
4700 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4701 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4709 static int nextMeteommsi = 199400000;
4710 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4712 if (lon_bits != 999 && !points.empty()) {
4713 wxString t_lat, t_lon;
4714 for (
const auto &point : points) {
4716 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4718 new_mmsi = point.mmsi;
4727 points.emplace_back(
4728 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4729 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
EventVar plugin_msg
A JSON message should be sent.
EventVar new_track
Notified on new track creation.
EventVar info_update
Notified when AIS user dialogs should update.
EventVar touch_state
Notified when gFrame->TouchAISActive() should be invoked.
Add a new point to the list of Meteo stations.
void Notify() override
Notify all listeners, no data supplied.
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 listener on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Represents a waypoint or mark within the navigation system.
A parsed SignalK message over ipv4.
Represents a single point in a track.
Represents a track, which is a series of connected track points.
The JSON document writer.
void Write(const wxJSONValue &value, wxString &str)
Write the JSONvalue object to a JSON text.
Raw messages layer, supports sending and recieving navmsg messages.
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.