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);
627 if (ptd->Class == AIS_CLASS_A) {
630 if (!ptd->b_isDSCtarget) {
631 if (!isBuoyMmsi(ptd->MMSI))
632 ptd->Class = AIS_CLASS_B;
634 ptd->Class = AIS_BUOY;
641 ptd->Class = AIS_BASE;
643 ptd->m_utc_hour = bstr->
GetInt(62, 5);
644 ptd->m_utc_min = bstr->
GetInt(67, 6);
645 ptd->m_utc_sec = bstr->
GetInt(73, 6);
647 int lon = bstr->
GetInt(80, 28);
648 if (lon & 0x08000000)
650 double lon_tentative = lon / 600000.;
652 int lat = bstr->
GetInt(108, 27);
653 if (lat & 0x04000000)
655 double lat_tentative = lat / 600000.;
657 if ((lon_tentative <= 180.) &&
661 ptd->Lon = lon_tentative;
662 ptd->Lat = lat_tentative;
663 ptd->b_positionDoubtful =
false;
664 ptd->b_positionOnceValid =
true;
665 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
666 ptd->PositionReportTicks = now.GetTicks();
668 ptd->b_positionDoubtful =
true;
675 b_posn_report =
true;
681 ptd->SOG = bstr->
GetInt(51, 10);
683 int lon = bstr->
GetInt(62, 28);
684 if (lon & 0x08000000)
686 double lon_tentative = lon / 600000.;
688 int lat = bstr->
GetInt(90, 27);
689 if (lat & 0x04000000)
691 double lat_tentative = lat / 600000.;
693 if ((lon_tentative <= 180.) &&
697 ptd->Lon = lon_tentative;
698 ptd->Lat = lat_tentative;
699 ptd->b_positionDoubtful =
false;
700 ptd->b_positionOnceValid =
true;
701 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
702 ptd->PositionReportTicks = now.GetTicks();
704 ptd->b_positionDoubtful =
true;
707 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
709 int alt_tent = bstr->
GetInt(39, 12);
710 ptd->altitude = alt_tent;
712 ptd->b_SarAircraftPosnReport =
true;
715 b_posn_report =
true;
721 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
727 ptd->DimA = bstr->
GetInt(220, 9);
728 ptd->DimB = bstr->
GetInt(229, 9);
729 ptd->DimC = bstr->
GetInt(238, 6);
730 ptd->DimD = bstr->
GetInt(244, 6);
733 ptd->m_utc_sec = bstr->
GetInt(254, 6);
735 int offpos = bstr->
GetInt(260, 1);
736 int virt = bstr->
GetInt(270, 1);
739 ptd->NavStatus = ATON_VIRTUAL;
741 ptd->NavStatus = ATON_REAL;
742 if (ptd->m_utc_sec <= 59 ) {
744 if (offpos) ptd->NavStatus += 1;
747 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
750 if (bstr->GetBitCount() > 276) {
751 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
752 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
753 ptd->ShipNameExtension[14] = 0;
755 ptd->ShipNameExtension[0] = 0;
758 ptd->b_nameValid =
true;
762 ptd->Class = AIS_ATON;
764 int lon = bstr->
GetInt(165, 28);
766 if (lon & 0x08000000)
768 double lon_tentative = lon / 600000.;
770 int lat = bstr->
GetInt(193, 27);
772 if (lat & 0x04000000)
774 double lat_tentative = lat / 600000.;
776 if ((lon_tentative <= 180.) &&
780 ptd->Lon = lon_tentative;
781 ptd->Lat = lat_tentative;
782 ptd->b_positionDoubtful =
false;
783 ptd->b_positionOnceValid =
true;
784 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
785 ptd->PositionReportTicks = now.GetTicks();
787 ptd->b_positionDoubtful =
true;
789 b_posn_report =
true;
794 int dac = bstr->
GetInt(41, 10);
795 int fi = bstr->
GetInt(51, 6);
801 ptd->b_isEuroInland =
true;
803 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
804 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
805 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
806 ptd->UN_shiptype = bstr->
GetInt(128, 14);
807 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
811 if (dac == 1 || dac == 366)
815 if (bstr->GetBitCount() >= 111) {
817 an.link_id = bstr->
GetInt(57, 10);
818 an.notice_type = bstr->
GetInt(67, 7);
819 an.month = bstr->
GetInt(74, 4);
820 an.day = bstr->
GetInt(78, 5);
821 an.hour = bstr->
GetInt(83, 5);
822 an.minute = bstr->
GetInt(88, 6);
823 an.duration_minutes = bstr->
GetInt(94, 18);
825 wxDateTime now_ = wxDateTime::Now();
828 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
829 now_.GetYear(), an.hour, an.minute);
834 if (an.start_time > now_ + wxTimeSpan::Hours(48))
835 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
836 now_.GetYear() - 1, an.hour, an.minute);
839 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
844 if (an.expiry_time < now_ - wxTimeSpan::Hours(24)) {
845 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
846 now_.GetYear() + 1, an.hour, an.minute);
848 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
853 int subarea_len = 87;
856 float pos_scale = 60000.0;
864 pos_scale = 600000.0;
869 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
870 for (
int i = 0; i < subarea_count; ++i) {
871 int base = 111 + i * subarea_len;
873 sa.shape = bstr->
GetInt(base + 1, 3);
874 int scale_factor = 1;
875 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
878 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
879 sa.text = wxString(t, wxConvUTF8);
881 int scale_multipliers[4] = {1, 10, 100, 1000};
882 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
884 case AIS8_001_22_SHAPE_SECTOR:
885 sa.left_bound_deg = bstr->
GetInt(
886 base + 6 + lon_len + lat_len + prec_size + 12, 9);
887 sa.right_bound_deg = bstr->
GetInt(
888 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
889 case AIS8_001_22_SHAPE_CIRCLE:
891 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
894 case AIS8_001_22_SHAPE_RECT:
896 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
898 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
901 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
906 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
908 sa.orient_deg = bstr->
GetInt(
909 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
911 case AIS8_001_22_SHAPE_POLYLINE:
912 case AIS8_001_22_SHAPE_POLYGON:
913 for (
int j = 0; j < 4; ++j) {
914 sa.angles[j] = bstr->
GetInt(base + 6 + j * 20, 10) * 0.5;
916 bstr->
GetInt(base + 16 + j * 20, 10) * scale_factor;
920 an.sub_areas.push_back(sa);
922 ptd->area_notices[an.link_id] = an;
929 if (bstr->GetBitCount() >= 360) {
931 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
934 double lon_tentative = 181.;
935 double lat_tentative = 91.;
937 int lon = bstr->
GetInt(57, 25);
938 int lat = bstr->
GetInt(82, 24);
940 if (lon & 0x01000000)
942 lon_tentative = lon / 60000.;
944 if (lat & 0x00800000)
946 lat_tentative = lat / 60000.;
948 ptd->Lon = lon_tentative;
949 ptd->Lat = lat_tentative;
952 wxString x = ptd->ShipName;
953 if (x.Find(
"METEO") == wxNOT_FOUND) {
955 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
956 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
959 wxString nameID =
"METEO ";
960 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
961 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
964 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
965 ptd->met_data.day = bstr->
GetInt(107, 5);
966 ptd->met_data.hour = bstr->
GetInt(112, 5);
967 ptd->met_data.minute = bstr->
GetInt(117, 6);
968 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
969 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
970 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
971 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
973 int tmp = bstr->
GetInt(155, 11);
974 if (tmp & 0x00000400)
976 ptd->met_data.air_temp = tmp / 10.;
977 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
978 int dew = bstr->
GetInt(173, 10);
979 if (dew & 0x00000200)
981 ptd->met_data.dew_point = dew / 10.;
986 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
987 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
989 int horVis = bstr->
GetInt(194, 8);
990 if (horVis & 0x80u) {
992 ptd->met_data.hor_vis_GT =
true;
994 ptd->met_data.hor_vis_GT =
false;
996 ptd->met_data.hor_vis = horVis / 10.0;
998 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
999 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
1000 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
1001 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
1002 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
1003 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
1004 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
1005 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
1006 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
1007 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
1008 ptd->met_data.seastate = bstr->
GetInt(323, 4);
1010 int wt = bstr->
GetInt(327, 10);
1011 if (wt & 0x00000200)
1013 ptd->met_data.water_temp = wt / 10.;
1015 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
1016 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
1017 ptd->met_data.ice = bstr->
GetInt(349, 2);
1019 ptd->Class = AIS_METEO;
1023 ptd->b_NoTrack =
true;
1024 ptd->b_show_track =
false;
1025 ptd->b_positionDoubtful =
false;
1026 ptd->b_positionOnceValid =
true;
1027 b_posn_report =
true;
1028 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1029 ptd->PositionReportTicks = now.GetTicks();
1030 ptd->b_nameValid =
true;
1032 parse_result =
true;
1038 if (dac == 367 && fi == 33) {
1042 const int size = bstr->GetBitCount();
1045 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
1046 const int startbits = 56;
1047 const int slotsize = 112;
1048 const int slots_count = (size - startbits) / slotsize;
1050 for (
int slot = 0; slot < slots_count; slot++) {
1051 slotbit = slot * slotsize;
1052 int type = bstr->
GetInt(slotbit + 57, 4);
1053 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
1054 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
1055 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
1058 if (!ptd->b_nameValid) {
1059 wxString nameID =
"METEO Site: ";
1061 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
1062 ptd->b_nameValid =
true;
1066 int lon = bstr->
GetInt(slotbit + 90, 28);
1067 if (lon & 0x08000000)
1069 ptd->Lon = lon / 600000.;
1071 int lat = bstr->
GetInt(slotbit + 118, 27);
1072 if (lat & 0x04000000)
1074 ptd->Lat = lat / 600000.;
1075 ptd->b_positionOnceValid =
true;
1077 }
else if (type == 1) {
1078 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
1079 ptd->b_nameValid =
true;
1081 }
else if (type == 2) {
1083 int descr = bstr->
GetInt(slotbit + 116, 3);
1084 if (descr == 1 || descr == 2) {
1085 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1086 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1087 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1088 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
1091 }
else if (type == 3) {
1093 int descr = bstr->
GetInt(slotbit + 108, 3);
1094 if (descr == 1 || descr == 2) {
1095 int wltype = bstr->
GetInt(slotbit + 84, 1);
1096 int wl = bstr->
GetInt(slotbit + 85, 16);
1097 if (wl & 0x00004000)
1101 ptd->met_data.water_level = wl / 100.;
1103 ptd->met_data.water_lev_dev = wl / 100.;
1105 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
1106 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
1108 }
else if (type == 6) {
1109 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
1110 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
1111 }
else if (type == 7) {
1113 bstr->
GetInt(slotbit + 111, 3);
1114 if (swell_descr == 1 || swell_descr == 2) {
1115 ptd->met_data.swell_height =
1116 bstr->
GetInt(slotbit + 84, 8) / 10.0;
1117 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
1118 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
1120 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
1121 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
1122 if (wt_descr == 1 || wt_descr == 2)
1123 ptd->met_data.water_temp =
1124 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
1126 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
1127 if (wawe_descr == 1 || wawe_descr == 2) {
1128 ptd->met_data.wave_height =
1129 bstr->
GetInt(slotbit + 134, 8) / 10.0;
1130 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
1131 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
1133 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
1135 }
else if (type == 8) {
1136 ptd->met_data.water_temp =
1137 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
1138 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
1140 }
else if (type == 9) {
1141 int tmp = bstr->
GetInt(slotbit + 84, 11);
1142 if (tmp & 0x00000400)
1144 ptd->met_data.air_temp = tmp / 10.;
1145 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
1156 ptd->met_data.precipitation = pp;
1157 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
1158 ptd->met_data.dew_point =
1159 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
1160 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
1161 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
1162 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
1164 }
else if (type == 11) {
1166 int descr = bstr->
GetInt(slotbit + 113, 3);
1167 if (descr == 1 || descr == 2) {
1168 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1169 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1170 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1175 if (ptd->b_positionOnceValid) {
1176 ptd->Class = AIS_METEO;
1180 ptd->b_NoTrack =
true;
1181 ptd->b_show_track =
false;
1182 ptd->b_positionDoubtful =
false;
1183 b_posn_report =
true;
1184 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1185 ptd->PositionReportTicks = now.GetTicks();
1186 ptd->b_nameValid =
true;
1188 parse_result =
true;
1198 char msg_14_text[968];
1199 if (bstr->GetBitCount() > 40) {
1200 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
1201 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
1203 nd = wxMin(nd, 967);
1204 msg_14_text[nd] = 0;
1205 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
1207 parse_result =
true;
1220 if (b_posn_report) ptd->b_lost =
false;
1222 if (
true == parse_result) {
1224 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
1225 ptd->b_active =
true;
1228 return parse_result;
1232 : m_signalk_selfid(
""), m_callbacks(callbacks) {
1234 AISTargetNamesC =
new AIS_Target_Name_Hash;
1235 AISTargetNamesNC =
new AIS_Target_Name_Hash;
1237 if (g_benableAISNameCache) {
1241 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
1242 wxString line = infile.GetFirstLine();
1243 while (!infile.Eof()) {
1244 if (line.IsSameAs(
"+++==Confirmed Entry's==+++"))
1245 HashFile = AISTargetNamesC;
1247 if (line.IsSameAs(
"+++==Non Confirmed Entry's==+++"))
1248 HashFile = AISTargetNamesNC;
1250 wxStringTokenizer tokenizer(line,
",");
1251 int mmsi = wxAtoi(tokenizer.GetNextToken());
1252 wxString name = tokenizer.GetNextToken().Trim();
1253 (*HashFile)[mmsi] = name;
1256 line = infile.GetNextLine();
1263 BuildERIShipTypeHash();
1265 g_pais_alert_dialog_active =
nullptr;
1266 m_bAIS_Audio_Alert_On =
false;
1270 m_bAIS_AlertPlaying =
false;
1272 TimerAIS.SetOwner(
this, TIMER_AIS1);
1273 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
1275 m_ptentative_dsctarget =
nullptr;
1276 m_dsc_timer.SetOwner(
this, TIMER_DSC);
1283 InitCommListeners();
1286AisDecoder::~AisDecoder() {
1296 wxString content =
"+++==Confirmed Entry's==+++";
1297 AIS_Target_Name_Hash::iterator it;
1298 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
1299 content.append(
"\r\n");
1300 content.append(wxString::Format(
"%i", it->first));
1301 content.append(
",").append(it->second);
1303 content.append(
"\r\n");
1304 content.append(
"+++==Non Confirmed Entry's==+++");
1305 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
1306 content.append(
"\r\n");
1307 content.append(wxString::Format(
"%i", it->first));
1308 content.append(
",").append(it->second);
1310 outfile.Write(content);
1314 AISTargetNamesC->clear();
1315 delete AISTargetNamesC;
1316 AISTargetNamesNC->clear();
1317 delete AISTargetNamesNC;
1322 m_AIS_Audio_Alert_Timer.Stop();
1327 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
1329 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
1333bool IsTargetOnTheIgnoreList(
const int &mmsi) {
1338 if (props->m_bignore) {
1346void AisDecoder::InitCommListeners() {
1352 listener_N0183_VDM.Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
1355 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1356 HandleN0183_AIS(n0183_msg);
1361 listener_N0183_FRPOS.Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
1363 Bind(EVT_N0183_FRPOS, [&](
const ObservedEvt &ev) {
1365 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1366 HandleN0183_AIS(n0183_msg);
1371 listener_N0183_CDDSC.Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
1372 Bind(EVT_N0183_CDDSC, [&](
const ObservedEvt &ev) {
1374 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1375 HandleN0183_AIS(n0183_msg);
1380 listener_N0183_CDDSE.Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
1381 Bind(EVT_N0183_CDDSE, [&](
const ObservedEvt &ev) {
1383 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1384 HandleN0183_AIS(n0183_msg);
1389 listener_N0183_TLL.Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
1393 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1394 HandleN0183_AIS(n0183_msg);
1399 listener_N0183_TTM.Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
1402 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1403 HandleN0183_AIS(n0183_msg);
1408 listener_N0183_OSD.Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
1411 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1412 HandleN0183_AIS(n0183_msg);
1417 listener_N0183_WPL.Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
1420 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1421 HandleN0183_AIS(n0183_msg);
1426 listener_SignalK.Listen(sk_msg,
this, EVT_SIGNALK);
1428 HandleSignalK(obs::UnpackEvtPointer<SignalkMsg>(ev));
1433 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
1434 listener_N2K_129038.Listen(n2k_msg_129038,
this, EVT_N2K_129038);
1436 HandleN2K_129038(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1441 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
1442 listener_N2K_129039.Listen(n2k_msg_129039,
this, EVT_N2K_129039);
1444 HandleN2K_129039(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1449 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
1450 listener_N2K_129041.Listen(n2k_msg_129041,
this, EVT_N2K_129041);
1452 HandleN2K_129041(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1457 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
1458 listener_N2K_129794.Listen(n2k_msg_129794,
this, EVT_N2K_129794);
1460 HandleN2K_129794(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1465 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
1466 listener_N2K_129809.Listen(n2k_msg_129809,
this, EVT_N2K_129809);
1468 HandleN2K_129809(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1473 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
1474 listener_N2K_129810.Listen(n2k_msg_129810,
this, EVT_N2K_129810);
1476 HandleN2K_129810(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1481 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
1482 listener_N2K_129793.Listen(n2k_msg_129793,
this, EVT_N2K_129793);
1484 HandleN2K_129793(obs::UnpackEvtPointer<Nmea2000Msg>(ev));
1488bool AisDecoder::HandleN0183_AIS(
const N0183MsgPtr &n0183_msg) {
1489 std::string str = n0183_msg->payload;
1490 wxString sentence(str.c_str());
1491 DecodeN0183(sentence);
1496bool AisDecoder::HandleN2K_129038(
const N2000MsgPtr &n2k_msg) {
1497 std::vector<unsigned char> v = n2k_msg->payload;
1500 tN2kAISRepeat Repeat;
1511 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
1512 tN2kAISTransceiverInformation AISTransceiverInformation;
1514 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
1515 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
1516 NavStat, AISTransceiverInformation)) {
1517 unsigned mmsi = UserID;
1519 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1523 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1524 bool bnewtarget =
false;
1526 auto it = AISTargetList.find(mmsi);
1527 if (it == AISTargetList.end())
1529 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1533 pTargetData = it->second;
1536 wxDateTime now = wxDateTime::Now();
1540 pTargetData->MMSI = mmsi;
1541 pTargetData->MID = MessageID;
1542 pTargetData->MMSI = mmsi;
1543 pTargetData->Class = AIS_CLASS_A;
1545 if (97 == pTargetData->MMSI / 10000000) {
1546 pTargetData->Class = AIS_SART;
1548 pTargetData->StaticReportTicks = now.GetTicks();
1550 pTargetData->NavStatus =
static_cast<ais_nav_status
>(NavStat);
1551 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1552 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1553 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1554 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1555 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1557 pTargetData->ROTAIS = ROT;
1569 pTargetData->b_OwnShip =
1570 AISTransceiverInformation ==
1571 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1572 pTargetData->b_active =
true;
1573 pTargetData->b_lost =
false;
1574 pTargetData->b_positionOnceValid =
true;
1575 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1576 pTargetData->PositionReportTicks = now.GetTicks();
1578 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1579 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1588bool AisDecoder::HandleN2K_129039(
const N2000MsgPtr &n2k_msg) {
1589 std::vector<unsigned char> v = n2k_msg->payload;
1604 tN2kAISRepeat Repeat;
1614 tN2kAISTransceiverInformation AISTransceiverInformation;
1616 bool DSC, Band, Msg22, State, Display;
1619 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
1620 Accuracy, RAIM, Seconds, COG, SOG,
1621 AISTransceiverInformation, Heading, Unit, Display, DSC,
1622 Band, Msg22, Mode, State)) {
1623 uint32_t mmsi = UserID;
1625 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1629 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1630 bool bnewtarget =
false;
1632 auto it = AISTargetList.find(mmsi);
1633 if (it == AISTargetList.end())
1635 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1639 pTargetData = it->second;
1642 wxDateTime now = wxDateTime::Now();
1646 pTargetData->MMSI = mmsi;
1647 pTargetData->MID = MessageID;
1648 if (!isBuoyMmsi(mmsi))
1649 pTargetData->Class = AIS_CLASS_B;
1651 pTargetData->Class = AIS_BUOY;
1653 pTargetData->NavStatus = UNDEFINED;
1654 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1655 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1656 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1657 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1658 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1660 pTargetData->b_positionOnceValid =
true;
1661 pTargetData->b_active =
true;
1662 pTargetData->b_lost =
false;
1663 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1664 pTargetData->PositionReportTicks = now.GetTicks();
1665 pTargetData->b_OwnShip =
1666 AISTransceiverInformation ==
1667 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1669 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1670 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1680bool AisDecoder::HandleN2K_129041(
const N2000MsgPtr &n2k_msg) {
1681 std::vector<unsigned char> v = n2k_msg->payload;
1683 tN2kAISAtoNReportData data;
1686 struct tN2kAISAtoNReportData {
1688 tN2kAISRepeat Repeat;
1697 double PositionReferenceStarboard ;
1698 double PositionReferenceTrueNorth;
1699 tN2kAISAtoNType AtoNType;
1700 bool OffPositionIndicator;
1701 bool VirtualAtoNFlag;
1702 bool AssignedModeFlag;
1703 tN2kGNSStype GNSSType;
1705 tN2kAISTransceiverInformation AISTransceiverInformation;
1706 char AtoNName[34 + 1];
1709 if (ParseN2kPGN129041(v, data)) {
1710 uint32_t mmsi = data.UserID;
1712 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1716 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1717 bool bnewtarget =
false;
1719 auto it = AISTargetList.find(mmsi);
1720 if (it == AISTargetList.end())
1722 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1726 pTargetData = it->second;
1730 pTargetData->MMSI = mmsi;
1732 wxDateTime now = wxDateTime::Now();
1735 int offpos = data.OffPositionIndicator;
1736 int virt = data.VirtualAtoNFlag;
1739 pTargetData->NavStatus = ATON_VIRTUAL;
1741 pTargetData->NavStatus = ATON_REAL;
1743 pTargetData->m_utc_sec = data.Seconds;
1745 if (pTargetData->m_utc_sec <= 59) {
1746 pTargetData->NavStatus += 1;
1747 if (offpos) pTargetData->NavStatus += 1;
1750 data.AtoNName[34] = 0;
1751 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
1752 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1753 pTargetData->b_nameValid =
true;
1754 pTargetData->MID = 124;
1756 pTargetData->ShipType = data.AtoNType;
1757 pTargetData->Class = AIS_ATON;
1759 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
1760 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
1761 pTargetData->b_positionDoubtful =
false;
1762 pTargetData->b_positionOnceValid =
true;
1763 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1764 pTargetData->PositionReportTicks = now.GetTicks();
1768 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1769 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1771 touch_state.Notify();
1778bool AisDecoder::HandleN2K_129794(
const N2000MsgPtr &n2k_msg) {
1779 std::vector<unsigned char> v = n2k_msg->payload;
1782 tN2kAISRepeat Repeat;
1786 char Name[SHIP_NAME_LEN];
1795 char Destination[DESTINATION_LEN];
1796 tN2kAISVersion AISversion;
1797 tN2kGNSStype GNSStype;
1799 tN2kAISTranceiverInfo AISinfo;
1801 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
1802 VesselType, Length, Beam, PosRefStbd, PosRefBow,
1803 ETAdate, ETAtime, Draught, Destination, AISversion,
1804 GNSStype, DTE, AISinfo)) {
1805 uint32_t mmsi = UserID;
1807 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1811 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1812 bool bnewtarget =
false;
1814 auto it = AISTargetList.find(mmsi);
1815 if (it == AISTargetList.end())
1817 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1821 pTargetData = it->second;
1825 pTargetData->MMSI = mmsi;
1826 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1827 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1828 Name[
sizeof(Name) - 1] = 0;
1829 pTargetData->b_nameValid =
true;
1830 pTargetData->MID = 124;
1832 pTargetData->b_OwnShip =
1834 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
1836 pTargetData->DimA = PosRefBow;
1837 pTargetData->DimB = Length - PosRefBow;
1838 pTargetData->DimC = Beam - PosRefStbd;
1839 pTargetData->DimD = PosRefStbd;
1840 pTargetData->Draft = Draught;
1841 pTargetData->IMO = IMOnumber;
1842 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1843 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1844 pTargetData->ShipType = (
unsigned char)VesselType;
1845 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
1846 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
1847 Destination[
sizeof(Destination) - 1] = 0;
1849 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
1850 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
1851 wxDateTime t((time_t)secs);
1853 wxDateTime tz = t.ToUTC();
1854 pTargetData->ETA_Mo = tz.GetMonth() + 1;
1855 pTargetData->ETA_Day = tz.GetDay();
1856 pTargetData->ETA_Hr = tz.GetHour();
1857 pTargetData->ETA_Min = tz.GetMinute();
1861 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1862 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1870bool AisDecoder::HandleN2K_129809(
const N2000MsgPtr &n2k_msg) {
1871 std::vector<unsigned char> v = n2k_msg->payload;
1874 tN2kAISRepeat Repeat;
1878 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
1879 unsigned mmsi = UserID;
1881 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1885 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1886 bool bnewtarget =
false;
1888 auto it = AISTargetList.find(mmsi);
1889 if (it == AISTargetList.end())
1891 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1895 pTargetData = it->second;
1899 pTargetData->MMSI = mmsi;
1900 Name[
sizeof(Name) - 1] = 0;
1901 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1902 pTargetData->b_nameValid =
true;
1903 pTargetData->MID = 124;
1905 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1906 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1916bool AisDecoder::HandleN2K_129810(
const N2000MsgPtr &n2k_msg) {
1917 std::vector<unsigned char> v = n2k_msg->payload;
1920 tN2kAISRepeat Repeat;
1929 uint32_t MothershipID;
1931 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
1932 Callsign, Length, Beam, PosRefStbd, PosRefBow,
1934 unsigned mmsi = UserID;
1936 if (mmsi ==
g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1940 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1941 bool bnewtarget =
false;
1943 auto it = AISTargetList.find(mmsi);
1944 if (it == AISTargetList.end())
1946 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1950 pTargetData = it->second;
1954 pTargetData->MMSI = mmsi;
1955 pTargetData->DimA = PosRefBow;
1956 pTargetData->DimB = Length - PosRefBow;
1957 pTargetData->DimC = Beam - PosRefStbd;
1958 pTargetData->DimD = PosRefStbd;
1959 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1960 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1961 pTargetData->ShipType = (
unsigned char)VesselType;
1963 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
1964 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1973bool AisDecoder::HandleN2K_129793(
const N2000MsgPtr &n2k_msg) {
1974 std::vector<unsigned char> v = n2k_msg->payload;
1977 tN2kAISRepeat Repeat;
1981 unsigned int SecondsSinceMidnight;
1982 unsigned int DaysSinceEpoch;
1984 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
1985 SecondsSinceMidnight, DaysSinceEpoch)) {
1986 wxDateTime now = wxDateTime::Now();
1992 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1993 bool bnewtarget =
false;
1995 auto it = AISTargetList.find(mmsi);
1996 if (it == AISTargetList.end())
1998 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2002 pTargetData = it->second;
2006 pTargetData->MMSI = mmsi;
2007 pTargetData->Class = AIS_BASE;
2009 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
2010 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
2011 pTargetData->b_positionDoubtful =
false;
2012 pTargetData->b_positionOnceValid =
true;
2013 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2014 pTargetData->PositionReportTicks = now.GetTicks();
2018 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
2019 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
2038void AisDecoder::HandleSignalK(
const SignalKMsgPtr &sK_msg) {
2039 rapidjson::Document root;
2041 root.Parse(sK_msg->raw_message);
2043 if (root.HasParseError())
return;
2045 if (root.HasMember(
"self")) {
2051 if (m_signalk_selfid.IsEmpty()) {
2056 int meteo_SiteID = 0;
2057 if (root.HasMember(
"context") && root[
"context"].IsString()) {
2058 wxString context = root[
"context"].GetString();
2059 if (context == m_signalk_selfid) {
2061 wxLogMessage(
"** Ignore context own ship..");
2065 wxString mmsi_string;
2066 if (context.StartsWith(
"vessels.urn:mrn:imo:mmsi:", &mmsi_string) ||
2067 context.StartsWith(
"atons.urn:mrn:imo:mmsi:", &mmsi_string) ||
2068 context.StartsWith(
"aircraft.urn:mrn:imo:mmsi:", &mmsi_string)) {
2071 if (mmsi_string.ToLong(&mmsi)) {
2076 }
else if (context.StartsWith(
"meteo.urn:mrn:imo:mmsi:", &mmsi_string)) {
2078 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
2079 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
2082 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
2093 if (g_pMUX && g_pMUX->IsLogActive()) {
2095 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
2100 if (IsTargetOnTheIgnoreList(mmsi))
return;
2106 wxLogDebug(
"AisDecoder::OnEvtSignalK: %s", root.dump().c_str(); );
2108 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2109 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2110 bool bnewtarget =
false;
2111 int last_report_ticks;
2113 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
2116 pTargetData->MMSI = mmsi;
2117 getMmsiProperties(pTargetData);
2118 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
2119 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
2120 itr != root[
"updates"].End(); ++itr) {
2121 handleUpdate(pTargetData, bnewtarget, *itr);
2126 if (97 == mmsi / 10000000) {
2127 pTargetData->Class = AIS_SART;
2128 }
else if (1994 == mmsi / 100000) {
2130 pTargetData->Class = AIS_METEO;
2131 pTargetData->met_data.original_mmsi = origin_mmsi;
2132 pTargetData->met_data.stationID = meteo_SiteID;
2135 wxString met_name = pTargetData->ShipName;
2136 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
2139 s_id << meteo_SiteID;
2140 id1 = wxAtoi(s_id.Mid(1, 3));
2141 id2 = wxAtoi(s_id.Mid(4, 3));
2142 met_name =
"METEO ";
2143 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
2144 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
2146 pTargetData->b_nameValid =
true;
2147 pTargetData->MID = 123;
2148 pTargetData->COG = -1.;
2149 pTargetData->HDG = 511;
2150 pTargetData->SOG = -1.;
2151 pTargetData->b_NoTrack =
true;
2152 pTargetData->b_show_track =
false;
2154 pTargetData->b_OwnShip =
false;
2155 AISTargetList[pTargetData->MMSI] = pTargetData;
2159void AisDecoder::handleUpdate(
const std::shared_ptr<AisTargetData> &pTargetData,
2160 bool bnewtarget,
const rapidjson::Value &update) {
2161 wxString sfixtime =
"";
2163 if (update.HasMember(
"timestamp")) {
2164 sfixtime = update[
"timestamp"].GetString();
2166 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
2167 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
2168 itr != update[
"values"].End(); ++itr) {
2169 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
2172 wxDateTime now = wxDateTime::Now();
2173 pTargetData->m_utc_hour = now.ToUTC().GetHour();
2174 pTargetData->m_utc_min = now.ToUTC().GetMinute();
2175 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
2177 pTargetData->b_active =
true;
2178 pTargetData->b_lost =
false;
2180 if (pTargetData->b_positionOnceValid) {
2181 long mmsi_long = pTargetData->MMSI;
2183 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2184 (
void *)mmsi_long, SELTYPE_AISTARGET);
2185 pSel->SetUserData(pTargetData->MMSI);
2187 UpdateOneCPA(pTargetData.get());
2188 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2191void AisDecoder::updateItem(
const std::shared_ptr<AisTargetData> &pTargetData,
2192 bool bnewtarget,
const rapidjson::Value &item,
2193 wxString &sfixtime)
const {
2194 if (item.HasMember(
"path") && item.HasMember(
"value")) {
2195 const wxString &update_path = item[
"path"].GetString();
2196 if (update_path ==
"navigation.position") {
2197 if (item[
"value"].HasMember(
"latitude") &&
2198 item[
"value"].HasMember(
"longitude")) {
2199 wxDateTime now = wxDateTime::Now();
2201 double lat = item[
"value"][
"latitude"].GetDouble();
2202 double lon = item[
"value"][
"longitude"].GetDouble();
2203 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2204 pTargetData->PositionReportTicks = now.GetTicks();
2205 pTargetData->StaticReportTicks = now.GetTicks();
2206 pTargetData->Lat = lat;
2207 pTargetData->Lon = lon;
2208 pTargetData->b_positionOnceValid =
true;
2209 pTargetData->b_positionDoubtful =
false;
2216 }
else if (update_path ==
"navigation.speedOverGround" &&
2217 item[
"value"].IsNumber()) {
2218 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
2219 }
else if (update_path ==
"navigation.courseOverGroundTrue" &&
2220 item[
"value"].IsNumber()) {
2221 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2222 }
else if (update_path ==
"navigation.headingTrue" &&
2223 item[
"value"].IsNumber()) {
2224 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2225 }
else if (update_path ==
"navigation.rateOfTurn" &&
2226 item[
"value"].IsNumber()) {
2227 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
2228 }
else if (update_path ==
"design.aisShipType") {
2229 if (item[
"value"].HasMember(
"id")) {
2230 if (!pTargetData->b_isDSCtarget) {
2231 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2234 }
else if (update_path ==
"atonType") {
2235 if (item[
"value"].HasMember(
"id")) {
2236 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2238 }
else if (update_path ==
"virtual") {
2239 if (item[
"value"].GetBool()) {
2240 pTargetData->NavStatus = ATON_VIRTUAL;
2242 pTargetData->NavStatus = ATON_REAL;
2244 }
else if (update_path ==
"offPosition") {
2245 if (item[
"value"].GetBool()) {
2246 if (ATON_REAL == pTargetData->NavStatus) {
2247 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
2248 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
2249 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
2252 }
else if (update_path ==
"design.draft") {
2253 if (item[
"value"].HasMember(
"maximum") &&
2254 item[
"value"][
"maximum"].IsNumber()) {
2255 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
2256 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
2258 if (item[
"value"].HasMember(
"current") &&
2259 item[
"value"][
"current"].IsNumber()) {
2260 double draft = item[
"value"][
"current"].GetDouble();
2262 pTargetData->Draft = draft;
2263 pTargetData->Euro_Draft = draft;
2266 }
else if (update_path ==
"design.length") {
2267 if (pTargetData->DimB == 0) {
2268 if (item[
"value"].HasMember(
"overall")) {
2269 if (item[
"value"][
"overall"].IsNumber()) {
2270 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
2271 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
2273 pTargetData->DimB = 0;
2276 }
else if (update_path ==
"sensors.ais.class") {
2277 wxString aisclass = item[
"value"].GetString();
2278 if (aisclass ==
"A") {
2279 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
2280 }
else if (aisclass ==
"B") {
2281 if (!pTargetData->b_isDSCtarget) {
2282 if (!isBuoyMmsi(pTargetData->MMSI))
2283 pTargetData->Class = AIS_CLASS_B;
2285 pTargetData->Class = AIS_BUOY;
2288 pTargetData->NavStatus = UNDEFINED;
2290 }
else if (aisclass ==
"BASE") {
2291 pTargetData->Class = AIS_BASE;
2292 }
else if (aisclass ==
"ATON") {
2293 pTargetData->Class = AIS_ATON;
2295 }
else if (update_path ==
"sensors.ais.fromBow") {
2296 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
2297 int length = pTargetData->DimA;
2298 if (item[
"value"].IsNumber()) {
2299 pTargetData->DimA = item[
"value"].GetDouble();
2300 pTargetData->DimB = length - item[
"value"].GetDouble();
2303 }
else if (update_path ==
"design.beam") {
2304 if (pTargetData->DimD == 0) {
2305 if (item[
"value"].IsNumber()) {
2306 pTargetData->Euro_Beam = item[
"value"].GetDouble();
2307 pTargetData->DimC = item[
"value"].GetDouble();
2309 pTargetData->DimD = 0;
2311 }
else if (update_path ==
"sensors.ais.fromCenter") {
2312 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
2313 int beam = pTargetData->DimC;
2314 int center = beam / 2;
2315 if (item[
"value"].IsNumber()) {
2318 pTargetData->DimC = center + item[
"value"].GetDouble();
2319 pTargetData->DimD = beam - pTargetData->DimC;
2322 }
else if (update_path ==
"navigation.state") {
2323 wxString state = item[
"value"].GetString();
2324 if (state ==
"motoring") {
2325 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
2326 }
else if (state ==
"anchored") {
2327 pTargetData->NavStatus = AT_ANCHOR;
2328 }
else if (state ==
"not under command") {
2329 pTargetData->NavStatus = NOT_UNDER_COMMAND;
2330 }
else if (state ==
"restricted manouverability") {
2331 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
2332 }
else if (state ==
"constrained by draft") {
2333 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
2334 }
else if (state ==
"moored") {
2335 pTargetData->NavStatus = MOORED;
2336 }
else if (state ==
"aground") {
2337 pTargetData->NavStatus = AGROUND;
2338 }
else if (state ==
"fishing") {
2339 pTargetData->NavStatus = FISHING;
2340 }
else if (state ==
"sailing") {
2341 pTargetData->NavStatus = UNDERWAY_SAILING;
2342 }
else if (state ==
"hazardous material high speed") {
2343 pTargetData->NavStatus = HSC;
2344 }
else if (state ==
"hazardous material wing in ground") {
2345 pTargetData->NavStatus = WIG;
2346 }
else if (state ==
"ais-sart") {
2347 pTargetData->NavStatus = RESERVED_14;
2349 pTargetData->NavStatus = UNDEFINED;
2351 }
else if (update_path ==
"navigation.destination.commonName") {
2352 const wxString &destination = item[
"value"].GetString();
2353 pTargetData->Destination[0] =
'\0';
2354 strncpy(pTargetData->Destination, destination.c_str(),
2355 DESTINATION_LEN - 1);
2356 }
else if (update_path ==
"navigation.destination.eta") {
2357 const wxString &eta = item[
"value"].GetString();
2362 pTargetData->ETA_Mo = tz.GetMonth() + 1;
2363 pTargetData->ETA_Day = tz.GetDay();
2364 pTargetData->ETA_Hr = tz.GetHour();
2365 pTargetData->ETA_Min = tz.GetMinute();
2367 }
else if (update_path ==
"navigation.specialManeuver") {
2368 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
2369 pTargetData->IMO < 1) {
2370 const wxString &bluesign = item[
"value"].GetString();
2371 if (
"not engaged" == bluesign) {
2372 pTargetData->blue_paddle = 1;
2374 if (
"engaged" == bluesign) {
2375 pTargetData->blue_paddle = 2;
2377 pTargetData->b_blue_paddle =
2378 pTargetData->blue_paddle == 2 ? true :
false;
2380 }
else if (update_path ==
"sensors.ais.designatedAreaCode") {
2381 if (item[
"value"].GetInt() == 200) {
2382 pTargetData->b_hasInlandDac =
true;
2384 }
else if (update_path ==
"sensors.ais.functionalId") {
2385 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
2387 pTargetData->b_isEuroInland =
true;
2391 }
else if (update_path ==
"environment.date") {
2392 const wxString &issued = item[
"value"].GetString();
2397 pTargetData->met_data.day = tz.GetDay();
2398 pTargetData->met_data.hour = tz.GetHour();
2399 pTargetData->met_data.minute = tz.GetMinute();
2401 }
else if (update_path ==
"environment.wind.averageSpeed" &&
2402 item[
"value"].IsNumber()) {
2403 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
2404 }
else if (update_path ==
"environment.wind.gust" &&
2405 item[
"value"].IsNumber()) {
2406 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
2407 }
else if (update_path ==
"environment.wind.directionTrue" &&
2408 item[
"value"].IsNumber()) {
2409 pTargetData->met_data.wind_dir =
2410 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2411 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
2412 item[
"value"].IsNumber()) {
2413 pTargetData->met_data.wind_gust_dir =
2414 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2415 }
else if (update_path ==
"environment.outside.temperature" &&
2416 item[
"value"].IsNumber()) {
2417 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
2418 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
2419 item[
"value"].IsNumber()) {
2420 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
2421 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
2422 item[
"value"].IsNumber()) {
2423 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
2424 }
else if (update_path ==
"environment.outside.pressure" &&
2425 item[
"value"].IsNumber()) {
2426 pTargetData->met_data.airpress =
2427 static_cast<int>(item[
"value"].GetDouble() / 100);
2428 }
else if (update_path ==
"environment.water.level" &&
2429 item[
"value"].IsNumber()) {
2430 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
2431 }
else if (update_path ==
"environment.water.current.drift" &&
2432 item[
"value"].IsNumber()) {
2433 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
2434 }
else if (update_path ==
"environment.water.current.set" &&
2435 item[
"value"].IsNumber()) {
2436 pTargetData->met_data.curr_dir =
2437 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2438 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
2439 item[
"value"].IsNumber()) {
2440 pTargetData->met_data.water_lev_trend =
2441 static_cast<int>(item[
"value"].GetDouble());
2442 }
else if (update_path ==
"environment.water.levelTendency") {
2444 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
2445 item[
"value"].IsNumber()) {
2446 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
2447 }
else if (update_path ==
"environment.water.waves.period" &&
2448 item[
"value"].IsNumber()) {
2449 pTargetData->met_data.wave_period =
2450 static_cast<int>(item[
"value"].GetDouble());
2451 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
2452 item[
"value"].IsNumber()) {
2453 pTargetData->met_data.wave_dir =
2454 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2455 }
else if (update_path ==
"environment.water.swell.height" &&
2456 item[
"value"].IsNumber()) {
2457 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
2458 }
else if (update_path ==
"environment.water.swell.period" &&
2459 item[
"value"].IsNumber()) {
2460 pTargetData->met_data.swell_per =
2461 static_cast<int>(item[
"value"].GetDouble());
2462 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
2463 item[
"value"].IsNumber()) {
2464 pTargetData->met_data.swell_dir =
2465 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2466 }
else if (update_path ==
"environment.water.temperature" &&
2467 item[
"value"].IsNumber()) {
2468 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
2469 }
else if (update_path ==
"environment.water.salinity" &&
2470 item[
"value"].IsNumber()) {
2471 pTargetData->met_data.salinity = item[
"value"].GetDouble();
2472 }
else if (update_path ==
"environment.water.ice") {
2474 }
else if (update_path ==
"environment.water.iceValue" &&
2475 item[
"value"].IsNumber()) {
2476 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
2477 }
else if (update_path ==
"environment.water.seaStateValue" &&
2478 item[
"value"].IsNumber()) {
2479 pTargetData->met_data.seastate =
2480 static_cast<int>(item[
"value"].GetDouble());
2481 }
else if (update_path ==
"environment.water.seaState") {
2483 }
else if (update_path ==
"environment.outside.precipitation") {
2485 }
else if (update_path ==
"environment.outside.precipitationValue" &&
2486 item[
"value"].IsNumber()) {
2487 pTargetData->met_data.precipitation =
2488 static_cast<int>(item[
"value"].GetDouble());
2489 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
2490 item[
"value"].IsNumber()) {
2491 pTargetData->met_data.airpress_tend =
2492 static_cast<int>(item[
"value"].GetDouble());
2493 }
else if (update_path ==
"environment.outside.pressureTendency") {
2495 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
2496 item[
"value"].IsNumber()) {
2497 pTargetData->met_data.hor_vis =
2498 GEODESIC_METERS2NM(item[
"value"].GetDouble());
2499 }
else if (update_path ==
2500 "environment.outside.horizontalVisibility.overRange") {
2501 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
2502 }
else if (update_path.empty()) {
2503 if (item[
"value"].HasMember(
"name")) {
2504 const wxString &name = item[
"value"][
"name"].GetString();
2505 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
2506 pTargetData->b_nameValid =
true;
2507 pTargetData->MID = 123;
2508 }
else if (item[
"value"].HasMember(
"registrations")) {
2509 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
2510 pTargetData->IMO = wxAtoi(imo.Right(7));
2511 }
else if (item[
"value"].HasMember(
"communication")) {
2512 const wxString &callsign =
2513 item[
"value"][
"communication"][
"callsignVhf"].GetString();
2514 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
2516 if (item[
"value"].HasMember(
"mmsi") &&
2517 1994 != (pTargetData->MMSI) / 100000) {
2519 wxString tmp = item[
"value"][
"mmsi"].GetString();
2520 if (tmp.ToLong(&mmsi)) {
2521 pTargetData->MMSI = mmsi;
2523 if (97 == mmsi / 10000000) {
2524 pTargetData->Class = AIS_SART;
2526 if (111 == mmsi / 1000000) {
2527 pTargetData->b_SarAircraftPosnReport =
true;
2530 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2535 wxLogMessage(wxString::Format(
2536 "** AisDecoder::updateItem: unhandled path %s", update_path));
2538 rapidjson::StringBuffer buffer;
2539 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2540 item.Accept(writer);
2541 wxString msg(
"update: ");
2542 msg.append(buffer.GetString());
2552AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
2553 wxString *accumulator) {
2555 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2557 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
2559 if (!pos)
return AIS_GENERIC_ERROR;
2561 if (!accumulator)
return AIS_GENERIC_ERROR;
2564 if (!str.Mid(1, 5).IsSameAs(
"AIVDO"))
return AIS_GENERIC_ERROR;
2567 wxStringTokenizer tkz(str,
",");
2570 token = tkz.GetNextToken();
2572 token = tkz.GetNextToken();
2573 int nsentences = atoi(token.mb_str());
2575 token = tkz.GetNextToken();
2576 int isentence = atoi(token.mb_str());
2578 token = tkz.GetNextToken();
2579 token = tkz.GetNextToken();
2581 wxString string_to_parse;
2582 string_to_parse.Clear();
2595 if ((1 == nsentences) && (1 == isentence)) {
2596 string_to_parse = tkz.GetNextToken();
2599 else if (nsentences > 1) {
2600 if (1 == isentence) {
2601 *accumulator = tkz.GetNextToken();
2605 accumulator->Append(tkz.GetNextToken());
2608 if (isentence == nsentences) {
2609 string_to_parse = *accumulator;
2613 if (string_to_parse.IsEmpty() &&
2615 return AIS_INCOMPLETE_MULTIPART;
2624 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2626 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
2628 if (bdecode_result) {
2629 switch (TargetData->MID) {
2634 if (!TargetData->b_positionDoubtful) {
2635 pos->
kLat = TargetData->Lat;
2636 pos->
kLon = TargetData->Lon;
2642 if (TargetData->COG == 360.0)
2645 pos->
kCog = TargetData->COG;
2647 if (TargetData->SOG > 102.2)
2650 pos->
kSog = TargetData->SOG;
2652 if ((
int)TargetData->HDG == 511)
2655 pos->
kHdt = TargetData->HDG;
2663 return AIS_GENERIC_ERROR;
2668 return AIS_GENERIC_ERROR;
2676AisError AisDecoder::DecodeN0183(
const wxString &str) {
2677 AisError ret = AIS_GENERIC_ERROR;
2678 wxString string_to_parse;
2680 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
2681 double gpsg_cog, gpsg_sog, gpsg_utc_time;
2682 int gpsg_utc_hour = 0;
2683 int gpsg_utc_min = 0;
2684 int gpsg_utc_sec = 0;
2685 char gpsg_name_str[21];
2688 bool bdecode_result =
false;
2695 long arpa_tgt_num = 0;
2696 double arpa_sog = 0.;
2697 double arpa_cog = 0.;
2698 double arpa_lat = 0.;
2699 double arpa_lon = 0.;
2700 double arpa_dist = 0.;
2701 double arpa_brg = 0.;
2702 wxString arpa_brgunit;
2703 wxString arpa_status;
2704 wxString arpa_distunit;
2705 wxString arpa_cogunit;
2706 double arpa_mins, arpa_degs;
2707 double arpa_utc_time;
2708 int arpa_utc_hour = 0;
2709 int arpa_utc_min = 0;
2710 int arpa_utc_sec = 0;
2711 char arpa_name_str[21];
2712 bool arpa_lost =
true;
2713 bool arpa_nottracked =
false;
2715 double aprs_lat = 0.;
2716 double aprs_lon = 0.;
2717 char aprs_name_str[21];
2718 double aprs_mins, aprs_degs;
2720 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2721 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2722 bool bnewtarget =
false;
2723 int last_report_ticks;
2727 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2729 if (!NMEACheckSumOK(str)) {
2730 return AIS_NMEAVDX_CHECKSUM_BAD;
2732 if (str.Mid(1, 2).IsSameAs(
"CD")) {
2735 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
2739 wxStringTokenizer tkz(str,
",*");
2742 token = tkz.GetNextToken();
2743 token = tkz.GetNextToken();
2744 token.ToLong(&arpa_tgt_num);
2745 token = tkz.GetNextToken();
2746 token.ToDouble(&arpa_dist);
2747 token = tkz.GetNextToken();
2748 token.ToDouble(&arpa_brg);
2749 arpa_brgunit = tkz.GetNextToken();
2750 if (arpa_brgunit ==
"R") {
2751 if (std::isnan(arpa_ref_hdg)) {
2752 if (!std::isnan(
gHdt))
2757 arpa_brg += arpa_ref_hdg;
2758 if (arpa_brg >= 360.) arpa_brg -= 360.;
2760 token = tkz.GetNextToken();
2761 token.ToDouble(&arpa_sog);
2762 token = tkz.GetNextToken();
2763 token.ToDouble(&arpa_cog);
2764 arpa_cogunit = tkz.GetNextToken();
2765 if (arpa_cogunit ==
"R") {
2766 if (std::isnan(arpa_ref_hdg)) {
2767 if (!std::isnan(
gHdt))
2772 arpa_cog += arpa_ref_hdg;
2773 if (arpa_cog >= 360.) arpa_cog -= 360.;
2775 token = tkz.GetNextToken();
2776 token = tkz.GetNextToken();
2778 arpa_distunit = tkz.GetNextToken();
2779 token = tkz.GetNextToken();
2780 if (token ==
"") token = wxString::Format(
"ARPA %ld", arpa_tgt_num);
2781 int len = wxMin(token.Length(), 20);
2782 strncpy(arpa_name_str, token.mb_str(), len);
2783 arpa_name_str[len] = 0;
2784 arpa_status = tkz.GetNextToken();
2785 if (arpa_status !=
"L") {
2787 }
else if (arpa_status !=
"")
2788 arpa_nottracked =
true;
2789 wxString arpa_reftarget = tkz.GetNextToken();
2790 if (tkz.HasMoreTokens()) {
2791 token = tkz.GetNextToken();
2792 token.ToDouble(&arpa_utc_time);
2793 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2794 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2796 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2798 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
2799 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
2800 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
2803 if (arpa_distunit ==
"K") {
2806 }
else if (arpa_distunit ==
"S") {
2811 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2815 }
else if (str.Mid(3, 3).IsSameAs(
"TLL")) {
2818 wxStringTokenizer tkz(str,
",*");
2821 wxString aprs_tll_str = tkz.GetNextToken();
2822 token = tkz.GetNextToken();
2823 token.ToLong(&arpa_tgt_num);
2824 token = tkz.GetNextToken();
2825 token.ToDouble(&arpa_lat);
2826 arpa_degs = (int)(arpa_lat / 100.0);
2827 arpa_mins = arpa_lat - arpa_degs * 100.0;
2828 arpa_lat = arpa_degs + arpa_mins / 60.0;
2829 token = tkz.GetNextToken();
2830 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2831 token.Mid(0, 1).Contains(
"s") ==
true)
2832 arpa_lat = 0. - arpa_lat;
2833 token = tkz.GetNextToken();
2834 token.ToDouble(&arpa_lon);
2835 arpa_degs = (int)(arpa_lon / 100.0);
2836 arpa_mins = arpa_lon - arpa_degs * 100.0;
2837 arpa_lon = arpa_degs + arpa_mins / 60.0;
2838 token = tkz.GetNextToken();
2839 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2840 token.Mid(0, 1).Contains(
"w") ==
true)
2841 arpa_lon = 0. - arpa_lon;
2842 token = tkz.GetNextToken();
2843 if (token ==
"") token = wxString::Format(
"ARPA %d", arpa_tgt_num);
2844 int len = wxMin(token.Length(), 20);
2845 strncpy(arpa_name_str, token.mb_str(), len);
2846 arpa_name_str[len] = 0;
2847 token = tkz.GetNextToken();
2848 token.ToDouble(&arpa_utc_time);
2849 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2850 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2852 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2857 if (arpa_status !=
"L")
2859 else if (arpa_status !=
"")
2860 arpa_nottracked =
true;
2861 wxString arpa_reftarget = tkz.GetNextToken();
2862 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2866 }
else if (str.Mid(3, 3).IsSameAs(
"OSD")) {
2868 wxStringTokenizer tkz(str,
",*");
2871 token = tkz.GetNextToken();
2872 token = tkz.GetNextToken();
2873 token.ToDouble(&arpa_ref_hdg);
2883 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(
"WPL")) {
2885 wxStringTokenizer tkz(str,
",*");
2888 token = tkz.GetNextToken();
2889 token = tkz.GetNextToken();
2890 token.ToDouble(&aprs_lat);
2891 aprs_degs = (int)(aprs_lat / 100.0);
2892 aprs_mins = aprs_lat - aprs_degs * 100.0;
2893 aprs_lat = aprs_degs + aprs_mins / 60.0;
2894 token = tkz.GetNextToken();
2895 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2896 token.Mid(0, 1).Contains(
"s") ==
true)
2897 aprs_lat = 0. - aprs_lat;
2898 token = tkz.GetNextToken();
2899 token.ToDouble(&aprs_lon);
2900 aprs_degs = (int)(aprs_lon / 100.0);
2901 aprs_mins = aprs_lon - aprs_degs * 100.0;
2902 aprs_lon = aprs_degs + aprs_mins / 60.0;
2903 token = tkz.GetNextToken();
2904 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2905 token.Mid(0, 1).Contains(
"w") ==
true)
2906 aprs_lon = 0. - aprs_lon;
2907 token = tkz.GetNextToken();
2908 int len = wxMin(token.Length(), 20);
2909 strncpy(aprs_name_str, token.mb_str(), len + 1);
2910 if (0 == g_WplAction) {
2912 aprs_name_str[len] = 0;
2913 for (i = 0; i < len; i++) {
2915 hash += (int)(aprs_name_str[i]);
2916 while (hash >= 100000) hash = hash / 100000;
2918 mmsi = aprs_mmsi = 199300000 + hash;
2922 }
else if (1 == g_WplAction) {
2924 new RoutePoint(aprs_lat, aprs_lon,
"", aprs_name_str,
"",
false);
2925 pWP->m_bIsolatedMark =
true;
2926 InsertWpt(pWP,
true);
2928 }
else if (str.Mid(1, 5).IsSameAs(
"FRPOS")) {
2932 wxStringTokenizer tkz(str,
",*");
2935 token = tkz.GetNextToken();
2937 token = tkz.GetNextToken();
2938 token.ToDouble(&gpsg_lat);
2939 gpsg_degs = (int)(gpsg_lat / 100.0);
2940 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
2941 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
2943 token = tkz.GetNextToken();
2944 if (token.Mid(0, 1).Contains(
"S") ==
true ||
2945 token.Mid(0, 1).Contains(
"s") ==
true)
2946 gpsg_lat = 0. - gpsg_lat;
2948 token = tkz.GetNextToken();
2949 token.ToDouble(&gpsg_lon);
2950 gpsg_degs = (int)(gpsg_lon / 100.0);
2951 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
2952 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
2954 token = tkz.GetNextToken();
2955 if (token.Mid(0, 1).Contains(
"W") ==
true ||
2956 token.Mid(0, 1).Contains(
"w") ==
true)
2957 gpsg_lon = 0. - gpsg_lon;
2959 token = tkz.GetNextToken();
2962 token = tkz.GetNextToken();
2963 token.ToDouble(&gpsg_sog);
2965 token = tkz.GetNextToken();
2966 token.ToDouble(&gpsg_cog);
2968 token = tkz.GetNextToken();
2971 token = tkz.GetNextToken();
2972 token.ToDouble(&gpsg_utc_time);
2973 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
2974 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
2976 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
2980 token = tkz.GetNextToken();
2981 int i, len, hash = 0;
2982 len = wxMin(wxStrlen(token), 20);
2983 strncpy(gpsg_name_str, token.mb_str(), len);
2984 gpsg_name_str[len] = 0;
2985 for (i = 0; i < len; i++) {
2987 hash += (int)(token[i]);
2988 while (hash >= 100000) hash = hash / 100000;
2991 gpsg_mmsi = 199000000 + hash;
2993 }
else if (!str.Mid(3, 2).IsSameAs(
"VD")) {
2994 return AIS_NMEAVDX_BAD;
3000 string_to_parse.Clear();
3002 if (str.Mid(3, 2).IsSameAs(
"VD")) {
3003 wxStringTokenizer tkz(str,
",");
3006 token = tkz.GetNextToken();
3008 token = tkz.GetNextToken();
3009 nsentences = atoi(token.mb_str());
3011 token = tkz.GetNextToken();
3012 isentence = atoi(token.mb_str());
3014 token = tkz.GetNextToken();
3015 long lsequence_id = 0;
3016 token.ToLong(&lsequence_id);
3018 token = tkz.GetNextToken();
3020 token.ToLong(&lchannel);
3025 if ((1 == nsentences) && (1 == isentence)) {
3026 string_to_parse = tkz.GetNextToken();
3029 else if (nsentences > 1) {
3030 if (1 == isentence) {
3031 sentence_accumulator = tkz.GetNextToken();
3035 sentence_accumulator += tkz.GetNextToken();
3038 if (isentence == nsentences) {
3039 string_to_parse = sentence_accumulator;
3044 if (mmsi || (!string_to_parse.IsEmpty() &&
3045 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
3047 wxCharBuffer abuf = string_to_parse.ToUTF8();
3049 return AIS_GENERIC_ERROR;
3054 if (!mmsi) mmsi = strbit.GetInt(9, 30);
3055 long mmsi_long = mmsi;
3058 int origin_mmsi = 0;
3059 int messID = strbit.GetInt(1, 6);
3061 if (messID < 1 || messID > 63)
return AIS_GENERIC_ERROR;
3063 int dac = strbit.GetInt(41, 10);
3064 int fi = strbit.GetInt(51, 6);
3066 int met_lon, met_lat;
3067 if (dac == 001 && fi == 31) {
3069 met_lon = strbit.GetInt(57, 25);
3070 met_lat = strbit.GetInt(82, 24);
3071 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
3074 }
else if (dac == 367 && fi == 33) {
3076 const int size = strbit.GetBitCount();
3077 if (size < 168)
return AIS_GENERIC_ERROR;
3078 const int startb = 56;
3079 const int slot_size = 112;
3080 const int extra_bits = (size - startb) % slot_size;
3081 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
3083 int mes_type = strbit.GetInt(57, 4);
3084 int site_ID = strbit.GetInt(77, 7);
3085 if (mes_type == 0) {
3087 met_lon = strbit.GetInt(90, 28);
3088 met_lat = strbit.GetInt(118, 27);
3089 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
3094 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
3100 return AIS_GENERIC_ERROR;
3109 auto it = AISTargetList.find(mmsi);
3110 if (it == AISTargetList.end())
3112 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3117 pTargetData->MMSI = mmsi;
3118 pTargetData->met_data.original_mmsi = origin_mmsi;
3121 pTargetData = it->second;
3124 pStaleTarget = pTargetData;
3126 pTargetData->MMSI = mmsi;
3127 pTargetData->met_data.original_mmsi = origin_mmsi;
3132 if (mmsi ==
static_cast<unsigned>(props->MMSI)) {
3134 if (TRACKTYPE_NEVER == props->TrackType) {
3135 pTargetData->b_show_track =
false;
3136 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
3137 pTargetData->b_show_track =
true;
3142 if (props->m_bignore)
return AIS_NoError;
3145 else if (props->m_bVDM) {
3147 if (str.Mid(3, 9).IsSameAs(
"VDM,1,1,,")) {
3148 int message_ID = strbit.GetInt(1, 6);
3151 if ((message_ID <= 3) || (message_ID == 18)) {
3153 pTargetData->b_OwnShip =
true;
3155 wxString aivdostr = str;
3156 aivdostr.replace(1, 5,
"AIVDO");
3157 unsigned char calculated_checksum = 0;
3158 wxString::iterator j;
3159 for (j = aivdostr.begin() + 1; j != aivdostr.end() && *j !=
'*';
3161 calculated_checksum ^=
static_cast<unsigned char>(*j);
3164 if (j <= aivdostr.end() - 3)
3167 wxString::Format(_(
"%02X"), calculated_checksum));
3169 gps_watchdog_timeout_ticks =
3172 std::string full_sentence = aivdostr.ToStdString();
3173 auto address = std::make_shared<NavAddr0183>(
"virtual");
3176 auto msg = std::make_shared<const Nmea0183Msg>(
3177 "AIVDO", full_sentence, address);
3178 NavMsgBus::GetInstance().
Notify(std::move(msg));
3188 wxDateTime now = wxDateTime::Now();
3192 last_report_ticks = pStaleTarget->PositionReportTicks;
3194 last_report_ticks = now.GetTicks();
3198 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3202 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3203 pTargetData->PositionReportTicks = now.GetTicks();
3204 pTargetData->StaticReportTicks = now.GetTicks();
3205 pTargetData->m_utc_hour = gpsg_utc_hour;
3206 pTargetData->m_utc_min = gpsg_utc_min;
3207 pTargetData->m_utc_sec = gpsg_utc_sec;
3208 pTargetData->m_date_string = gpsg_date;
3209 pTargetData->MMSI = gpsg_mmsi;
3210 pTargetData->NavStatus = 0;
3211 pTargetData->Lat = gpsg_lat;
3212 pTargetData->Lon = gpsg_lon;
3213 pTargetData->b_positionOnceValid =
true;
3214 pTargetData->COG = gpsg_cog;
3215 pTargetData->SOG = gpsg_sog;
3216 pTargetData->ShipType = 52;
3217 pTargetData->Class = AIS_GPSG_BUDDY;
3218 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
3219 pTargetData->b_nameValid =
true;
3220 pTargetData->b_active =
true;
3221 pTargetData->b_lost =
false;
3223 bdecode_result =
true;
3224 }
else if (arpa_mmsi) {
3225 pTargetData->m_utc_hour = arpa_utc_hour;
3226 pTargetData->m_utc_min = arpa_utc_min;
3227 pTargetData->m_utc_sec = arpa_utc_sec;
3228 pTargetData->MMSI = arpa_mmsi;
3229 pTargetData->NavStatus = 15;
3230 if (str.Mid(3, 3).IsSameAs(
"TLL")) {
3233 (now.GetTicks() - pTargetData->PositionReportTicks);
3234 if (age_of_last > 0) {
3235 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
3236 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
3237 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3240 pTargetData->Lat = arpa_lat;
3241 pTargetData->Lon = arpa_lon;
3242 }
else if (str.Mid(3, 3).IsSameAs(
"TTM")) {
3243 if (arpa_dist != 0.)
3244 ll_gc_ll(
gLat,
gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
3248 pTargetData->COG = arpa_cog;
3249 pTargetData->SOG = arpa_sog;
3251 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3252 pTargetData->PositionReportTicks = now.GetTicks();
3253 pTargetData->StaticReportTicks = now.GetTicks();
3254 pTargetData->b_positionOnceValid =
true;
3255 pTargetData->ShipType = 55;
3256 pTargetData->Class = AIS_ARPA;
3258 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
3259 if (arpa_status !=
"Q")
3260 pTargetData->b_nameValid =
true;
3262 pTargetData->b_nameValid =
false;
3263 pTargetData->b_active = !arpa_lost;
3264 pTargetData->b_lost = arpa_nottracked;
3266 bdecode_result =
true;
3267 }
else if (aprs_mmsi) {
3268 pTargetData->m_utc_hour = now.GetHour();
3269 pTargetData->m_utc_min = now.GetMinute();
3270 pTargetData->m_utc_sec = now.GetSecond();
3271 pTargetData->MMSI = aprs_mmsi;
3272 pTargetData->NavStatus = 15;
3274 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
3275 if (age_of_last > 0) {
3276 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
3277 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
3278 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3281 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3282 pTargetData->PositionReportTicks = now.GetTicks();
3283 pTargetData->StaticReportTicks = now.GetTicks();
3284 pTargetData->Lat = aprs_lat;
3285 pTargetData->Lon = aprs_lon;
3286 pTargetData->b_positionOnceValid =
true;
3287 pTargetData->ShipType = 56;
3288 pTargetData->Class = AIS_APRS;
3289 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
3290 pTargetData->b_nameValid =
true;
3291 pTargetData->b_active =
true;
3292 pTargetData->b_lost =
false;
3294 bdecode_result =
true;
3298 Parse_VDXBitstring(&strbit, pTargetData);
3302 getMmsiProperties(pTargetData);
3305 pTargetData->RecentPeriod =
3306 pTargetData->PositionReportTicks - last_report_ticks;
3311 pTargetData =
nullptr;
3316 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
3321 if ((n_msgs % 10000) == 0)
3322 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
3323 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
3329void AisDecoder::CommitAISTarget(
3330 const std::shared_ptr<AisTargetData> &pTargetData,
const wxString &str,
3331 bool message_valid,
bool new_target) {
3332 m_pLatestTargetData = pTargetData;
3334 if (!str.IsEmpty()) {
3335 if (str.Mid(3, 3).IsSameAs(
"VDO"))
3336 pTargetData->b_OwnShip =
true;
3338 pTargetData->b_OwnShip =
false;
3341 if (!pTargetData->b_OwnShip) {
3343 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
3345 pTargetData->b_PersistTrack =
false;
3350 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
3357 pTargetData->b_NoTrack =
false;
3362 if (message_valid) {
3364 if (pTargetData->MMSI) {
3365 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
3368 AISTargetList[pTargetData->MMSI] =
3371 if (!pTargetData->area_notices.empty()) {
3372 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
3373 if (it == AIS_AreaNotice_Sources.end())
3374 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
3379 if (!pTargetData->b_OwnShip) {
3380 if (pTargetData->b_positionOnceValid) {
3381 long mmsi_long = pTargetData->MMSI;
3383 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3385 pSel->SetUserData(pTargetData->MMSI);
3389 UpdateOneCPA(pTargetData.get());
3392 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3395 plugin_msg_evt.Notify(std::make_shared<AisTargetData>(*pTargetData),
"");
3405 if (!pTargetData->b_OwnShip) {
3406 if (pTargetData->b_positionOnceValid) {
3407 long mmsi_long = pTargetData->MMSI;
3409 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3411 pSel->SetUserData(pTargetData->MMSI);
3418void AisDecoder::getAISTarget(
long mmsi,
3419 std::shared_ptr<AisTargetData> &pTargetData,
3420 std::shared_ptr<AisTargetData> &pStaleTarget,
3421 bool &bnewtarget,
int &last_report_ticks,
3423 now = wxDateTime::Now();
3424 auto it = AISTargetList.find(mmsi);
3425 if (it == AISTargetList.end())
3427 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3431 pTargetData = it->second;
3432 pStaleTarget = pTargetData;
3439 last_report_ticks = pStaleTarget->PositionReportTicks;
3441 last_report_ticks = now.GetTicks();
3445 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
3448std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
3450 double dsc_lat = 0.;
3451 double dsc_lon = 0.;
3452 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
3454 double dse_lat = 0.;
3455 double dse_lon = 0.;
3456 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
3459 int dsc_tx_mmsi = 0;
3461 double dse_cog = 0.;
3462 double dse_sog = 0.;
3463 wxString dse_shipName =
"";
3468 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
3472 wxStringTokenizer tkz(str,
",*");
3475 token = tkz.GetNextToken();
3477 if (str.Mid(3, 3).IsSameAs(
"DSC")) {
3478 m_dsc_last_string = str;
3480 token = tkz.GetNextToken();
3484 token = tkz.GetNextToken();
3486 if (dsc_fmt == 12 || dsc_fmt == 16) {
3487 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3489 token.ToDouble(&dsc_addr);
3490 dsc_mmsi = 0 - (int)(dsc_addr / 10);
3493 token = tkz.GetNextToken();
3494 token.ToLong(&dsc_cat);
3496 token = tkz.GetNextToken();
3497 if (!token.IsSameAs(
"")) {
3498 token.ToLong(&dsc_nature);
3502 token = tkz.GetNextToken();
3504 token = tkz.GetNextToken();
3505 token.ToDouble(&dsc_tmp);
3507 token = tkz.GetNextToken();
3508 token = tkz.GetNextToken();
3509 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(
"")) {
3511 dsc_tx_mmsi = dsc_mmsi;
3512 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3514 token = tkz.GetNextToken();
3515 if (dsc_fmt == 16 && dsc_cat == 12) {
3516 if (!token.IsSameAs(
"")) {
3517 token.ToLong(&dsc_nature);
3521 token = tkz.GetNextToken();
3522 token = tkz.GetNextToken();
3524 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
3526 if (dsc_quadrant > 3)
3529 dsc_lat = (int)(dsc_tmp / 100000.0);
3530 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
3531 dsc_lat = dsc_lat - dsc_quadrant * 10000;
3532 dsc_degs = (int)(dsc_lat / 100.0);
3533 dsc_mins = dsc_lat - dsc_degs * 100.0;
3534 dsc_lat = dsc_degs + dsc_mins / 60.0;
3536 dsc_degs = (int)(dsc_lon / 100.0);
3537 dsc_mins = dsc_lon - dsc_degs * 100.0;
3538 dsc_lon = dsc_degs + dsc_mins / 60.0;
3539 switch (dsc_quadrant) {
3555 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
3557 }
else if (str.Mid(3, 3).IsSameAs(
"DSE")) {
3558 token = tkz.GetNextToken();
3559 token = tkz.GetNextToken();
3560 token = tkz.GetNextToken();
3561 token = tkz.GetNextToken();
3562 dse_mmsi = wxAtoi(token.Mid(
3568 token = tkz.GetNextToken();
3571 token.ToDouble(&dse_tmp);
3572 dse_lat = (int)(dse_tmp / 10000.0);
3573 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3574 dse_lat = dse_lat / 600000.0;
3575 dse_lon = dse_lon / 600000.0;
3578 while (tkz.HasMoreTokens()) {
3579 dseSymbol = tkz.GetNextToken();
3580 token = tkz.GetNextToken();
3581 if (dseSymbol.IsSameAs(
"00")) {
3582 token.ToDouble(&dse_tmp);
3583 dse_lat = (int)(dse_tmp / 10000.0);
3584 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3585 dse_lat = dse_lat / 600000.0;
3586 dse_lon = dse_lon / 600000.0;
3587 }
else if (dseSymbol.IsSameAs(
"01")) {
3588 }
else if (dseSymbol.IsSameAs(
"02")) {
3589 token.ToDouble(&dse_tmp);
3590 dse_sog = dse_tmp / 10.0;
3591 }
else if (dseSymbol.IsSameAs(
"03")) {
3592 token.ToDouble(&dse_tmp);
3593 dse_cog = dse_tmp / 10.0;
3594 }
else if (dseSymbol.IsSameAs(
"04")) {
3595 dse_shipName = DecodeDSEExpansionCharacters(token);
3596 }
else if (dseSymbol.IsSameAs(
"05")) {
3597 }
else if (dseSymbol.IsSameAs(
"06")) {
3600 mmsi = abs((
int)dse_mmsi);
3604 wxDateTime now = wxDateTime::Now();
3606 int last_report_ticks = now.GetTicks();
3609 auto it = AISTargetList.find(mmsi);
3610 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
3611 if (it == AISTargetList.end()) {
3613 pStaleTarget = it->second;
3614 last_report_ticks = pStaleTarget->PositionReportTicks;
3620 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
3622 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
3623 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
3625 m_ptentative_dsctarget->MMSI = mmsi;
3626 m_ptentative_dsctarget->NavStatus =
3628 m_ptentative_dsctarget->Lat = dsc_lat;
3629 m_ptentative_dsctarget->Lon = dsc_lon;
3630 m_ptentative_dsctarget->b_positionOnceValid =
true;
3631 m_ptentative_dsctarget->COG = 0;
3632 m_ptentative_dsctarget->SOG = 0;
3633 m_ptentative_dsctarget->ShipType = dsc_fmt;
3634 m_ptentative_dsctarget->Class = AIS_DSC;
3635 m_ptentative_dsctarget->b_isDSCtarget =
true;
3636 m_ptentative_dsctarget->b_nameValid =
true;
3637 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
3638 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %u",
3640 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
3641 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
3643 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %u",
3647 m_ptentative_dsctarget->b_active =
true;
3648 m_ptentative_dsctarget->b_lost =
false;
3649 m_ptentative_dsctarget->RecentPeriod =
3650 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3653 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
3658 if (dse_mmsi || b_take_dsc) {
3659 if (m_ptentative_dsctarget) {
3665 m_ptentative_dsctarget->Lat =
3666 m_ptentative_dsctarget->Lat +
3667 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
3668 m_ptentative_dsctarget->Lon =
3669 m_ptentative_dsctarget->Lon +
3670 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
3671 if (!dse_shipName.empty()) {
3672 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
3673 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
3674 "%s", dse_shipName.ToAscii().data());
3676 m_ptentative_dsctarget->COG = dse_cog;
3677 m_ptentative_dsctarget->SOG = dse_sog;
3681 m_ptentative_dsctarget->RecentPeriod =
3682 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3687 auto found = AISTargetList.find(mmsi);
3688 if (found == AISTargetList.end()) {
3689 pTargetData = m_ptentative_dsctarget;
3691 pTargetData = found->second;
3692 std::vector<AISTargetTrackPoint> ptrack =
3693 std::move(pTargetData->m_ptrack);
3694 pTargetData->CloneFrom(
3695 m_ptentative_dsctarget
3698 pTargetData->m_ptrack =
3705 m_ptentative_dsctarget =
nullptr;
3707 m_pLatestTargetData = pTargetData;
3709 AISTargetList[pTargetData->MMSI] =
3712 long mmsi_long = pTargetData->MMSI;
3716 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3719 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
3720 (
void *)mmsi_long, SELTYPE_AISTARGET);
3721 pSel->SetUserData(pTargetData->MMSI);
3724 UpdateOneCPA(pTargetData.get());
3727 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3734bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3735 unsigned char checksum_value = 0;
3736 int sentence_hex_sum;
3738 wxCharBuffer buf = str_in.ToUTF8();
3739 if (!buf.data())
return false;
3741 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3742 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3743 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3745 int string_length = strlen(str_ascii);
3747 int payload_length = 0;
3748 while ((payload_length < string_length) &&
3749 (str_ascii[payload_length] !=
'*'))
3752 if (payload_length == string_length)
3757 while (index < payload_length) {
3758 checksum_value ^= str_ascii[index];
3762 if (string_length > 4) {
3764 scanstr[0] = str_ascii[payload_length + 1];
3765 scanstr[1] = str_ascii[payload_length + 2];
3767 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3769 if (sentence_hex_sum == checksum_value)
return true;
3775void AisDecoder::UpdateAllCPA() {
3777 for (
const auto &it : GetTargetList()) {
3778 std::shared_ptr<AisTargetData> td = it.second;
3780 if (
nullptr != td) UpdateOneCPA(td.get());
3784void AisDecoder::UpdateAllTracks() {
3785 for (
const auto &it : GetTargetList()) {
3786 std::shared_ptr<AisTargetData> td = it.second;
3787 if (td) UpdateOneTrack(td.get());
3793 if (!ptarget->b_positionOnceValid)
return;
3795 if (!ptarget->m_ptrack.empty()) {
3797 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3798 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3801 ptarget->m_ptrack.pop_back();
3802 ptarget->b_positionDoubtful =
true;
3809 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3812 ptrackpoint.m_lat = ptarget->Lat;
3813 ptrackpoint.m_lon = ptarget->Lon;
3814 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3816 ptarget->m_ptrack.push_back(ptrackpoint);
3818 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3820 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3822 t->SetName(wxString::Format(
3823 "AIS %s (%u) %s %s", ptarget->GetFullName().c_str(), ptarget->MMSI,
3824 wxDateTime::Now().FormatISODate().c_str(),
3825 wxDateTime::Now().FormatISOTime().c_str()));
3828 m_persistent_tracks[ptarget->MMSI] = t;
3830 t = m_persistent_tracks[ptarget->MMSI];
3833 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3835 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3838 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3839 tp1->m_lon, tp, tp1, t);
3849 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3851 ptarget->m_ptrack.erase(
3852 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3854 return track.m_time < test_time;
3856 ptarget->m_ptrack.end());
3861void AisDecoder::DeletePersistentTrack(
const Track *track) {
3862 for (
auto it = m_persistent_tracks.begin(); it != m_persistent_tracks.end();
3864 if (it->second == track) {
3865 unsigned mmsi = it->first;
3866 m_persistent_tracks.erase(it);
3868 if (0 == m_persistent_tracks.count(mmsi)) {
3872 if (props->m_bPersistentTrack) {
3876 std::shared_ptr<AisTargetData> td =
3877 Get_Target_Data_From_MMSI(mmsi);
3879 props->m_bPersistentTrack =
false;
3880 td->b_mPropPersistTrack =
false;
3882 if (!m_callbacks.confirm_stop_track()) {
3883 props->m_bPersistentTrack =
true;
3895void AisDecoder::UpdateAllAlarms() {
3896 m_bGeneralAlert =
false;
3899 for (
const auto &it : GetTargetList()) {
3900 std::shared_ptr<AisTargetData> td = it.second;
3904 if (!m_bGeneralAlert) {
3906 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3907 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3908 m_bGeneralAlert =
true;
3911 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3912 m_bGeneralAlert =
false;
3915 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3916 m_bGeneralAlert =
false;
3919 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3922 if (td->Class == AIS_SART && td->NavStatus == 14)
3923 m_bGeneralAlert =
true;
3926 if ((td->Class == AIS_DSC) &&
3927 ((td->ShipType == 12) || (td->ShipType == 16)))
3928 m_bGeneralAlert =
true;
3931 ais_alert_type this_alarm = AIS_NO_ALERT;
3934 if (td->Class == AIS_SART && td->NavStatus == 14)
3935 this_alarm = AIS_ALERT_SET;
3938 if ((td->Class == AIS_DSC) &&
3939 ((td->ShipType == 12) || (td->ShipType == 16)))
3940 this_alarm = AIS_ALERT_SET;
3942 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3943 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3946 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3947 td->n_alert_state = AIS_NO_ALERT;
3953 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3954 (td->SOG <= g_ShowMoored_Kts)) {
3955 td->n_alert_state = AIS_NO_ALERT;
3961 if (td->Range_NM > g_CPAMax_NM) {
3962 td->n_alert_state = AIS_NO_ALERT;
3967 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3968 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3969 (td->Class != AIS_METEO)) {
3971 if (td->TCPA < g_TCPA_Max) {
3972 if (td->b_isFollower)
3973 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3975 this_alarm = AIS_ALERT_SET;
3978 if (td->b_isFollower)
3979 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3981 this_alarm = AIS_ALERT_SET;
3988 if (td->Class == AIS_SART) {
3989 if (td->b_in_ack_timeout) {
3990 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3992 if (delta.GetMinutes() >= 10) td->b_in_ack_timeout =
false;
3994 }
else if (g_bAIS_ACK_Timeout ||
3995 ((td->Class == AIS_DSC) &&
3996 ((td->ShipType == 12) || (td->ShipType == 16)))) {
3997 if (td->b_in_ack_timeout) {
3998 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3999 if (delta.GetMinutes() > g_AckTimeout_Mins)
4000 td->b_in_ack_timeout =
false;
4006 if (td->b_in_ack_timeout) {
4007 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
4011 td->n_alert_state = this_alarm;
4017 ptarget->Range_NM = -1.;
4026 DistanceBearingMercator(ptarget->Lat, ptarget->Lon,
gLat,
gLon, &brg, &dist);
4027 ptarget->Range_NM = dist;
4030 if (dist <= 1e-5) ptarget->Brg = -1.0;
4032 if (!ptarget->b_positionOnceValid || !
bGPSValid) {
4033 ptarget->bCPA_Valid =
false;
4037 if (ptarget->Class == AIS_METEO) {
4038 ptarget->bCPA_Valid =
false;
4047 if (ptarget->b_OwnShip) {
4049 ptarget->TCPA = -100;
4050 ptarget->bCPA_Valid =
false;
4054 double cpa_calc_ownship_cog =
gCog;
4055 double cpa_calc_target_cog = ptarget->COG;
4058 if (std::isnan(
gSog) || (
gSog > 102.2)) {
4059 ptarget->bCPA_Valid =
false;
4064 if (std::isnan(
gCog) ||
gCog == 360.0) {
4066 cpa_calc_ownship_cog =
4070 ptarget->bCPA_Valid =
false;
4076 if (ptarget->COG == 360.0) {
4077 if (ptarget->SOG > 102.2) {
4078 ptarget->bCPA_Valid =
false;
4080 }
else if (ptarget->SOG < .01) {
4081 cpa_calc_target_cog = 0.;
4084 ptarget->bCPA_Valid =
false;
4090 double v0 =
gSog * 1852.;
4091 double v1 = ptarget->SOG * 1852.;
4093 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4097 ptarget->bCPA_Valid =
false;
4104 double east1 = (ptarget->Lon -
gLon) * 60 * 1852;
4105 double north1 = (ptarget->Lat -
gLat) * 60 * 1852;
4107 double east = east1 * (cos(
gLat * PI / 180.));
4109 double north = north1;
4112 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4113 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4114 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4115 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4118 double fc = (v0 * cosa) - (v1 * cosb);
4119 double fs = (v0 * sina) - (v1 * sinb);
4121 double d = (fc * fc) + (fs * fs);
4129 tcpa = ((fc * east) + (fs * north)) / d;
4132 ptarget->TCPA = tcpa * 60.;
4137 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4139 ll_gc_ll(
gLat,
gLon, cpa_calc_ownship_cog,
gSog * tcpa, &OwnshipLatCPA,
4141 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4142 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4145 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4148 ptarget->bCPA_Valid =
true;
4150 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4154void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4157 if (m_ptentative_dsctarget) {
4158 ProcessDSx(m_dsc_last_string,
true);
4162void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4167 wxDateTime now = wxDateTime::Now();
4170 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4173 auto it = current_targets.begin();
4174 std::vector<int> remove_array;
4176 while (it != current_targets.end()) {
4180 current_targets.erase(it);
4185 std::shared_ptr<AisTargetData> xtd = it->second;
4187 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4188 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4200 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4202 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4203 double iECD_LostTimeOut = 0.0;
4207 if (xtd->Class == AIS_CLASS_A) {
4208 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4210 iECD_LostTimeOut = 18 * 60;
4212 iECD_LostTimeOut = 60;
4214 iECD_LostTimeOut = 60;
4215 }
else if (xtd->Class == AIS_CLASS_B) {
4217 iECD_LostTimeOut = 18 * 60;
4219 iECD_LostTimeOut = 180;
4220 }
else if (xtd->Class == AIS_ATON || xtd->Class == AIS_BASE)
4221 iECD_LostTimeOut = 18 * 60;
4223 if ((target_posn_age > iECD_LostTimeOut) &&
4224 (xtd->Class != AIS_GPSG_BUDDY))
4225 xtd->b_active =
false;
4227 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4229 }
else if (g_bMarkLost) {
4230 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4231 (xtd->Class != AIS_GPSG_BUDDY) && (xtd->Class != AIS_SART))
4232 xtd->b_active =
false;
4235 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4236 removelost_Mins = 18.0;
4240 if (g_bRemoveLost || g_bInlandEcdis) {
4242 (xtd->Class == AIS_ARPA &&
4244 if (((target_posn_age > removelost_Mins * 60) &&
4245 (xtd->Class != AIS_GPSG_BUDDY)) ||
4250 xtd->b_positionOnceValid =
false;
4258 long mmsi_long = xtd->MMSI;
4259 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4264 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4265 xtd->b_removed =
true;
4267 remove_array.push_back(xtd->MMSI);
4276 if (xtd->MMSI == props->MMSI) {
4277 if (props->m_bignore) {
4278 remove_array.push_back(xtd->MMSI);
4279 xtd->b_removed =
true;
4288 remove_array.push_back(xtd->MMSI);
4289 xtd->b_removed =
true;
4297 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4298 auto itd = current_targets.find(remove_array[i]);
4299 if (itd != current_targets.end()) {
4300 std::shared_ptr<AisTargetData> td = itd->second;
4301 current_targets.erase(itd);
4310 m_bSuppressed =
false;
4311 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4312 (g_bShowScaled && g_bAllowShowScaled))
4313 m_bSuppressed =
true;
4315 m_bAIS_Audio_Alert_On =
false;
4324 std::shared_ptr<AisTargetData> palert_target =
nullptr;
4326 if (!g_pais_alert_dialog_active) {
4328 double tcpa_min = 1e6;
4329 double sart_range = 1e6;
4330 std::shared_ptr<AisTargetData> palert_target_cpa =
nullptr;
4331 std::shared_ptr<AisTargetData> palert_target_sart =
nullptr;
4332 std::shared_ptr<AisTargetData> palert_target_dsc =
nullptr;
4334 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4335 std::shared_ptr<AisTargetData> td = it->second;
4337 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4338 if (g_bAIS_CPA_Alert && td->b_active) {
4339 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4340 if (td->TCPA < tcpa_min) {
4341 tcpa_min = td->TCPA;
4342 palert_target_cpa = td;
4346 }
else if ((td->Class == AIS_DSC) &&
4347 ((td->ShipType == 12) || (td->ShipType == 16))) {
4349 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4350 palert_target_dsc = td;
4353 td->b_isDSCtarget =
false;
4358 else if (td->Class == AIS_SART) {
4360 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4361 if (td->Range_NM < sart_range) {
4362 tcpa_min = sart_range;
4363 palert_target_sart = td;
4373 palert_target = palert_target_cpa;
4375 if (palert_target_sart) {
4376 palert_target = palert_target_sart;
4379 if (palert_target_dsc) {
4380 palert_target = palert_target_dsc;
4384 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4387 if (palert_target)
info_update.Notify(palert_target,
"");
4389 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4392std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
4394 if (AISTargetList.find(mmsi) == AISTargetList.end())
return nullptr;
4395 return AISTargetList[mmsi];
4402MmsiProperties::MmsiProperties(wxString &spec) {
4404 wxStringTokenizer tkz(spec,
";");
4407 s = tkz.GetNextToken();
4412 s = tkz.GetNextToken();
4414 if (s.Upper() ==
"ALWAYS")
4415 TrackType = TRACKTYPE_ALWAYS;
4416 else if (s.Upper() ==
"NEVER")
4417 TrackType = TRACKTYPE_NEVER;
4420 s = tkz.GetNextToken();
4422 if (s.Upper() ==
"IGNORE") m_bignore =
true;
4425 s = tkz.GetNextToken();
4427 if (s.Upper() ==
"MOB") m_bMOB =
true;
4430 s = tkz.GetNextToken();
4432 if (s.Upper() ==
"VDM") m_bVDM =
true;
4435 s = tkz.GetNextToken();
4437 if (s.Upper() ==
"FOLLOWER") m_bFollower =
true;
4440 s = tkz.GetNextToken();
4442 if (s.Upper() ==
"PERSIST") m_bPersistentTrack =
true;
4445 s = tkz.GetNextToken();
4447 m_ShipName = s.Upper();
4451MmsiProperties::~MmsiProperties() =
default;
4453void MmsiProperties::Init() {
4455 TrackType = TRACKTYPE_DEFAULT;
4459 m_bFollower =
false;
4460 m_bPersistentTrack =
false;
4464wxString MmsiProperties::Serialize() {
4468 sMMSI.Printf(
"%d", MMSI);
4472 if (TRACKTYPE_ALWAYS == TrackType)
4474 else if (TRACKTYPE_NEVER == TrackType)
4495 sMMSI <<
"Follower";
4499 if (m_bPersistentTrack) {
4504 if (m_ShipName ==
"") {
4505 m_ShipName = GetShipNameFromFile(MMSI);
4507 sMMSI << m_ShipName;
4512 AIS_Target_Name_Hash *AISTargetNamesC,
4513 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4514 if (g_benableAISNameCache) {
4515 wxString ship_name =
"";
4518 if (!pTargetData->b_nameValid) {
4519 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4520 if (it != AISTargetNamesC->end()) {
4521 ship_name = (*AISTargetNamesC)[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;
4526 }
else if (!g_bUseOnlyConfirmedAISName) {
4527 it = AISTargetNamesNC->find(mmsi);
4528 if (it != AISTargetNamesNC->end()) {
4529 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4530 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4531 ship_name.length() + 1);
4532 pTargetData->b_nameValid =
true;
4533 pTargetData->b_nameFromCache =
true;
4538 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4539 (pTargetData->MID == 19) ||
4540 (pTargetData->MID == 123) ||
4541 (pTargetData->MID == 124)) {
4543 pTargetData->b_nameFromCache =
false;
4544 ship_name = trimAISField(pTargetData->ShipName);
4545 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4546 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4548 AISTargetNamesC->end()) {
4549 if ((*AISTargetNamesC)[mmsi] ==
4551 if (itNC != AISTargetNamesNC->end()) {
4553 AISTargetNamesNC->erase(itNC);
4556 if (itNC != AISTargetNamesNC->end()) {
4558 if ((*AISTargetNamesNC)[mmsi] ==
4561 (*AISTargetNamesC)[mmsi] = ship_name;
4563 AISTargetNamesNC->erase(itNC);
4566 (*AISTargetNamesNC)[mmsi] = ship_name;
4568 if (g_bUseOnlyConfirmedAISName)
4569 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4570 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4572 (*AISTargetNamesNC)[mmsi] = ship_name;
4577 AISTargetNamesNC->end()) {
4578 if ((*AISTargetNamesNC)[mmsi] ==
4581 (*AISTargetNamesC)[mmsi] = ship_name;
4583 AISTargetNamesNC->erase(itNC);
4585 (*AISTargetNamesNC)[mmsi] = ship_name;
4588 (*AISTargetNamesNC)[mmsi] = ship_name;
4590 if (g_bUseOnlyConfirmedAISName) {
4591 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4592 strncpy(pTargetData->ShipName,
"Unknown ",
4600wxString GetShipNameFromFile(
int nmmsi) {
4602 if (g_benableAISNameCache) {
4606 while (getline(infile, line)) {
4607 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4608 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4609 name = tokenizer.GetNextToken().Trim();
4612 tokenizer.GetNextToken();
4620void AisDecoder::UpdateMMSItoNameFile(
const wxString &mmsi,
4621 const wxString &name) {
4625 std::map<wxString, wxString> mmsi_name_map;
4631 while (getline(infile, line)) {
4632 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()),
",");
4633 wxString file_mmsi = tokenizer.GetNextToken();
4634 wxString file_name = tokenizer.GetNextToken().Trim();
4635 mmsi_name_map[file_mmsi] = file_name;
4641 mmsi_name_map[mmsi] = name.Upper();
4646 for (
const auto &pair : mmsi_name_map) {
4647 std::string line = std::string(pair.first.mb_str()) +
"," +
4648 std::string(pair.second.mb_str()) +
"\n";
4655wxString AisDecoder::GetMMSItoNameEntry(
const wxString &mmsi) {
4656 return GetShipNameFromFile(wxAtoi(mmsi));
4660int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4664 if ((!lon_bits || lon_bits == 999) && siteID) {
4667 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4668 if (points.size()) {
4669 for (
const auto &point : points) {
4671 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4673 new_mmsi = point.mmsi;
4679 if (!found && !lon_bits) {
4684 double lon_tentative = 181.;
4685 double lat_tentative = 91.;
4687 if (lon_bits == 25) {
4688 if (m_lon & 0x01000000)
4689 m_lon |= 0xFE000000;
4690 lon_tentative = m_lon / 60000.;
4692 if (m_lat & 0x00800000)
4693 m_lat |= 0xFF000000;
4694 lat_tentative = m_lat / 60000.;
4696 }
else if (lon_bits == 28) {
4697 if (m_lon & 0x08000000)
4698 m_lon |= 0xf0000000;
4699 lon_tentative = m_lon / 600000.;
4701 if (m_lat & 0x04000000)
4702 m_lat |= 0xf8000000;
4703 lat_tentative = m_lat / 600000.;
4708 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4709 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4717 static int nextMeteommsi = 199400000;
4718 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4720 if (lon_bits != 999 && !points.empty()) {
4721 wxString t_lat, t_lon;
4722 for (
const auto &point : points) {
4724 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4726 new_mmsi = point.mmsi;
4735 points.emplace_back(
4736 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4737 new_mmsi = nextMeteommsi;
Select * pSelectAIS
Global instance.
wxString AISTargetNameFileName
Global instance.
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
unsigned g_OwnShipmmsi
Global instance.
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
Select * pSelectAIS
Global instance.
wxString AISTargetNameFileName
Global instance.
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
unsigned g_OwnShipmmsi
Global instance.
Global state for AIS decoder.
int GetInt(int sp, int len, bool signed_flag=false)
sp is starting bit, 1-based
obs::EventVar info_update
Notified when AIS user dialogs should update.
obs::EventVar plugin_msg_evt
A JSON message should be sent.
obs::EventVar touch_state
Notified when gFrame->TouchAISActive() should be invoked.
obs::EventVar new_track
Notified on new track creation.
Add a new point to the list of Meteo stations.
Process incoming AIS messages.
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString &error_msg="") const
Logs an input message with context information.
void Notify(std::shared_ptr< const NavMsg > message)
Accept message received by driver, make it available for upper layers.
A regular Nmea0183 message.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
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.
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.