42#include <wx/datetime.h>
46#include <wx/textfile.h>
48#include <wx/tokenzr.h>
49#include <wx/filename.h>
53#include "rapidjson/document.h"
54#include "rapidjson/writer.h"
55#include "rapidjson/stringbuffer.h"
59#include "model/meteo_points.h"
60#include "model/ais_target_data.h"
62#include "model/config_vars.h"
63#include "model/geodesic.h"
64#include "model/georef.h"
65#include "model/idents.h"
67#include "model/navutil_base.h"
68#include "model/own_ship.h"
69#include "model/route_point.h"
70#include "model/select.h"
71#include "model/track.h"
75static const long long lNaN = 0xfff8000000000000;
76#define NAN (*(double *)&lNaN)
79wxEvtHandler *g_pais_alert_dialog_active;
83bool g_bUseOnlyConfirmedAISName;
84wxString GetShipNameFromFile(
int);
85wxString AISTargetNameFileName;
87unsigned g_OwnShipmmsi;
108EVT_TIMER(TIMER_AIS1, AisDecoder::OnTimerAIS)
109EVT_TIMER(TIMER_DSC, AisDecoder::OnTimerDSC)
112static constexpr
double ms_to_knot_factor = 1.9438444924406;
118static
bool b_firstrx;
119static
int first_rx_ticks;
121static
double arpa_ref_hdg = NAN;
123static inline
double GeodesicRadToDeg(
double rads) {
124 return rads * 180.0 / M_PI;
127static inline double MS2KNOTS(
double ms) {
return ms * 1.9438444924406; }
129static bool isBuoyMmsi(
unsigned msi) {
134 int mid = msi / 1000000;
135 if ((mid > 200 && mid < 880) || mid >= 970) {
144int AisMeteoNewMmsi(
int,
int,
int,
int,
int);
148 AIS_Target_Name_Hash *AISTargetNamesC,
149 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi);
150static void BuildERIShipTypeHash() {
151 make_hash_ERI(8000, _(
"Vessel, type unknown"));
152 make_hash_ERI(8150, _(
"Freightbarge"));
153 make_hash_ERI(8160, _(
"Tankbarge"));
154 make_hash_ERI(8163, _(
"Tankbarge, dry cargo as if liquid (e.g. cement)"));
155 make_hash_ERI(8450, _(
"Service vessel, police patrol, port service"));
156 make_hash_ERI(8430, _(
"Pushboat, single"));
157 make_hash_ERI(8510, _(
"Object, not otherwise specified"));
158 make_hash_ERI(8470, _(
"Object, towed, not otherwise specified"));
159 make_hash_ERI(8490, _(
"Bunkership"));
160 make_hash_ERI(8010, _(
"Motor freighter"));
161 make_hash_ERI(8020, _(
"Motor tanker"));
162 make_hash_ERI(8021, _(
"Motor tanker, liquid cargo, type N"));
163 make_hash_ERI(8022, _(
"Motor tanker, liquid cargo, type C"));
164 make_hash_ERI(8023, _(
"Motor tanker, dry cargo as if liquid (e.g. cement)"));
165 make_hash_ERI(8030, _(
"Container vessel"));
166 make_hash_ERI(8040, _(
"Gas tanker"));
167 make_hash_ERI(8050, _(
"Motor freighter, tug"));
168 make_hash_ERI(8060, _(
"Motor tanker, tug"));
169 make_hash_ERI(8070, _(
"Motor freighter with one or more ships alongside"));
170 make_hash_ERI(8080, _(
"Motor freighter with tanker"));
171 make_hash_ERI(8090, _(
"Motor freighter pushing one or more freighters"));
172 make_hash_ERI(8100, _(
"Motor freighter pushing at least one tank-ship"));
173 make_hash_ERI(8110, _(
"Tug, freighter"));
174 make_hash_ERI(8120, _(
"Tug, tanker"));
175 make_hash_ERI(8130, _(
"Tug freighter, coupled"));
176 make_hash_ERI(8140, _(
"Tug, freighter/tanker, coupled"));
177 make_hash_ERI(8161, _(
"Tankbarge, liquid cargo, type N"));
178 make_hash_ERI(8162, _(
"Tankbarge, liquid cargo, type C"));
179 make_hash_ERI(8170, _(
"Freightbarge with containers"));
180 make_hash_ERI(8180, _(
"Tankbarge, gas"));
181 make_hash_ERI(8210, _(
"Pushtow, one cargo barge"));
182 make_hash_ERI(8220, _(
"Pushtow, two cargo barges"));
183 make_hash_ERI(8230, _(
"Pushtow, three cargo barges"));
184 make_hash_ERI(8240, _(
"Pushtow, four cargo barges"));
185 make_hash_ERI(8250, _(
"Pushtow, five cargo barges"));
186 make_hash_ERI(8260, _(
"Pushtow, six cargo barges"));
187 make_hash_ERI(8270, _(
"Pushtow, seven cargo barges"));
188 make_hash_ERI(8280, _(
"Pushtow, eight cargo barges"));
189 make_hash_ERI(8290, _(
"Pushtow, nine or more barges"));
190 make_hash_ERI(8310, _(
"Pushtow, one tank/gas barge"));
192 _(
"Pushtow, two barges at least one tanker or gas barge"));
194 _(
"Pushtow, three barges at least one tanker or gas barge"));
196 _(
"Pushtow, four barges at least one tanker or gas barge"));
198 _(
"Pushtow, five barges at least one tanker or gas barge"));
200 _(
"Pushtow, six barges at least one tanker or gas barge"));
202 _(
"Pushtow, seven barges at least one tanker or gas barge"));
204 _(
"Pushtow, eight barges at least one tanker or gas barge"));
206 8390, _(
"Pushtow, nine or more barges at least one tanker or gas barge"));
207 make_hash_ERI(8400, _(
"Tug, single"));
208 make_hash_ERI(8410, _(
"Tug, one or more tows"));
209 make_hash_ERI(8420, _(
"Tug, assisting a vessel or linked combination"));
210 make_hash_ERI(8430, _(
"Pushboat, single"));
211 make_hash_ERI(8440, _(
"Passenger ship, ferry, cruise ship, red cross ship"));
212 make_hash_ERI(8441, _(
"Ferry"));
213 make_hash_ERI(8442, _(
"Red cross ship"));
214 make_hash_ERI(8443, _(
"Cruise ship"));
215 make_hash_ERI(8444, _(
"Passenger ship without accommodation"));
216 make_hash_ERI(8460, _(
"Vessel, work maintenance craft, floating derrick, "
217 "cable-ship, buoy-ship, dredge"));
218 make_hash_ERI(8480, _(
"Fishing boat"));
219 make_hash_ERI(8500, _(
"Barge, tanker, chemical"));
220 make_hash_ERI(1500, _(
"General cargo Vessel maritime"));
221 make_hash_ERI(1510, _(
"Unit carrier maritime"));
222 make_hash_ERI(1520, _(
"Bulk carrier maritime"));
223 make_hash_ERI(1530, _(
"Tanker"));
224 make_hash_ERI(1540, _(
"Liquified gas tanker"));
225 make_hash_ERI(1850, _(
"Pleasure craft, longer than 20 metres"));
226 make_hash_ERI(1900, _(
"Fast ship"));
227 make_hash_ERI(1910, _(
"Hydrofoil"));
231static wxString DecodeDSEExpansionCharacters(
const wxString &dseData) {
233 char lookupTable[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
' ',
234 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
235 'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
236 'W',
'X',
'Y',
'Z',
'.',
',',
'-',
'/',
' '};
238 for (
size_t i = 0; i < dseData.length(); i += 2) {
240 lookupTable[strtol(dseData.Mid(i, 2).data(),
nullptr, 10)]);
245static void getMmsiProperties(std::shared_ptr<AisTargetData> &pTargetData) {
246 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
247 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
249 pTargetData->b_isFollower = props->m_bFollower;
250 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
251 if (TRACKTYPE_NEVER == props->TrackType) {
252 pTargetData->b_show_track =
false;
253 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
254 pTargetData->b_show_track =
true;
265 const std::shared_ptr<AisTargetData> &ptd) {
266 bool parse_result =
false;
267 bool b_posn_report =
false;
269 wxDateTime now = wxDateTime::Now();
271 int message_ID = bstr->
GetInt(1, 6);
272 ptd->MID = message_ID;
275 int met_mmsi = ptd->MMSI;
278 ptd->MMSI = bstr->
GetInt(9, 30);
280 switch (message_ID) {
286 ptd->NavStatus = bstr->
GetInt(39, 4);
287 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
289 int lon = bstr->
GetInt(62, 28);
290 if (lon & 0x08000000)
292 double lon_tentative = lon / 600000.;
294 int lat = bstr->
GetInt(90, 27);
295 if (lat & 0x04000000)
297 double lat_tentative = lat / 600000.;
299 if ((lon_tentative <= 180.) &&
303 ptd->Lon = lon_tentative;
304 ptd->Lat = lat_tentative;
305 ptd->b_positionDoubtful =
false;
306 ptd->b_positionOnceValid =
true;
307 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
308 ptd->PositionReportTicks = now.GetTicks();
310 ptd->b_positionDoubtful =
true;
313 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
314 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
316 ptd->ROTAIS = bstr->
GetInt(43, 8);
317 double rot_dir = 1.0;
319 if (ptd->ROTAIS == 128)
321 else if ((ptd->ROTAIS & 0x80) == 0x80) {
322 ptd->ROTAIS = ptd->ROTAIS - 256;
326 ptd->ROTIND = wxRound(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733),
329 ptd->m_utc_sec = bstr->
GetInt(138, 6);
331 if ((1 == message_ID) ||
334 ptd->SyncState = bstr->
GetInt(151, 2);
335 ptd->SlotTO = bstr->
GetInt(153, 2);
336 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
338 ptd->m_utc_hour = bstr->
GetInt(155, 5);
340 ptd->m_utc_min = bstr->
GetInt(160, 7);
342 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
343 (ptd->m_utc_sec < 60)) {
344 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
345 rx_ticks = rx_time.GetTicks();
347 first_rx_ticks = rx_ticks;
355 ptd->blue_paddle = bstr->
GetInt(144, 2);
356 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
358 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
361 int mmsi_start = ptd->MMSI / 10000000;
363 if (mmsi_start == 97) {
364 ptd->Class = AIS_SART;
365 ptd->StaticReportTicks =
380 b_posn_report =
true;
389 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
391 int lon = bstr->
GetInt(58, 28);
392 if (lon & 0x08000000)
394 double lon_tentative = lon / 600000.;
396 int lat = bstr->
GetInt(86, 27);
397 if (lat & 0x04000000)
399 double lat_tentative = lat / 600000.;
401 if ((lon_tentative <= 180.) &&
405 ptd->Lon = lon_tentative;
406 ptd->Lat = lat_tentative;
407 ptd->b_positionDoubtful =
false;
408 ptd->b_positionOnceValid =
true;
409 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
410 ptd->PositionReportTicks = now.GetTicks();
412 ptd->b_positionDoubtful =
true;
414 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
415 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
417 ptd->m_utc_sec = bstr->
GetInt(134, 6);
419 if (!ptd->b_isDSCtarget) {
420 if (!isBuoyMmsi(ptd->MMSI))
421 ptd->Class = AIS_CLASS_B;
423 ptd->Class = AIS_BUOY;
426 b_posn_report =
true;
434 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
435 int lon = bstr->
GetInt(58, 28);
436 if (lon & 0x08000000)
438 double lon_tentative = lon / 600000.;
440 int lat = bstr->
GetInt(86, 27);
441 if (lat & 0x04000000)
443 double lat_tentative = lat / 600000.;
445 if ((lon_tentative <= 180.) &&
449 ptd->Lon = lon_tentative;
450 ptd->Lat = lat_tentative;
451 ptd->b_positionDoubtful =
false;
452 ptd->b_positionOnceValid =
true;
453 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
454 ptd->PositionReportTicks = now.GetTicks();
456 ptd->b_positionDoubtful =
true;
458 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
459 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
460 ptd->m_utc_sec = bstr->
GetInt(134, 6);
462 bstr->GetStr(144, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
463 ptd->b_nameValid =
true;
464 if (!ptd->b_isDSCtarget) {
465 ptd->ShipType = (
unsigned char)bstr->
GetInt(264, 8);
467 ptd->DimA = bstr->
GetInt(272, 9);
468 ptd->DimB = bstr->
GetInt(281, 9);
469 ptd->DimC = bstr->
GetInt(290, 6);
470 ptd->DimD = bstr->
GetInt(296, 6);
472 if (!ptd->b_isDSCtarget) {
474 if (!isBuoyMmsi(ptd->MMSI))
475 ptd->Class = AIS_CLASS_B;
477 ptd->Class = AIS_BUOY;
480 b_posn_report =
true;
493 int bitCorrection = 10;
497 double lon_tentative = 181.;
498 double lat_tentative = 91.;
501 printf(
"AIS Message 27 - received:\r\n");
502 printf(
"MMSI : %i\r\n", ptd->MMSI);
508 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
510 ptd->NavStatus = bstr->
GetInt(39, 4);
512 int lon = bstr->
GetInt(45, 18);
513 int lat = bstr->
GetInt(63, 17);
519 if (lat >= (0x4000000 >> bitCorrection)) {
520 lat_tentative = (0x8000000 >> bitCorrection) - lat;
525 if (lon >= (0x8000000 >> bitCorrection)) {
526 lon_tentative = (0x10000000 >> bitCorrection) - lon;
531 lat_tentative = lat_tentative / resolution / 60.0;
532 lon_tentative = lon_tentative / resolution / 60.0;
535 printf(
"Latitude : %f\r\n", lat_tentative);
536 printf(
"Longitude : %f\r\n", lon_tentative);
540 int positionLatency = bstr->
GetInt(95, 1);
542 if ((lon_tentative <= 180.) &&
546 ptd->Lon = lon_tentative;
547 ptd->Lat = lat_tentative;
548 ptd->b_positionDoubtful =
false;
549 ptd->b_positionOnceValid =
true;
550 if (positionLatency == 0) {
553 printf(
"Low latency position report.\r\n");
555 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
556 ptd->PositionReportTicks = now.GetTicks();
559 ptd->b_positionDoubtful =
true;
561 ptd->SOG = 1.0 * (bstr->
GetInt(80, 6));
562 ptd->COG = 1.0 * (bstr->
GetInt(85, 9));
564 b_posn_report =
true;
571 if (!ptd->b_isDSCtarget) ptd->Class = AIS_CLASS_A;
577 int AIS_version_indicator = bstr->
GetInt(39, 2);
578 if (AIS_version_indicator < 4) {
579 ptd->IMO = bstr->
GetInt(41, 30);
581 bstr->GetStr(71, 42, &ptd->CallSign[0], 7);
582 bstr->GetStr(113, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
583 ptd->b_nameValid =
true;
584 if (!ptd->b_isDSCtarget) {
585 ptd->ShipType = (
unsigned char)bstr->
GetInt(233, 8);
588 ptd->DimA = bstr->
GetInt(241, 9);
589 ptd->DimB = bstr->
GetInt(250, 9);
590 ptd->DimC = bstr->
GetInt(259, 6);
591 ptd->DimD = bstr->
GetInt(265, 6);
593 ptd->ETA_Mo = bstr->
GetInt(275, 4);
594 ptd->ETA_Day = bstr->
GetInt(279, 5);
595 ptd->ETA_Hr = bstr->
GetInt(284, 5);
596 ptd->ETA_Min = bstr->
GetInt(289, 6);
598 ptd->Draft = (double)(bstr->
GetInt(295, 8)) / 10.0;
600 bstr->GetStr(303, 120, &ptd->Destination[0], DESTINATION_LEN - 1);
602 ptd->StaticReportTicks = now.GetTicks();
611 int part_number = bstr->
GetInt(39, 2);
612 if (0 == part_number) {
613 bstr->GetStr(41, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
614 ptd->b_nameValid =
true;
617 }
else if (1 == part_number) {
618 if (!ptd->b_isDSCtarget) {
619 ptd->ShipType = (
unsigned char)bstr->
GetInt(41, 8);
621 bstr->GetStr(91, 42, &ptd->CallSign[0], 7);
623 ptd->DimA = bstr->
GetInt(133, 9);
624 ptd->DimB = bstr->
GetInt(142, 9);
625 ptd->DimC = bstr->
GetInt(151, 6);
626 ptd->DimD = bstr->
GetInt(157, 6);
633 ptd->Class = AIS_BASE;
635 ptd->m_utc_hour = bstr->
GetInt(62, 5);
636 ptd->m_utc_min = bstr->
GetInt(67, 6);
637 ptd->m_utc_sec = bstr->
GetInt(73, 6);
639 int lon = bstr->
GetInt(80, 28);
640 if (lon & 0x08000000)
642 double lon_tentative = lon / 600000.;
644 int lat = bstr->
GetInt(108, 27);
645 if (lat & 0x04000000)
647 double lat_tentative = lat / 600000.;
649 if ((lon_tentative <= 180.) &&
653 ptd->Lon = lon_tentative;
654 ptd->Lat = lat_tentative;
655 ptd->b_positionDoubtful =
false;
656 ptd->b_positionOnceValid =
true;
657 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
658 ptd->PositionReportTicks = now.GetTicks();
660 ptd->b_positionDoubtful =
true;
667 b_posn_report =
true;
673 ptd->SOG = bstr->
GetInt(51, 10);
675 int lon = bstr->
GetInt(62, 28);
676 if (lon & 0x08000000)
678 double lon_tentative = lon / 600000.;
680 int lat = bstr->
GetInt(90, 27);
681 if (lat & 0x04000000)
683 double lat_tentative = lat / 600000.;
685 if ((lon_tentative <= 180.) &&
689 ptd->Lon = lon_tentative;
690 ptd->Lat = lat_tentative;
691 ptd->b_positionDoubtful =
false;
692 ptd->b_positionOnceValid =
true;
693 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
694 ptd->PositionReportTicks = now.GetTicks();
696 ptd->b_positionDoubtful =
true;
699 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
701 int alt_tent = bstr->
GetInt(39, 12);
702 ptd->altitude = alt_tent;
704 ptd->b_SarAircraftPosnReport =
true;
707 b_posn_report =
true;
713 ptd->ShipType = (
unsigned char)bstr->
GetInt(39, 5);
719 ptd->DimA = bstr->
GetInt(220, 9);
720 ptd->DimB = bstr->
GetInt(229, 9);
721 ptd->DimC = bstr->
GetInt(238, 6);
722 ptd->DimD = bstr->
GetInt(244, 6);
725 ptd->m_utc_sec = bstr->
GetInt(254, 6);
727 int offpos = bstr->
GetInt(260, 1);
728 int virt = bstr->
GetInt(270, 1);
731 ptd->NavStatus = ATON_VIRTUAL;
733 ptd->NavStatus = ATON_REAL;
734 if (ptd->m_utc_sec <= 59 ) {
736 if (offpos) ptd->NavStatus += 1;
739 bstr->GetStr(44, 120, &ptd->ShipName[0], SHIP_NAME_LEN);
742 if (bstr->GetBitCount() > 276) {
743 int nx = ((bstr->GetBitCount() - 272) / 6) * 6;
744 bstr->GetStr(273, nx, &ptd->ShipNameExtension[0], 14);
745 ptd->ShipNameExtension[14] = 0;
747 ptd->ShipNameExtension[0] = 0;
750 ptd->b_nameValid =
true;
754 ptd->Class = AIS_ATON;
756 int lon = bstr->
GetInt(165, 28);
758 if (lon & 0x08000000)
760 double lon_tentative = lon / 600000.;
762 int lat = bstr->
GetInt(193, 27);
764 if (lat & 0x04000000)
766 double lat_tentative = lat / 600000.;
768 if ((lon_tentative <= 180.) &&
772 ptd->Lon = lon_tentative;
773 ptd->Lat = lat_tentative;
774 ptd->b_positionDoubtful =
false;
775 ptd->b_positionOnceValid =
true;
776 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
777 ptd->PositionReportTicks = now.GetTicks();
779 ptd->b_positionDoubtful =
true;
781 b_posn_report =
true;
786 int dac = bstr->
GetInt(41, 10);
787 int fi = bstr->
GetInt(51, 6);
793 ptd->b_isEuroInland =
true;
795 bstr->GetStr(57, 48, &ptd->Euro_VIN[0], 8);
796 ptd->Euro_Length = ((double)bstr->
GetInt(105, 13)) / 10.0;
797 ptd->Euro_Beam = ((double)bstr->
GetInt(118, 10)) / 10.0;
798 ptd->UN_shiptype = bstr->
GetInt(128, 14);
799 ptd->Euro_Draft = ((double)bstr->
GetInt(145, 11)) / 100.0;
803 if (dac == 1 || dac == 366)
807 if (bstr->GetBitCount() >= 111) {
809 an.link_id = bstr->
GetInt(57, 10);
810 an.notice_type = bstr->
GetInt(67, 7);
811 an.month = bstr->
GetInt(74, 4);
812 an.day = bstr->
GetInt(78, 5);
813 an.hour = bstr->
GetInt(83, 5);
814 an.minute = bstr->
GetInt(88, 6);
815 an.duration_minutes = bstr->
GetInt(94, 18);
817 wxDateTime now_ = wxDateTime::Now();
820 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
821 now_.GetYear(), an.hour, an.minute);
826 if (an.start_time > now_ + wxTimeSpan::Hours(48))
827 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
828 now_.GetYear() - 1, an.hour, an.minute);
831 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
836 if (an.expiry_time < now_ - wxTimeSpan::Hours(24)) {
837 an.start_time.Set(an.day, wxDateTime::Month(an.month - 1),
838 now_.GetYear() + 1, an.hour, an.minute);
840 an.start_time + wxTimeSpan::Minutes(an.duration_minutes);
845 int subarea_len = 87;
848 float pos_scale = 60000.0;
856 pos_scale = 600000.0;
861 int subarea_count = (bstr->GetBitCount() - 111) / subarea_len;
862 for (
int i = 0; i < subarea_count; ++i) {
863 int base = 111 + i * subarea_len;
865 sa.shape = bstr->
GetInt(base + 1, 3);
866 int scale_factor = 1;
867 if (sa.shape == AIS8_001_22_SHAPE_TEXT) {
870 bstr->GetStr(base + 4, subarea_len - 3, t, 14);
871 sa.text = wxString(t, wxConvUTF8);
873 int scale_multipliers[4] = {1, 10, 100, 1000};
874 scale_factor = scale_multipliers[bstr->
GetInt(base + 4, 2)];
876 case AIS8_001_22_SHAPE_SECTOR:
877 sa.left_bound_deg = bstr->
GetInt(
878 base + 6 + lon_len + lat_len + prec_size + 12, 9);
879 sa.right_bound_deg = bstr->
GetInt(
880 base + 6 + lon_len + lat_len + prec_size + 12 + 9, 9);
881 case AIS8_001_22_SHAPE_CIRCLE:
883 bstr->
GetInt(base + 6 + lon_len + lat_len + 3, 12) *
886 case AIS8_001_22_SHAPE_RECT:
888 bstr->
GetInt(base + 6, lon_len,
true) / pos_scale;
890 bstr->
GetInt(base + 6 + lon_len, lat_len,
true) /
893 bstr->
GetInt(base + 6 + lon_len + lat_len + prec_size,
898 base + 6 + lon_len + lat_len + prec_size + 8, 8) *
900 sa.orient_deg = bstr->
GetInt(
901 base + 6 + lon_len + lat_len + prec_size + 8 + 8, 9);
903 case AIS8_001_22_SHAPE_POLYLINE:
904 case AIS8_001_22_SHAPE_POLYGON:
905 for (
int j = 0; j < 4; ++j) {
906 sa.angles[j] = bstr->
GetInt(base + 6 + j * 20, 10) * 0.5;
908 bstr->
GetInt(base + 16 + j * 20, 10) * scale_factor;
912 an.sub_areas.push_back(sa);
914 ptd->area_notices[an.link_id] = an;
921 if (bstr->GetBitCount() >= 360) {
923 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
926 double lon_tentative = 181.;
927 double lat_tentative = 91.;
929 int lon = bstr->
GetInt(57, 25);
930 int lat = bstr->
GetInt(82, 24);
932 if (lon & 0x01000000)
934 lon_tentative = lon / 60000.;
936 if (lat & 0x00800000)
938 lat_tentative = lat / 60000.;
940 ptd->Lon = lon_tentative;
941 ptd->Lat = lat_tentative;
944 wxString x = ptd->ShipName;
945 if (x.Find(
"METEO") == wxNOT_FOUND) {
947 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
948 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
951 wxString nameID =
"METEO ";
952 nameID << wxString::Format(
"%0.3f", abs(id1) + abs(id2)).Right(3);
953 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
956 ptd->met_data.pos_acc = bstr->
GetInt(106, 1);
957 ptd->met_data.day = bstr->
GetInt(107, 5);
958 ptd->met_data.hour = bstr->
GetInt(112, 5);
959 ptd->met_data.minute = bstr->
GetInt(117, 6);
960 ptd->met_data.wind_kn = bstr->
GetInt(123, 7);
961 ptd->met_data.wind_gust_kn = bstr->
GetInt(130, 7);
962 ptd->met_data.wind_dir = bstr->
GetInt(137, 9);
963 ptd->met_data.wind_gust_dir = bstr->
GetInt(146, 9);
965 int tmp = bstr->
GetInt(155, 11);
966 if (tmp & 0x00000400)
968 ptd->met_data.air_temp = tmp / 10.;
969 ptd->met_data.rel_humid = bstr->
GetInt(166, 7);
970 int dew = bstr->
GetInt(173, 10);
971 if (dew & 0x00000200)
973 ptd->met_data.dew_point = dew / 10.;
978 ptd->met_data.airpress = bstr->
GetInt(183, 9) + 799;
979 ptd->met_data.airpress_tend = bstr->
GetInt(192, 2);
981 int horVis = bstr->
GetInt(194, 8);
982 if (horVis & 0x80u) {
984 ptd->met_data.hor_vis_GT =
true;
986 ptd->met_data.hor_vis_GT =
false;
988 ptd->met_data.hor_vis = horVis / 10.0;
990 ptd->met_data.water_lev_dev = (bstr->
GetInt(202, 12) / 100.) - 10.;
991 ptd->met_data.water_lev_trend = bstr->
GetInt(214, 2);
992 ptd->met_data.current = bstr->
GetInt(216, 8) / 10.;
993 ptd->met_data.curr_dir = bstr->
GetInt(224, 9);
994 ptd->met_data.wave_height = bstr->
GetInt(277, 8) / 10.;
995 ptd->met_data.wave_period = bstr->
GetInt(285, 6);
996 ptd->met_data.wave_dir = bstr->
GetInt(291, 9);
997 ptd->met_data.swell_height = bstr->
GetInt(300, 8) / 10;
998 ptd->met_data.swell_per = bstr->
GetInt(308, 6);
999 ptd->met_data.swell_dir = bstr->
GetInt(314, 9);
1000 ptd->met_data.seastate = bstr->
GetInt(323, 4);
1002 int wt = bstr->
GetInt(327, 10);
1003 if (wt & 0x00000200)
1005 ptd->met_data.water_temp = wt / 10.;
1007 ptd->met_data.precipitation = bstr->
GetInt(337, 3);
1008 ptd->met_data.salinity = bstr->
GetInt(340, 9) / 10.;
1009 ptd->met_data.ice = bstr->
GetInt(349, 2);
1011 ptd->Class = AIS_METEO;
1015 ptd->b_NoTrack =
true;
1016 ptd->b_show_track =
false;
1017 ptd->b_positionDoubtful =
false;
1018 ptd->b_positionOnceValid =
true;
1019 b_posn_report =
true;
1020 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1021 ptd->PositionReportTicks = now.GetTicks();
1022 ptd->b_nameValid =
true;
1024 parse_result =
true;
1030 if (dac == 367 && fi == 33) {
1034 const int size = bstr->GetBitCount();
1037 if (met_mmsi != 666) ptd->MMSI = met_mmsi;
1038 const int startbits = 56;
1039 const int slotsize = 112;
1040 const int slots_count = (size - startbits) / slotsize;
1042 for (
int slot = 0; slot < slots_count; slot++) {
1043 slotbit = slot * slotsize;
1044 int type = bstr->
GetInt(slotbit + 57, 4);
1045 ptd->met_data.hour = bstr->
GetInt(slotbit + 66, 5);
1046 ptd->met_data.minute = bstr->
GetInt(slotbit + 71, 6);
1047 int Site_ID = bstr->
GetInt(slotbit + 77, 7);
1050 if (!ptd->b_nameValid) {
1051 wxString nameID =
"METEO Site: ";
1053 strncpy(ptd->ShipName, nameID, SHIP_NAME_LEN - 1);
1054 ptd->b_nameValid =
true;
1058 int lon = bstr->
GetInt(slotbit + 90, 28);
1059 if (lon & 0x08000000)
1061 ptd->Lon = lon / 600000.;
1063 int lat = bstr->
GetInt(slotbit + 118, 27);
1064 if (lat & 0x04000000)
1066 ptd->Lat = lat / 600000.;
1067 ptd->b_positionOnceValid =
true;
1069 }
else if (type == 1) {
1070 bstr->GetStr(slotbit + 84, 84, &ptd->ShipName[0], SHIP_NAME_LEN);
1071 ptd->b_nameValid =
true;
1073 }
else if (type == 2) {
1075 int descr = bstr->
GetInt(slotbit + 116, 3);
1076 if (descr == 1 || descr == 2) {
1077 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1078 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1079 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1080 ptd->met_data.wind_gust_dir = bstr->
GetInt(slotbit + 107, 9);
1083 }
else if (type == 3) {
1085 int descr = bstr->
GetInt(slotbit + 108, 3);
1086 if (descr == 1 || descr == 2) {
1087 int wltype = bstr->
GetInt(slotbit + 84, 1);
1088 int wl = bstr->
GetInt(slotbit + 85, 16);
1089 if (wl & 0x00004000)
1093 ptd->met_data.water_level = wl / 100.;
1095 ptd->met_data.water_lev_dev = wl / 100.;
1097 ptd->met_data.water_lev_trend = bstr->
GetInt(slotbit + 101, 2);
1098 ptd->met_data.vertical_ref = bstr->
GetInt(slotbit + 103, 5);
1100 }
else if (type == 6) {
1101 ptd->met_data.current = bstr->
GetInt(slotbit + 102, 8) / 10.0;
1102 ptd->met_data.curr_dir = bstr->
GetInt(slotbit + 110, 9);
1103 }
else if (type == 7) {
1105 bstr->
GetInt(slotbit + 111, 3);
1106 if (swell_descr == 1 || swell_descr == 2) {
1107 ptd->met_data.swell_height =
1108 bstr->
GetInt(slotbit + 84, 8) / 10.0;
1109 ptd->met_data.swell_per = bstr->
GetInt(slotbit + 92, 6);
1110 ptd->met_data.swell_dir = bstr->
GetInt(slotbit + 98, 9);
1112 ptd->met_data.seastate = bstr->
GetInt(slotbit + 107, 4);
1113 int wt_descr = bstr->
GetInt(slotbit + 131, 3);
1114 if (wt_descr == 1 || wt_descr == 2)
1115 ptd->met_data.water_temp =
1116 bstr->
GetInt(slotbit + 114, 10) / 10. - 10.;
1118 int wawe_descr = bstr->
GetInt(slotbit + 157, 3);
1119 if (wawe_descr == 1 || wawe_descr == 2) {
1120 ptd->met_data.wave_height =
1121 bstr->
GetInt(slotbit + 134, 8) / 10.0;
1122 ptd->met_data.wave_period = bstr->
GetInt(slotbit + 142, 6);
1123 ptd->met_data.wave_dir = bstr->
GetInt(slotbit + 148, 9);
1125 ptd->met_data.salinity = bstr->
GetInt(slotbit + 160, 9 / 10.0);
1127 }
else if (type == 8) {
1128 ptd->met_data.water_temp =
1129 bstr->
GetInt(slotbit + 84, 10) / 10.0 - 10.0;
1130 ptd->met_data.salinity = bstr->
GetInt(slotbit + 120, 9) / 10.0;
1132 }
else if (type == 9) {
1133 int tmp = bstr->
GetInt(slotbit + 84, 11);
1134 if (tmp & 0x00000400)
1136 ptd->met_data.air_temp = tmp / 10.;
1137 int pp, precip = bstr->
GetInt(slotbit + 98, 2);
1148 ptd->met_data.precipitation = pp;
1149 ptd->met_data.hor_vis = bstr->
GetInt(slotbit + 100, 8) / 10.0;
1150 ptd->met_data.dew_point =
1151 bstr->
GetInt(slotbit + 108, 10) / 10.0 - 20.0;
1152 ptd->met_data.airpress = bstr->
GetInt(slotbit + 121, 9) + 799;
1153 ptd->met_data.airpress_tend = bstr->
GetInt(slotbit + 130, 2);
1154 ptd->met_data.salinity = bstr->
GetInt(slotbit + 135, 9) / 10.0;
1156 }
else if (type == 11) {
1158 int descr = bstr->
GetInt(slotbit + 113, 3);
1159 if (descr == 1 || descr == 2) {
1160 ptd->met_data.wind_kn = bstr->
GetInt(slotbit + 84, 7);
1161 ptd->met_data.wind_gust_kn = bstr->
GetInt(slotbit + 91, 7);
1162 ptd->met_data.wind_dir = bstr->
GetInt(slotbit + 98, 9);
1167 if (ptd->b_positionOnceValid) {
1168 ptd->Class = AIS_METEO;
1172 ptd->b_NoTrack =
true;
1173 ptd->b_show_track =
false;
1174 ptd->b_positionDoubtful =
false;
1175 b_posn_report =
true;
1176 ptd->LastPositionReportTicks = ptd->PositionReportTicks;
1177 ptd->PositionReportTicks = now.GetTicks();
1178 ptd->b_nameValid =
true;
1180 parse_result =
true;
1190 char msg_14_text[968];
1191 if (bstr->GetBitCount() > 40) {
1192 int nx = ((bstr->GetBitCount() - 40) / 6) * 6;
1193 int nd = bstr->GetStr(41, nx, msg_14_text, 968);
1195 nd = wxMin(nd, 967);
1196 msg_14_text[nd] = 0;
1197 ptd->MSG_14_text = wxString(msg_14_text, wxConvUTF8);
1199 parse_result =
true;
1212 if (b_posn_report) ptd->b_lost =
false;
1214 if (
true == parse_result) {
1216 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
1217 ptd->b_active =
true;
1220 return parse_result;
1224 : m_signalk_selfid(
""), m_callbacks(callbacks) {
1226 AISTargetNamesC =
new AIS_Target_Name_Hash;
1227 AISTargetNamesNC =
new AIS_Target_Name_Hash;
1229 if (g_benableAISNameCache) {
1230 if (wxFileName::FileExists(AISTargetNameFileName)) {
1232 if (infile.Open(AISTargetNameFileName)) {
1233 AIS_Target_Name_Hash *HashFile = AISTargetNamesNC;
1234 wxString line = infile.GetFirstLine();
1235 while (!infile.Eof()) {
1236 if (line.IsSameAs(wxT(
"+++==Confirmed Entry's==+++")))
1237 HashFile = AISTargetNamesC;
1239 if (line.IsSameAs(wxT(
"+++==Non Confirmed Entry's==+++")))
1240 HashFile = AISTargetNamesNC;
1242 wxStringTokenizer tokenizer(line, _T(
","));
1243 int mmsi = wxAtoi(tokenizer.GetNextToken());
1244 wxString name = tokenizer.GetNextToken().Trim();
1245 (*HashFile)[mmsi] = name;
1248 line = infile.GetNextLine();
1255 BuildERIShipTypeHash();
1257 g_pais_alert_dialog_active =
nullptr;
1258 m_bAIS_Audio_Alert_On =
false;
1262 m_bAIS_AlertPlaying =
false;
1264 TimerAIS.SetOwner(
this, TIMER_AIS1);
1265 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
1267 m_ptentative_dsctarget =
nullptr;
1268 m_dsc_timer.SetOwner(
this, TIMER_DSC);
1275 InitCommListeners();
1278AisDecoder::~AisDecoder() {
1287 if (outfile.Open(AISTargetNameFileName)) {
1288 wxString content = wxT(
"+++==Confirmed Entry's==+++");
1289 AIS_Target_Name_Hash::iterator it;
1290 for (it = AISTargetNamesC->begin(); it != AISTargetNamesC->end(); ++it) {
1291 content.append(_T(
"\r\n"));
1292 content.append(wxString::Format(wxT(
"%i"), it->first));
1293 content.append(_T(
",")).append(it->second);
1295 content.append(_T(
"\r\n"));
1296 content.append(_T(
"+++==Non Confirmed Entry's==+++"));
1297 for (it = AISTargetNamesNC->begin(); it != AISTargetNamesNC->end(); ++it) {
1298 content.append(_T(
"\r\n"));
1299 content.append(wxString::Format(wxT(
"%i"), it->first));
1300 content.append(_T(
",")).append(it->second);
1302 outfile.Write(content);
1306 AISTargetNamesC->clear();
1307 delete AISTargetNamesC;
1308 AISTargetNamesNC->clear();
1309 delete AISTargetNamesNC;
1314 m_AIS_Audio_Alert_Timer.Stop();
1319 "First message[1, 2] ticks: %d Last Message [1,2]ticks %d Difference: "
1321 first_rx_ticks, rx_ticks, rx_ticks - first_rx_ticks);
1325bool IsTargetOnTheIgnoreList(
const int &mmsi) {
1327 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1328 if (mmsi == g_MMSI_Props_Array[i]->MMSI) {
1330 if (props->m_bignore) {
1338void AisDecoder::InitCommListeners() {
1344 listener_N0183_VDM.
Listen(n0183_msg_VDM,
this, EVT_N0183_VDM);
1347 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1348 HandleN0183_AIS(n0183_msg);
1353 listener_N0183_FRPOS.
Listen(n0183_msg_FRPOS,
this, EVT_N0183_FRPOS);
1355 Bind(EVT_N0183_FRPOS, [&](
const ObservedEvt &ev) {
1357 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1358 HandleN0183_AIS(n0183_msg);
1363 listener_N0183_CDDSC.
Listen(n0183_msg_CDDSC,
this, EVT_N0183_CDDSC);
1364 Bind(EVT_N0183_CDDSC, [&](
const ObservedEvt &ev) {
1366 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1367 HandleN0183_AIS(n0183_msg);
1372 listener_N0183_CDDSE.
Listen(n0183_msg_CDDSE,
this, EVT_N0183_CDDSE);
1373 Bind(EVT_N0183_CDDSE, [&](
const ObservedEvt &ev) {
1375 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1376 HandleN0183_AIS(n0183_msg);
1381 listener_N0183_TLL.
Listen(n0183_msg_TLL,
this, EVT_N0183_TLL);
1385 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1386 HandleN0183_AIS(n0183_msg);
1391 listener_N0183_TTM.
Listen(n0183_msg_ttm,
this, EVT_N0183_TTM);
1394 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1395 HandleN0183_AIS(n0183_msg);
1400 listener_N0183_OSD.
Listen(n0183_msg_OSD,
this, EVT_N0183_OSD);
1403 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1404 HandleN0183_AIS(n0183_msg);
1409 listener_N0183_WPL.
Listen(n0183_msg_WPL,
this, EVT_N0183_WPL);
1412 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1413 HandleN0183_AIS(n0183_msg);
1418 listener_SignalK.
Listen(sk_msg,
this, EVT_SIGNALK);
1420 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
1425 Nmea2000Msg n2k_msg_129038(
static_cast<uint64_t
>(129038));
1426 listener_N2K_129038.
Listen(n2k_msg_129038,
this, EVT_N2K_129038);
1428 HandleN2K_129038(UnpackEvtPointer<Nmea2000Msg>(ev));
1433 Nmea2000Msg n2k_msg_129039(
static_cast<uint64_t
>(129039));
1434 listener_N2K_129039.
Listen(n2k_msg_129039,
this, EVT_N2K_129039);
1436 HandleN2K_129039(UnpackEvtPointer<Nmea2000Msg>(ev));
1441 Nmea2000Msg n2k_msg_129041(
static_cast<uint64_t
>(129041));
1442 listener_N2K_129041.
Listen(n2k_msg_129041,
this, EVT_N2K_129041);
1444 HandleN2K_129041(UnpackEvtPointer<Nmea2000Msg>(ev));
1449 Nmea2000Msg n2k_msg_129794(
static_cast<uint64_t
>(129794));
1450 listener_N2K_129794.
Listen(n2k_msg_129794,
this, EVT_N2K_129794);
1452 HandleN2K_129794(UnpackEvtPointer<Nmea2000Msg>(ev));
1457 Nmea2000Msg n2k_msg_129809(
static_cast<uint64_t
>(129809));
1458 listener_N2K_129809.
Listen(n2k_msg_129809,
this, EVT_N2K_129809);
1460 HandleN2K_129809(UnpackEvtPointer<Nmea2000Msg>(ev));
1465 Nmea2000Msg n2k_msg_129810(
static_cast<uint64_t
>(129810));
1466 listener_N2K_129810.
Listen(n2k_msg_129810,
this, EVT_N2K_129810);
1468 HandleN2K_129810(UnpackEvtPointer<Nmea2000Msg>(ev));
1473 Nmea2000Msg n2k_msg_129793(
static_cast<uint64_t
>(129793));
1474 listener_N2K_129793.
Listen(n2k_msg_129793,
this, EVT_N2K_129793);
1476 HandleN2K_129793(UnpackEvtPointer<Nmea2000Msg>(ev));
1480bool AisDecoder::HandleN0183_AIS(
const N0183MsgPtr &n0183_msg) {
1481 std::string str = n0183_msg->payload;
1482 wxString sentence(str.c_str());
1483 DecodeN0183(sentence);
1488bool AisDecoder::HandleN2K_129038(
const N2000MsgPtr &n2k_msg) {
1489 std::vector<unsigned char> v = n2k_msg->payload;
1492 tN2kAISRepeat Repeat;
1503 tN2kAISNavStatus NavStat = N2kaisns_Under_Way_Motoring;
1504 tN2kAISTransceiverInformation AISTransceiverInformation;
1506 if (ParseN2kPGN129038(v, MessageID, Repeat, UserID, Latitude, Longitude,
1507 Accuracy, RAIM, Seconds, COG, SOG, Heading, ROT,
1508 NavStat, AISTransceiverInformation)) {
1509 unsigned mmsi = UserID;
1511 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1515 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1516 bool bnewtarget =
false;
1518 auto it = AISTargetList.find(mmsi);
1519 if (it == AISTargetList.end())
1521 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1525 pTargetData = it->second;
1528 wxDateTime now = wxDateTime::Now();
1532 pTargetData->MMSI = mmsi;
1533 pTargetData->MID = MessageID;
1534 pTargetData->MMSI = mmsi;
1535 pTargetData->Class = AIS_CLASS_A;
1537 if (97 == pTargetData->MMSI / 10000000) {
1538 pTargetData->Class = AIS_SART;
1540 pTargetData->StaticReportTicks = now.GetTicks();
1542 pTargetData->NavStatus =
static_cast<ais_nav_status
>(NavStat);
1543 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1544 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1545 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1546 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1547 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1549 pTargetData->ROTAIS = ROT;
1561 pTargetData->b_OwnShip =
1562 AISTransceiverInformation ==
1563 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1564 pTargetData->b_active =
true;
1565 pTargetData->b_lost =
false;
1566 pTargetData->b_positionOnceValid =
true;
1567 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1568 pTargetData->PositionReportTicks = now.GetTicks();
1570 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1571 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1580bool AisDecoder::HandleN2K_129039(
const N2000MsgPtr &n2k_msg) {
1581 std::vector<unsigned char> v = n2k_msg->payload;
1596 tN2kAISRepeat Repeat;
1606 tN2kAISTransceiverInformation AISTransceiverInformation;
1608 bool DSC, Band, Msg22, State, Display;
1611 if (ParseN2kPGN129039(v, MessageID, Repeat, UserID, Latitude, Longitude,
1612 Accuracy, RAIM, Seconds, COG, SOG,
1613 AISTransceiverInformation, Heading, Unit, Display, DSC,
1614 Band, Msg22, Mode, State)) {
1615 uint32_t mmsi = UserID;
1617 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1621 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1622 bool bnewtarget =
false;
1624 auto it = AISTargetList.find(mmsi);
1625 if (it == AISTargetList.end())
1627 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1631 pTargetData = it->second;
1634 wxDateTime now = wxDateTime::Now();
1638 pTargetData->MMSI = mmsi;
1639 pTargetData->MID = MessageID;
1640 if (!isBuoyMmsi(mmsi))
1641 pTargetData->Class = AIS_CLASS_B;
1643 pTargetData->Class = AIS_BUOY;
1645 pTargetData->NavStatus = UNDEFINED;
1646 if (!N2kIsNA(SOG)) pTargetData->SOG = MS2KNOTS(SOG);
1647 if (!N2kIsNA(COG)) pTargetData->COG = GeodesicRadToDeg(COG);
1648 if (!N2kIsNA(Heading)) pTargetData->HDG = GeodesicRadToDeg(Heading);
1649 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
1650 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
1652 pTargetData->b_positionOnceValid =
true;
1653 pTargetData->b_active =
true;
1654 pTargetData->b_lost =
false;
1655 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1656 pTargetData->PositionReportTicks = now.GetTicks();
1657 pTargetData->b_OwnShip =
1658 AISTransceiverInformation ==
1659 tN2kAISTransceiverInformation::N2kaisown_information_not_broadcast;
1661 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1662 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1672bool AisDecoder::HandleN2K_129041(
const N2000MsgPtr &n2k_msg) {
1673 std::vector<unsigned char> v = n2k_msg->payload;
1675 tN2kAISAtoNReportData data;
1678 struct tN2kAISAtoNReportData {
1680 tN2kAISRepeat Repeat;
1689 double PositionReferenceStarboard ;
1690 double PositionReferenceTrueNorth;
1691 tN2kAISAtoNType AtoNType;
1692 bool OffPositionIndicator;
1693 bool VirtualAtoNFlag;
1694 bool AssignedModeFlag;
1695 tN2kGNSStype GNSSType;
1697 tN2kAISTransceiverInformation AISTransceiverInformation;
1698 char AtoNName[34 + 1];
1701 if (ParseN2kPGN129041(v, data)) {
1702 uint32_t mmsi = data.UserID;
1704 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1708 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1709 bool bnewtarget =
false;
1711 auto it = AISTargetList.find(mmsi);
1712 if (it == AISTargetList.end())
1714 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1718 pTargetData = it->second;
1722 pTargetData->MMSI = mmsi;
1724 wxDateTime now = wxDateTime::Now();
1727 int offpos = data.OffPositionIndicator;
1728 int virt = data.VirtualAtoNFlag;
1731 pTargetData->NavStatus = ATON_VIRTUAL;
1733 pTargetData->NavStatus = ATON_REAL;
1735 pTargetData->m_utc_sec = data.Seconds;
1737 if (pTargetData->m_utc_sec <= 59) {
1738 pTargetData->NavStatus += 1;
1739 if (offpos) pTargetData->NavStatus += 1;
1742 data.AtoNName[34] = 0;
1743 strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1);
1744 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1745 pTargetData->b_nameValid =
true;
1746 pTargetData->MID = 124;
1748 pTargetData->ShipType = data.AtoNType;
1749 pTargetData->Class = AIS_ATON;
1751 if (!N2kIsNA(data.Longitude)) pTargetData->Lon = data.Longitude;
1752 if (!N2kIsNA(data.Latitude)) pTargetData->Lat = data.Latitude;
1753 pTargetData->b_positionDoubtful =
false;
1754 pTargetData->b_positionOnceValid =
true;
1755 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
1756 pTargetData->PositionReportTicks = now.GetTicks();
1760 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1761 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1763 touch_state.Notify();
1770bool AisDecoder::HandleN2K_129794(
const N2000MsgPtr &n2k_msg) {
1771 std::vector<unsigned char> v = n2k_msg->payload;
1774 tN2kAISRepeat Repeat;
1778 char Name[SHIP_NAME_LEN];
1787 char Destination[DESTINATION_LEN];
1788 tN2kAISVersion AISversion;
1789 tN2kGNSStype GNSStype;
1791 tN2kAISTranceiverInfo AISinfo;
1793 if (ParseN2kPGN129794(v, MessageID, Repeat, UserID, IMOnumber, Callsign, Name,
1794 VesselType, Length, Beam, PosRefStbd, PosRefBow,
1795 ETAdate, ETAtime, Draught, Destination, AISversion,
1796 GNSStype, DTE, AISinfo)) {
1797 uint32_t mmsi = UserID;
1799 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1803 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1804 bool bnewtarget =
false;
1806 auto it = AISTargetList.find(mmsi);
1807 if (it == AISTargetList.end())
1809 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1813 pTargetData = it->second;
1817 pTargetData->MMSI = mmsi;
1818 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1819 pTargetData->ShipName[
sizeof(pTargetData->ShipName) - 1] =
'\0';
1820 Name[
sizeof(Name) - 1] = 0;
1821 pTargetData->b_nameValid =
true;
1822 pTargetData->MID = 124;
1824 pTargetData->b_OwnShip =
1826 tN2kAISTranceiverInfo::N2kaisti_Own_information_not_broadcast;
1828 pTargetData->DimA = PosRefBow;
1829 pTargetData->DimB = Length - PosRefBow;
1830 pTargetData->DimC = Beam - PosRefStbd;
1831 pTargetData->DimD = PosRefStbd;
1832 pTargetData->Draft = Draught;
1833 pTargetData->IMO = IMOnumber;
1834 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1835 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1836 pTargetData->ShipType = (
unsigned char)VesselType;
1837 strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1);
1838 pTargetData->Destination[
sizeof(pTargetData->Destination) - 1] =
'\0';
1839 Destination[
sizeof(Destination) - 1] = 0;
1841 if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) {
1842 long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime);
1843 wxDateTime t((time_t)secs);
1845 wxDateTime tz = t.ToUTC();
1846 pTargetData->ETA_Mo = tz.GetMonth() + 1;
1847 pTargetData->ETA_Day = tz.GetDay();
1848 pTargetData->ETA_Hr = tz.GetHour();
1849 pTargetData->ETA_Min = tz.GetMinute();
1853 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1854 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1862bool AisDecoder::HandleN2K_129809(
const N2000MsgPtr &n2k_msg) {
1863 std::vector<unsigned char> v = n2k_msg->payload;
1866 tN2kAISRepeat Repeat;
1870 if (ParseN2kPGN129809(v, MessageID, Repeat, UserID, Name)) {
1871 unsigned mmsi = UserID;
1873 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1877 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1878 bool bnewtarget =
false;
1880 auto it = AISTargetList.find(mmsi);
1881 if (it == AISTargetList.end())
1883 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1887 pTargetData = it->second;
1891 pTargetData->MMSI = mmsi;
1892 Name[
sizeof(Name) - 1] = 0;
1893 strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1);
1894 pTargetData->b_nameValid =
true;
1895 pTargetData->MID = 124;
1897 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
1898 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1908bool AisDecoder::HandleN2K_129810(
const N2000MsgPtr &n2k_msg) {
1909 std::vector<unsigned char> v = n2k_msg->payload;
1912 tN2kAISRepeat Repeat;
1921 uint32_t MothershipID;
1923 if (ParseN2kPGN129810(v, MessageID, Repeat, UserID, VesselType, Vendor,
1924 Callsign, Length, Beam, PosRefStbd, PosRefBow,
1926 unsigned mmsi = UserID;
1928 if (mmsi == g_OwnShipmmsi || IsTargetOnTheIgnoreList(mmsi))
return false;
1932 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1933 bool bnewtarget =
false;
1935 auto it = AISTargetList.find(mmsi);
1936 if (it == AISTargetList.end())
1938 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1942 pTargetData = it->second;
1946 pTargetData->MMSI = mmsi;
1947 pTargetData->DimA = PosRefBow;
1948 pTargetData->DimB = Length - PosRefBow;
1949 pTargetData->DimC = Beam - PosRefStbd;
1950 pTargetData->DimD = PosRefStbd;
1951 strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1);
1952 pTargetData->CallSign[
sizeof(pTargetData->CallSign) - 1] =
'\0';
1953 pTargetData->ShipType = (
unsigned char)VesselType;
1955 pSelectAIS->DeleteSelectablePoint((
void *)(long)mmsi, SELTYPE_AISTARGET);
1956 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
1965bool AisDecoder::HandleN2K_129793(
const N2000MsgPtr &n2k_msg) {
1966 std::vector<unsigned char> v = n2k_msg->payload;
1969 tN2kAISRepeat Repeat;
1973 unsigned int SecondsSinceMidnight;
1974 unsigned int DaysSinceEpoch;
1976 if (ParseN2kPGN129793(v, MessageID, Repeat, UserID, Longitude, Latitude,
1977 SecondsSinceMidnight, DaysSinceEpoch)) {
1978 wxDateTime now = wxDateTime::Now();
1984 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
1985 bool bnewtarget =
false;
1987 auto it = AISTargetList.find(mmsi);
1988 if (it == AISTargetList.end())
1990 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
1994 pTargetData = it->second;
1998 pTargetData->MMSI = mmsi;
1999 pTargetData->Class = AIS_BASE;
2001 if (!N2kIsNA(Longitude)) pTargetData->Lon = Longitude;
2002 if (!N2kIsNA(Latitude)) pTargetData->Lat = Latitude;
2003 pTargetData->b_positionDoubtful =
false;
2004 pTargetData->b_positionOnceValid =
true;
2005 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2006 pTargetData->PositionReportTicks = now.GetTicks();
2010 pSelectAIS->DeleteSelectablePoint((
void *)(
long)mmsi, SELTYPE_AISTARGET);
2011 CommitAISTarget(pTargetData,
"",
true, bnewtarget);
2030void AisDecoder::HandleSignalK(
const SignalKMsgPtr &sK_msg) {
2031 rapidjson::Document root;
2033 root.Parse(sK_msg->raw_message);
2035 if (root.HasParseError())
return;
2037 if (root.HasMember(
"self")) {
2043 if (m_signalk_selfid.IsEmpty()) {
2048 int meteo_SiteID = 0;
2049 if (root.HasMember(
"context") && root[
"context"].IsString()) {
2050 wxString context = root[
"context"].GetString();
2051 if (context == m_signalk_selfid) {
2053 wxLogMessage(_T(
"** Ignore context own ship.."));
2057 wxString mmsi_string;
2058 if (context.StartsWith(_T(
"vessels.urn:mrn:imo:mmsi:"), &mmsi_string) ||
2059 context.StartsWith(_T(
"atons.urn:mrn:imo:mmsi:"), &mmsi_string) ||
2060 context.StartsWith(_T(
"aircraft.urn:mrn:imo:mmsi:"), &mmsi_string)) {
2063 if (mmsi_string.ToLong(&mmsi)) {
2068 }
else if (context.StartsWith(_T(
"meteo.urn:mrn:imo:mmsi:"),
2071 origin_mmsi = wxAtoi(wxString(mmsi_string).BeforeFirst(
':'));
2072 meteo_SiteID = wxAtoi(
'1' + wxString(mmsi_string).AfterFirst(
':'));
2075 int meteo_mmsi = AisMeteoNewMmsi(origin_mmsi, 0, 0, 999, meteo_SiteID);
2086 if (g_pMUX && g_pMUX->IsLogActive()) {
2088 logmsg.Printf(
"AIS :MMSI: %ld", mmsi);
2093 if (IsTargetOnTheIgnoreList(mmsi))
return;
2096 if (mmsi == g_OwnShipmmsi)
return;
2101 writer.
Write(root, dbg);
2103 wxString msg( _T(
"AisDecoder::OnEvtSignalK: ") );
2107 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2108 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2109 bool bnewtarget =
false;
2110 int last_report_ticks;
2112 getAISTarget(mmsi, pTargetData, pStaleTarget, bnewtarget, last_report_ticks,
2115 pTargetData->MMSI = mmsi;
2116 getMmsiProperties(pTargetData);
2117 if (root.HasMember(
"updates") && root[
"updates"].IsArray()) {
2118 for (rapidjson::Value::ConstValueIterator itr = root[
"updates"].Begin();
2119 itr != root[
"updates"].End(); ++itr) {
2120 handleUpdate(pTargetData, bnewtarget, *itr);
2125 if (97 == mmsi / 10000000) {
2126 pTargetData->Class = AIS_SART;
2127 }
else if (1994 == mmsi / 100000) {
2129 pTargetData->Class = AIS_METEO;
2130 pTargetData->met_data.original_mmsi = origin_mmsi;
2131 pTargetData->met_data.stationID = meteo_SiteID;
2134 wxString met_name = pTargetData->ShipName;
2135 if (met_name.Find(
"METEO") == wxNOT_FOUND) {
2138 s_id << meteo_SiteID;
2139 id1 = wxAtoi(s_id.Mid(1, 3));
2140 id2 = wxAtoi(s_id.Mid(4, 3));
2141 met_name =
"METEO ";
2142 met_name << wxString::Format(
"%03d", (id1 + id2)).Right(3);
2143 strncpy(pTargetData->ShipName, met_name, SHIP_NAME_LEN - 1);
2145 pTargetData->b_nameValid =
true;
2146 pTargetData->MID = 123;
2147 pTargetData->COG = -1.;
2148 pTargetData->HDG = 511;
2149 pTargetData->SOG = -1.;
2150 pTargetData->b_NoTrack =
true;
2151 pTargetData->b_show_track =
false;
2153 pTargetData->b_OwnShip =
false;
2154 AISTargetList[pTargetData->MMSI] = pTargetData;
2158void AisDecoder::handleUpdate(
const std::shared_ptr<AisTargetData> &pTargetData,
2159 bool bnewtarget,
const rapidjson::Value &update) {
2160 wxString sfixtime =
"";
2162 if (update.HasMember(
"timestamp")) {
2163 sfixtime = update[
"timestamp"].GetString();
2165 if (update.HasMember(
"values") && update[
"values"].IsArray()) {
2166 for (rapidjson::Value::ConstValueIterator itr = update[
"values"].Begin();
2167 itr != update[
"values"].End(); ++itr) {
2168 updateItem(pTargetData, bnewtarget, *itr, sfixtime);
2171 wxDateTime now = wxDateTime::Now();
2172 pTargetData->m_utc_hour = now.ToUTC().GetHour();
2173 pTargetData->m_utc_min = now.ToUTC().GetMinute();
2174 pTargetData->m_utc_sec = now.ToUTC().GetSecond();
2176 pTargetData->b_active =
true;
2177 pTargetData->b_lost =
false;
2179 if (pTargetData->b_positionOnceValid) {
2180 long mmsi_long = pTargetData->MMSI;
2182 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
2183 (
void *)mmsi_long, SELTYPE_AISTARGET);
2184 pSel->SetUserData(pTargetData->MMSI);
2186 UpdateOneCPA(pTargetData.get());
2187 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
2190void AisDecoder::updateItem(
const std::shared_ptr<AisTargetData> &pTargetData,
2191 bool bnewtarget,
const rapidjson::Value &item,
2192 wxString &sfixtime)
const {
2193 if (item.HasMember(
"path") && item.HasMember(
"value")) {
2194 const wxString &update_path = item[
"path"].GetString();
2195 if (update_path == _T(
"navigation.position")) {
2196 if (item[
"value"].HasMember(
"latitude") &&
2197 item[
"value"].HasMember(
"longitude")) {
2198 wxDateTime now = wxDateTime::Now();
2200 double lat = item[
"value"][
"latitude"].GetDouble();
2201 double lon = item[
"value"][
"longitude"].GetDouble();
2202 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
2203 pTargetData->PositionReportTicks = now.GetTicks();
2204 pTargetData->StaticReportTicks = now.GetTicks();
2205 pTargetData->Lat = lat;
2206 pTargetData->Lon = lon;
2207 pTargetData->b_positionOnceValid =
true;
2208 pTargetData->b_positionDoubtful =
false;
2215 }
else if (update_path == _T(
"navigation.speedOverGround") &&
2216 item[
"value"].IsNumber()) {
2217 pTargetData->SOG = item[
"value"].GetDouble() * ms_to_knot_factor;
2218 }
else if (update_path == _T(
"navigation.courseOverGroundTrue") &&
2219 item[
"value"].IsNumber()) {
2220 pTargetData->COG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2221 }
else if (update_path == _T(
"navigation.headingTrue") &&
2222 item[
"value"].IsNumber()) {
2223 pTargetData->HDG = GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2224 }
else if (update_path == _T(
"navigation.rateOfTurn") &&
2225 item[
"value"].IsNumber()) {
2226 pTargetData->ROTAIS = 4.733 * sqrt(item[
"value"].GetDouble());
2227 }
else if (update_path == _T(
"design.aisShipType")) {
2228 if (item[
"value"].HasMember(
"id")) {
2229 if (!pTargetData->b_isDSCtarget) {
2230 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2233 }
else if (update_path == _T(
"atonType")) {
2234 if (item[
"value"].HasMember(
"id")) {
2235 pTargetData->ShipType = item[
"value"][
"id"].GetUint();
2237 }
else if (update_path == _T(
"virtual")) {
2238 if (item[
"value"].GetBool()) {
2239 pTargetData->NavStatus = ATON_VIRTUAL;
2241 pTargetData->NavStatus = ATON_REAL;
2243 }
else if (update_path == _T(
"offPosition")) {
2244 if (item[
"value"].GetBool()) {
2245 if (ATON_REAL == pTargetData->NavStatus) {
2246 pTargetData->NavStatus = ATON_REAL_OFFPOSITION;
2247 }
else if (ATON_VIRTUAL == pTargetData->NavStatus) {
2248 pTargetData->NavStatus = ATON_VIRTUAL_OFFPOSITION;
2251 }
else if (update_path == _T(
"design.draft")) {
2252 if (item[
"value"].HasMember(
"maximum") &&
2253 item[
"value"][
"maximum"].IsNumber()) {
2254 pTargetData->Draft = item[
"value"][
"maximum"].GetDouble();
2255 pTargetData->Euro_Draft = item[
"value"][
"maximum"].GetDouble();
2257 if (item[
"value"].HasMember(
"current") &&
2258 item[
"value"][
"current"].IsNumber()) {
2259 double draft = item[
"value"][
"current"].GetDouble();
2261 pTargetData->Draft = draft;
2262 pTargetData->Euro_Draft = draft;
2265 }
else if (update_path == _T(
"design.length")) {
2266 if (pTargetData->DimB == 0) {
2267 if (item[
"value"].HasMember(
"overall")) {
2268 if (item[
"value"][
"overall"].IsNumber()) {
2269 pTargetData->Euro_Length = item[
"value"][
"overall"].GetDouble();
2270 pTargetData->DimA = item[
"value"][
"overall"].GetDouble();
2272 pTargetData->DimB = 0;
2275 }
else if (update_path == _T(
"sensors.ais.class")) {
2276 wxString aisclass = item[
"value"].GetString();
2277 if (aisclass == _T(
"A")) {
2278 if (!pTargetData->b_isDSCtarget) pTargetData->Class = AIS_CLASS_A;
2279 }
else if (aisclass == _T(
"B")) {
2280 if (!pTargetData->b_isDSCtarget) {
2281 if (!isBuoyMmsi(pTargetData->MMSI))
2282 pTargetData->Class = AIS_CLASS_B;
2284 pTargetData->Class = AIS_BUOY;
2287 pTargetData->NavStatus = UNDEFINED;
2289 }
else if (aisclass == _T(
"BASE")) {
2290 pTargetData->Class = AIS_BASE;
2291 }
else if (aisclass == _T(
"ATON")) {
2292 pTargetData->Class = AIS_ATON;
2294 }
else if (update_path == _T(
"sensors.ais.fromBow")) {
2295 if (pTargetData->DimB == 0 && pTargetData->DimA != 0) {
2296 int length = pTargetData->DimA;
2297 if (item[
"value"].IsNumber()) {
2298 pTargetData->DimA = item[
"value"].GetDouble();
2299 pTargetData->DimB = length - item[
"value"].GetDouble();
2302 }
else if (update_path == _T(
"design.beam")) {
2303 if (pTargetData->DimD == 0) {
2304 if (item[
"value"].IsNumber()) {
2305 pTargetData->Euro_Beam = item[
"value"].GetDouble();
2306 pTargetData->DimC = item[
"value"].GetDouble();
2308 pTargetData->DimD = 0;
2310 }
else if (update_path == _T(
"sensors.ais.fromCenter")) {
2311 if (pTargetData->DimD == 0 && pTargetData->DimC != 0) {
2312 int beam = pTargetData->DimC;
2313 int center = beam / 2;
2314 if (item[
"value"].IsNumber()) {
2317 pTargetData->DimC = center + item[
"value"].GetDouble();
2318 pTargetData->DimD = beam - pTargetData->DimC;
2321 }
else if (update_path == _T(
"navigation.state")) {
2322 wxString state = item[
"value"].GetString();
2323 if (state == _T(
"motoring")) {
2324 pTargetData->NavStatus = UNDERWAY_USING_ENGINE;
2325 }
else if (state == _T(
"anchored")) {
2326 pTargetData->NavStatus = AT_ANCHOR;
2327 }
else if (state == _T(
"not under command")) {
2328 pTargetData->NavStatus = NOT_UNDER_COMMAND;
2329 }
else if (state == _T(
"restricted manouverability")) {
2330 pTargetData->NavStatus = RESTRICTED_MANOEUVRABILITY;
2331 }
else if (state == _T(
"constrained by draft")) {
2332 pTargetData->NavStatus = CONSTRAINED_BY_DRAFT;
2333 }
else if (state == _T(
"moored")) {
2334 pTargetData->NavStatus = MOORED;
2335 }
else if (state == _T(
"aground")) {
2336 pTargetData->NavStatus = AGROUND;
2337 }
else if (state == _T(
"fishing")) {
2338 pTargetData->NavStatus = FISHING;
2339 }
else if (state == _T(
"sailing")) {
2340 pTargetData->NavStatus = UNDERWAY_SAILING;
2341 }
else if (state == _T(
"hazardous material high speed")) {
2342 pTargetData->NavStatus = HSC;
2343 }
else if (state == _T(
"hazardous material wing in ground")) {
2344 pTargetData->NavStatus = WIG;
2345 }
else if (state == _T(
"ais-sart")) {
2346 pTargetData->NavStatus = RESERVED_14;
2348 pTargetData->NavStatus = UNDEFINED;
2350 }
else if (update_path == _T(
"navigation.destination.commonName")) {
2351 const wxString &destination = item[
"value"].GetString();
2352 pTargetData->Destination[0] =
'\0';
2353 strncpy(pTargetData->Destination, destination.c_str(),
2354 DESTINATION_LEN - 1);
2355 }
else if (update_path == _T(
"navigation.specialManeuver")) {
2356 if (strcmp(
"not available", item[
"value"].GetString()) != 0 &&
2357 pTargetData->IMO < 1) {
2358 const wxString &bluesign = item[
"value"].GetString();
2359 if (_T(
"not engaged") == bluesign) {
2360 pTargetData->blue_paddle = 1;
2362 if (_T(
"engaged") == bluesign) {
2363 pTargetData->blue_paddle = 2;
2365 pTargetData->b_blue_paddle =
2366 pTargetData->blue_paddle == 2 ? true :
false;
2368 }
else if (update_path == _T(
"sensors.ais.designatedAreaCode")) {
2369 if (item[
"value"].GetInt() == 200) {
2370 pTargetData->b_hasInlandDac =
true;
2372 }
else if (update_path == _T(
"sensors.ais.functionalId")) {
2373 if (item[
"value"].GetInt() == 10 && pTargetData->b_hasInlandDac) {
2375 pTargetData->b_isEuroInland =
true;
2379 }
else if (update_path ==
"environment.date") {
2380 wxString issued = item[
"value"].GetString();
2384 ParseGPXDateTime(tz, issued);
2385 pTargetData->met_data.day = tz.GetDay();
2386 pTargetData->met_data.hour = tz.GetHour();
2387 pTargetData->met_data.minute = tz.GetMinute();
2389 }
else if (update_path ==
"environment.wind.averageSpeed" &&
2390 item[
"value"].IsNumber()) {
2391 pTargetData->met_data.wind_kn = MS2KNOTS(item[
"value"].GetDouble());
2392 }
else if (update_path ==
"environment.wind.gust" &&
2393 item[
"value"].IsNumber()) {
2394 pTargetData->met_data.wind_gust_kn = MS2KNOTS(item[
"value"].GetDouble());
2395 }
else if (update_path ==
"environment.wind.directionTrue" &&
2396 item[
"value"].IsNumber()) {
2397 pTargetData->met_data.wind_dir =
2398 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2399 }
else if (update_path ==
"environment.wind.gustDirectionTrue" &&
2400 item[
"value"].IsNumber()) {
2401 pTargetData->met_data.wind_gust_dir =
2402 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2403 }
else if (update_path ==
"environment.outside.temperature" &&
2404 item[
"value"].IsNumber()) {
2405 pTargetData->met_data.air_temp = KelvinToC(item[
"value"].GetDouble());
2406 }
else if (update_path ==
"environment.outside.relativeHumidity" &&
2407 item[
"value"].IsNumber()) {
2408 pTargetData->met_data.rel_humid = item[
"value"].GetDouble();
2409 }
else if (update_path ==
"environment.outside.dewPointTemperature" &&
2410 item[
"value"].IsNumber()) {
2411 pTargetData->met_data.dew_point = KelvinToC(item[
"value"].GetDouble());
2412 }
else if (update_path ==
"environment.outside.pressure" &&
2413 item[
"value"].IsNumber()) {
2414 pTargetData->met_data.airpress =
2415 static_cast<int>(item[
"value"].GetDouble() / 100);
2416 }
else if (update_path ==
"environment.water.level" &&
2417 item[
"value"].IsNumber()) {
2418 pTargetData->met_data.water_lev_dev = item[
"value"].GetDouble();
2419 }
else if (update_path ==
"environment.water.current.drift" &&
2420 item[
"value"].IsNumber()) {
2421 pTargetData->met_data.current = MS2KNOTS(item[
"value"].GetDouble());
2422 }
else if (update_path ==
"environment.water.current.set" &&
2423 item[
"value"].IsNumber()) {
2424 pTargetData->met_data.curr_dir =
2425 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2426 }
else if (update_path ==
"environment.water.levelTendencyValue" &&
2427 item[
"value"].IsNumber()) {
2428 pTargetData->met_data.water_lev_trend =
2429 static_cast<int>(item[
"value"].GetDouble());
2430 }
else if (update_path ==
"environment.water.levelTendency") {
2432 }
else if (update_path ==
"environment.water.waves.significantHeight" &&
2433 item[
"value"].IsNumber()) {
2434 pTargetData->met_data.wave_height = item[
"value"].GetDouble();
2435 }
else if (update_path ==
"environment.water.waves.period" &&
2436 item[
"value"].IsNumber()) {
2437 pTargetData->met_data.wave_period =
2438 static_cast<int>(item[
"value"].GetDouble());
2439 }
else if (update_path ==
"environment.water.waves.directionTrue" &&
2440 item[
"value"].IsNumber()) {
2441 pTargetData->met_data.wave_dir =
2442 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2443 }
else if (update_path ==
"environment.water.swell.height" &&
2444 item[
"value"].IsNumber()) {
2445 pTargetData->met_data.swell_height = item[
"value"].GetDouble();
2446 }
else if (update_path ==
"environment.water.swell.period" &&
2447 item[
"value"].IsNumber()) {
2448 pTargetData->met_data.swell_per =
2449 static_cast<int>(item[
"value"].GetDouble());
2450 }
else if (update_path ==
"environment.water.swell.directionTrue" &&
2451 item[
"value"].IsNumber()) {
2452 pTargetData->met_data.swell_dir =
2453 GEODESIC_RAD2DEG(item[
"value"].GetDouble());
2454 }
else if (update_path ==
"environment.water.temperature" &&
2455 item[
"value"].IsNumber()) {
2456 pTargetData->met_data.water_temp = KelvinToC(item[
"value"].GetDouble());
2457 }
else if (update_path ==
"environment.water.salinity" &&
2458 item[
"value"].IsNumber()) {
2459 pTargetData->met_data.salinity = item[
"value"].GetDouble();
2460 }
else if (update_path ==
"environment.water.ice") {
2462 }
else if (update_path ==
"environment.water.iceValue" &&
2463 item[
"value"].IsNumber()) {
2464 pTargetData->met_data.ice =
static_cast<int>(item[
"value"].GetDouble());
2465 }
else if (update_path ==
"environment.water.seaStateValue" &&
2466 item[
"value"].IsNumber()) {
2467 pTargetData->met_data.seastate =
2468 static_cast<int>(item[
"value"].GetDouble());
2469 }
else if (update_path ==
"environment.water.seaState") {
2471 }
else if (update_path ==
"environment.outside.precipitation") {
2473 }
else if (update_path ==
"environment.outside.precipitationValue" &&
2474 item[
"value"].IsNumber()) {
2475 pTargetData->met_data.precipitation =
2476 static_cast<int>(item[
"value"].GetDouble());
2477 }
else if (update_path ==
"environment.outside.pressureTendencyValue" &&
2478 item[
"value"].IsNumber()) {
2479 pTargetData->met_data.airpress_tend =
2480 static_cast<int>(item[
"value"].GetDouble());
2481 }
else if (update_path ==
"environment.outside.pressureTendency") {
2483 }
else if (update_path ==
"environment.outside.horizontalVisibility" &&
2484 item[
"value"].IsNumber()) {
2485 pTargetData->met_data.hor_vis =
2486 GEODESIC_METERS2NM(item[
"value"].GetDouble());
2487 }
else if (update_path ==
2488 "environment.outside.horizontalVisibility.overRange") {
2489 pTargetData->met_data.hor_vis_GT = item[
"value"].GetBool();
2490 }
else if (update_path.empty()) {
2491 if (item[
"value"].HasMember(
"name")) {
2492 const wxString &name = item[
"value"][
"name"].GetString();
2493 strncpy(pTargetData->ShipName, name.c_str(), SHIP_NAME_LEN - 1);
2494 pTargetData->b_nameValid =
true;
2495 pTargetData->MID = 123;
2496 }
else if (item[
"value"].HasMember(
"registrations")) {
2497 const wxString &imo = item[
"value"][
"registrations"][
"imo"].GetString();
2498 pTargetData->IMO = wxAtoi(imo.Right(7));
2499 }
else if (item[
"value"].HasMember(
"communication")) {
2500 const wxString &callsign =
2501 item[
"value"][
"communication"][
"callsignVhf"].GetString();
2502 strncpy(pTargetData->CallSign, callsign.c_str(), 7);
2504 if (item[
"value"].HasMember(
"mmsi") &&
2505 1994 != (pTargetData->MMSI) / 100000) {
2507 wxString tmp = item[
"value"][
"mmsi"].GetString();
2508 if (tmp.ToLong(&mmsi)) {
2509 pTargetData->MMSI = mmsi;
2511 if (97 == mmsi / 10000000) {
2512 pTargetData->Class = AIS_SART;
2514 if (111 == mmsi / 1000000) {
2515 pTargetData->b_SarAircraftPosnReport =
true;
2518 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
2523 wxLogMessage(wxString::Format(
2524 _T(
"** AisDecoder::updateItem: unhandled path %s"), update_path));
2526 rapidjson::StringBuffer buffer;
2527 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2528 item.Accept(writer);
2529 wxString msg(_T(
"update: "));
2530 msg.append(buffer.GetString());
2540AisError AisDecoder::DecodeSingleVDO(
const wxString &str,
GenericPosDatEx *pos,
2541 wxString *accumulator) {
2543 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2545 if (!NMEACheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
2547 if (!pos)
return AIS_GENERIC_ERROR;
2549 if (!accumulator)
return AIS_GENERIC_ERROR;
2552 if (!str.Mid(1, 5).IsSameAs(_T(
"AIVDO")))
return AIS_GENERIC_ERROR;
2555 wxStringTokenizer tkz(str, _T(
","));
2558 token = tkz.GetNextToken();
2560 token = tkz.GetNextToken();
2561 int nsentences = atoi(token.mb_str());
2563 token = tkz.GetNextToken();
2564 int isentence = atoi(token.mb_str());
2566 token = tkz.GetNextToken();
2567 token = tkz.GetNextToken();
2569 wxString string_to_parse;
2570 string_to_parse.Clear();
2583 if ((1 == nsentences) && (1 == isentence)) {
2584 string_to_parse = tkz.GetNextToken();
2587 else if (nsentences > 1) {
2588 if (1 == isentence) {
2589 *accumulator = tkz.GetNextToken();
2593 accumulator->Append(tkz.GetNextToken());
2596 if (isentence == nsentences) {
2597 string_to_parse = *accumulator;
2601 if (string_to_parse.IsEmpty() &&
2603 return AIS_INCOMPLETE_MULTIPART;
2612 auto TargetData = AisTargetDataMaker::GetInstance().GetTargetData();
2614 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData);
2616 if (bdecode_result) {
2617 switch (TargetData->MID) {
2622 if (!TargetData->b_positionDoubtful) {
2623 pos->
kLat = TargetData->Lat;
2624 pos->
kLon = TargetData->Lon;
2630 if (TargetData->COG == 360.0)
2633 pos->
kCog = TargetData->COG;
2635 if (TargetData->SOG > 102.2)
2638 pos->
kSog = TargetData->SOG;
2640 if ((
int)TargetData->HDG == 511)
2643 pos->
kHdt = TargetData->HDG;
2651 return AIS_GENERIC_ERROR;
2656 return AIS_GENERIC_ERROR;
2664AisError AisDecoder::DecodeN0183(
const wxString &str) {
2665 AisError ret = AIS_GENERIC_ERROR;
2666 wxString string_to_parse;
2668 double gpsg_lat, gpsg_lon, gpsg_mins, gpsg_degs;
2669 double gpsg_cog, gpsg_sog, gpsg_utc_time;
2670 int gpsg_utc_hour = 0;
2671 int gpsg_utc_min = 0;
2672 int gpsg_utc_sec = 0;
2673 char gpsg_name_str[21];
2676 bool bdecode_result =
false;
2683 long arpa_tgt_num = 0;
2684 double arpa_sog = 0.;
2685 double arpa_cog = 0.;
2686 double arpa_lat = 0.;
2687 double arpa_lon = 0.;
2688 double arpa_dist = 0.;
2689 double arpa_brg = 0.;
2690 wxString arpa_brgunit;
2691 wxString arpa_status;
2692 wxString arpa_distunit;
2693 wxString arpa_cogunit;
2694 double arpa_mins, arpa_degs;
2695 double arpa_utc_time;
2696 int arpa_utc_hour = 0;
2697 int arpa_utc_min = 0;
2698 int arpa_utc_sec = 0;
2699 char arpa_name_str[21];
2700 bool arpa_lost =
true;
2701 bool arpa_nottracked =
false;
2703 double aprs_lat = 0.;
2704 double aprs_lon = 0.;
2705 char aprs_name_str[21];
2706 double aprs_mins, aprs_degs;
2708 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
2709 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
2710 bool bnewtarget =
false;
2711 int last_report_ticks;
2715 if (str.Len() > 128)
return AIS_NMEAVDX_TOO_LONG;
2717 if (!NMEACheckSumOK(str)) {
2718 return AIS_NMEAVDX_CHECKSUM_BAD;
2720 if (str.Mid(1, 2).IsSameAs(_T(
"CD"))) {
2723 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
2727 wxStringTokenizer tkz(str,
",*");
2730 token = tkz.GetNextToken();
2731 token = tkz.GetNextToken();
2732 token.ToLong(&arpa_tgt_num);
2733 token = tkz.GetNextToken();
2734 token.ToDouble(&arpa_dist);
2735 token = tkz.GetNextToken();
2736 token.ToDouble(&arpa_brg);
2737 arpa_brgunit = tkz.GetNextToken();
2738 if (arpa_brgunit == _T(
"R")) {
2739 if (std::isnan(arpa_ref_hdg)) {
2740 if (!std::isnan(gHdt))
2745 arpa_brg += arpa_ref_hdg;
2746 if (arpa_brg >= 360.) arpa_brg -= 360.;
2748 token = tkz.GetNextToken();
2749 token.ToDouble(&arpa_sog);
2750 token = tkz.GetNextToken();
2751 token.ToDouble(&arpa_cog);
2752 arpa_cogunit = tkz.GetNextToken();
2753 if (arpa_cogunit == _T(
"R")) {
2754 if (std::isnan(arpa_ref_hdg)) {
2755 if (!std::isnan(gHdt))
2760 arpa_cog += arpa_ref_hdg;
2761 if (arpa_cog >= 360.) arpa_cog -= 360.;
2763 token = tkz.GetNextToken();
2764 token = tkz.GetNextToken();
2766 arpa_distunit = tkz.GetNextToken();
2767 token = tkz.GetNextToken();
2768 if (token == wxEmptyString)
2769 token = wxString::Format(_T(
"ARPA %ld"), arpa_tgt_num);
2770 int len = wxMin(token.Length(), 20);
2771 strncpy(arpa_name_str, token.mb_str(), len);
2772 arpa_name_str[len] = 0;
2773 arpa_status = tkz.GetNextToken();
2774 if (arpa_status != _T(
"L" )) {
2776 }
else if (arpa_status != wxEmptyString)
2777 arpa_nottracked =
true;
2778 wxString arpa_reftarget = tkz.GetNextToken();
2779 if (tkz.HasMoreTokens()) {
2780 token = tkz.GetNextToken();
2781 token.ToDouble(&arpa_utc_time);
2782 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2783 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2785 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2787 arpa_utc_hour = wxDateTime::Now().ToUTC().GetHour();
2788 arpa_utc_min = wxDateTime::Now().ToUTC().GetMinute();
2789 arpa_utc_sec = wxDateTime::Now().ToUTC().GetSecond();
2792 if (arpa_distunit == _T(
"K")) {
2793 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_KM, g_iDistanceFormat);
2794 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_KMH, g_iSpeedFormat);
2795 }
else if (arpa_distunit == _T(
"S")) {
2796 arpa_dist = fromUsrDistance(arpa_dist, DISTANCE_MI, g_iDistanceFormat);
2797 arpa_sog = fromUsrSpeed(arpa_sog, SPEED_MPH, g_iSpeedFormat);
2800 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2804 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
2807 wxStringTokenizer tkz(str,
",*");
2810 wxString aprs_tll_str = tkz.GetNextToken();
2811 token = tkz.GetNextToken();
2812 token.ToLong(&arpa_tgt_num);
2813 token = tkz.GetNextToken();
2814 token.ToDouble(&arpa_lat);
2815 arpa_degs = (int)(arpa_lat / 100.0);
2816 arpa_mins = arpa_lat - arpa_degs * 100.0;
2817 arpa_lat = arpa_degs + arpa_mins / 60.0;
2818 token = tkz.GetNextToken();
2819 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
2820 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
2821 arpa_lat = 0. - arpa_lat;
2822 token = tkz.GetNextToken();
2823 token.ToDouble(&arpa_lon);
2824 arpa_degs = (int)(arpa_lon / 100.0);
2825 arpa_mins = arpa_lon - arpa_degs * 100.0;
2826 arpa_lon = arpa_degs + arpa_mins / 60.0;
2827 token = tkz.GetNextToken();
2828 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
2829 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
2830 arpa_lon = 0. - arpa_lon;
2831 token = tkz.GetNextToken();
2832 if (token == wxEmptyString)
2833 token = wxString::Format(_T(
"ARPA %d"), arpa_tgt_num);
2834 int len = wxMin(token.Length(), 20);
2835 strncpy(arpa_name_str, token.mb_str(), len);
2836 arpa_name_str[len] = 0;
2837 token = tkz.GetNextToken();
2838 token.ToDouble(&arpa_utc_time);
2839 arpa_utc_hour = (int)(arpa_utc_time / 10000.0);
2840 arpa_utc_min = (int)(arpa_utc_time / 100.0) - arpa_utc_hour * 100;
2842 (int)arpa_utc_time - arpa_utc_hour * 10000 - arpa_utc_min * 100;
2847 if (arpa_status != _T(
"L"))
2849 else if (arpa_status != wxEmptyString)
2850 arpa_nottracked =
true;
2851 wxString arpa_reftarget = tkz.GetNextToken();
2852 mmsi = arpa_mmsi = 199200000 + arpa_tgt_num;
2856 }
else if (str.Mid(3, 3).IsSameAs(_T(
"OSD"))) {
2858 wxStringTokenizer tkz(str, _T(
",*"));
2861 token = tkz.GetNextToken();
2862 token = tkz.GetNextToken();
2863 token.ToDouble(&arpa_ref_hdg);
2873 }
else if (g_bWplUsePosition && str.Mid(3, 3).IsSameAs(_T(
"WPL"))) {
2875 wxStringTokenizer tkz(str,
",*");
2878 token = tkz.GetNextToken();
2879 token = tkz.GetNextToken();
2880 token.ToDouble(&aprs_lat);
2881 aprs_degs = (int)(aprs_lat / 100.0);
2882 aprs_mins = aprs_lat - aprs_degs * 100.0;
2883 aprs_lat = aprs_degs + aprs_mins / 60.0;
2884 token = tkz.GetNextToken();
2885 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
2886 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
2887 aprs_lat = 0. - aprs_lat;
2888 token = tkz.GetNextToken();
2889 token.ToDouble(&aprs_lon);
2890 aprs_degs = (int)(aprs_lon / 100.0);
2891 aprs_mins = aprs_lon - aprs_degs * 100.0;
2892 aprs_lon = aprs_degs + aprs_mins / 60.0;
2893 token = tkz.GetNextToken();
2894 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
2895 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
2896 aprs_lon = 0. - aprs_lon;
2897 token = tkz.GetNextToken();
2898 int len = wxMin(token.Length(), 20);
2899 strncpy(aprs_name_str, token.mb_str(), len + 1);
2900 if (0 == g_WplAction) {
2902 aprs_name_str[len] = 0;
2903 for (i = 0; i < len; i++) {
2905 hash += (int)(aprs_name_str[i]);
2906 while (hash >= 100000) hash = hash / 100000;
2908 mmsi = aprs_mmsi = 199300000 + hash;
2912 }
else if (1 == g_WplAction) {
2913 auto *pWP =
new RoutePoint(aprs_lat, aprs_lon, wxEmptyString,
2914 aprs_name_str, wxEmptyString,
false);
2915 pWP->m_bIsolatedMark =
true;
2916 InsertWpt(pWP,
true);
2918 }
else if (str.Mid(1, 5).IsSameAs(_T(
"FRPOS"))) {
2922 wxStringTokenizer tkz(str,
",*");
2925 token = tkz.GetNextToken();
2927 token = tkz.GetNextToken();
2928 token.ToDouble(&gpsg_lat);
2929 gpsg_degs = (int)(gpsg_lat / 100.0);
2930 gpsg_mins = gpsg_lat - gpsg_degs * 100.0;
2931 gpsg_lat = gpsg_degs + gpsg_mins / 60.0;
2933 token = tkz.GetNextToken();
2934 if (token.Mid(0, 1).Contains(_T(
"S")) ==
true ||
2935 token.Mid(0, 1).Contains(_T(
"s")) ==
true)
2936 gpsg_lat = 0. - gpsg_lat;
2938 token = tkz.GetNextToken();
2939 token.ToDouble(&gpsg_lon);
2940 gpsg_degs = (int)(gpsg_lon / 100.0);
2941 gpsg_mins = gpsg_lon - gpsg_degs * 100.0;
2942 gpsg_lon = gpsg_degs + gpsg_mins / 60.0;
2944 token = tkz.GetNextToken();
2945 if (token.Mid(0, 1).Contains(_T(
"W")) ==
true ||
2946 token.Mid(0, 1).Contains(_T(
"w")) ==
true)
2947 gpsg_lon = 0. - gpsg_lon;
2949 token = tkz.GetNextToken();
2952 token = tkz.GetNextToken();
2953 token.ToDouble(&gpsg_sog);
2955 token = tkz.GetNextToken();
2956 token.ToDouble(&gpsg_cog);
2958 token = tkz.GetNextToken();
2961 token = tkz.GetNextToken();
2962 token.ToDouble(&gpsg_utc_time);
2963 gpsg_utc_hour = (int)(gpsg_utc_time / 10000.0);
2964 gpsg_utc_min = (int)(gpsg_utc_time / 100.0) - gpsg_utc_hour * 100;
2966 (int)gpsg_utc_time - gpsg_utc_hour * 10000 - gpsg_utc_min * 100;
2970 token = tkz.GetNextToken();
2971 int i, len, hash = 0;
2972 len = wxMin(wxStrlen(token), 20);
2973 strncpy(gpsg_name_str, token.mb_str(), len);
2974 gpsg_name_str[len] = 0;
2975 for (i = 0; i < len; i++) {
2977 hash += (int)(token[i]);
2978 while (hash >= 100000) hash = hash / 100000;
2981 gpsg_mmsi = 199000000 + hash;
2983 }
else if (!str.Mid(3, 2).IsSameAs(_T(
"VD"))) {
2984 return AIS_NMEAVDX_BAD;
2990 string_to_parse.Clear();
2992 if (str.Mid(3, 2).IsSameAs(_T(
"VD"))) {
2993 wxStringTokenizer tkz(str,
",");
2996 token = tkz.GetNextToken();
2998 token = tkz.GetNextToken();
2999 nsentences = atoi(token.mb_str());
3001 token = tkz.GetNextToken();
3002 isentence = atoi(token.mb_str());
3004 token = tkz.GetNextToken();
3005 long lsequence_id = 0;
3006 token.ToLong(&lsequence_id);
3008 token = tkz.GetNextToken();
3010 token.ToLong(&lchannel);
3015 if ((1 == nsentences) && (1 == isentence)) {
3016 string_to_parse = tkz.GetNextToken();
3019 else if (nsentences > 1) {
3020 if (1 == isentence) {
3021 sentence_accumulator = tkz.GetNextToken();
3025 sentence_accumulator += tkz.GetNextToken();
3028 if (isentence == nsentences) {
3029 string_to_parse = sentence_accumulator;
3034 if (mmsi || (!string_to_parse.IsEmpty() &&
3035 (string_to_parse.Len() < AIS_MAX_MESSAGE_LEN))) {
3037 wxCharBuffer abuf = string_to_parse.ToUTF8();
3039 return AIS_GENERIC_ERROR;
3044 if (!mmsi) mmsi = strbit.GetInt(9, 30);
3045 long mmsi_long = mmsi;
3048 int origin_mmsi = 0;
3049 int messID = strbit.GetInt(1, 6);
3050 int dac = strbit.GetInt(41, 10);
3051 int fi = strbit.GetInt(51, 6);
3053 int met_lon, met_lat;
3054 if (dac == 001 && fi == 31) {
3056 met_lon = strbit.GetInt(57, 25);
3057 met_lat = strbit.GetInt(82, 24);
3058 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 25, 0);
3061 }
else if (dac == 367 && fi == 33) {
3063 const int size = strbit.GetBitCount();
3064 if (size < 168)
return AIS_GENERIC_ERROR;
3065 const int startb = 56;
3066 const int slot_size = 112;
3067 const int extra_bits = (size - startb) % slot_size;
3068 if (extra_bits > 0)
return AIS_GENERIC_ERROR;
3070 int mes_type = strbit.GetInt(57, 4);
3071 int site_ID = strbit.GetInt(77, 7);
3072 if (mes_type == 0) {
3074 met_lon = strbit.GetInt(90, 28);
3075 met_lat = strbit.GetInt(118, 27);
3076 mmsi = AisMeteoNewMmsi(mmsi, met_lat, met_lon, 28, site_ID);
3081 int x_mmsi = AisMeteoNewMmsi(mmsi, 91, 181, 0, site_ID);
3087 return AIS_GENERIC_ERROR;
3093 if (mmsi == g_OwnShipmmsi)
return AIS_GENERIC_ERROR;
3096 auto it = AISTargetList.find(mmsi);
3097 if (it == AISTargetList.end())
3099 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3104 pTargetData->MMSI = mmsi;
3105 pTargetData->met_data.original_mmsi = origin_mmsi;
3108 pTargetData = it->second;
3111 pStaleTarget = pTargetData;
3113 pTargetData->MMSI = mmsi;
3114 pTargetData->met_data.original_mmsi = origin_mmsi;
3117 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3119 if (mmsi ==
static_cast<unsigned>(props->MMSI)) {
3121 if (TRACKTYPE_NEVER == props->TrackType) {
3122 pTargetData->b_show_track =
false;
3123 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
3124 pTargetData->b_show_track =
true;
3129 if (props->m_bignore)
return AIS_NoError;
3132 else if (props->m_bVDM) {
3134 if (str.Mid(3, 9).IsSameAs(wxT(
"VDM,1,1,,"))) {
3135 int message_ID = strbit.GetInt(1, 6);
3138 if ((message_ID <= 3) || (message_ID == 18)) {
3140 pTargetData->b_OwnShip =
true;
3142 wxString aivdostr = str;
3143 aivdostr.replace(1, 5,
"AIVDO");
3144 unsigned char calculated_checksum = 0;
3145 wxString::iterator j;
3146 for (j = aivdostr.begin() + 1; j != aivdostr.end() && *j !=
'*';
3148 calculated_checksum ^=
static_cast<unsigned char>(*j);
3151 if (j <= aivdostr.end() - 3)
3154 wxString::Format(_(
"%02X"), calculated_checksum));
3156 gps_watchdog_timeout_ticks =
3159 std::string full_sentence = aivdostr.ToStdString();
3160 auto address = std::make_shared<NavAddr0183>(
"virtual");
3163 auto msg = std::make_shared<const Nmea0183Msg>(
3164 "AIVDO", full_sentence, address);
3165 NavMsgBus::GetInstance().
Notify(std::move(msg));
3175 wxDateTime now = wxDateTime::Now();
3179 last_report_ticks = pStaleTarget->PositionReportTicks;
3181 last_report_ticks = now.GetTicks();
3185 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3189 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3190 pTargetData->PositionReportTicks = now.GetTicks();
3191 pTargetData->StaticReportTicks = now.GetTicks();
3192 pTargetData->m_utc_hour = gpsg_utc_hour;
3193 pTargetData->m_utc_min = gpsg_utc_min;
3194 pTargetData->m_utc_sec = gpsg_utc_sec;
3195 pTargetData->m_date_string = gpsg_date;
3196 pTargetData->MMSI = gpsg_mmsi;
3197 pTargetData->NavStatus = 0;
3198 pTargetData->Lat = gpsg_lat;
3199 pTargetData->Lon = gpsg_lon;
3200 pTargetData->b_positionOnceValid =
true;
3201 pTargetData->COG = gpsg_cog;
3202 pTargetData->SOG = gpsg_sog;
3203 pTargetData->ShipType = 52;
3204 pTargetData->Class = AIS_GPSG_BUDDY;
3205 memcpy(pTargetData->ShipName, gpsg_name_str,
sizeof(gpsg_name_str));
3206 pTargetData->b_nameValid =
true;
3207 pTargetData->b_active =
true;
3208 pTargetData->b_lost =
false;
3210 bdecode_result =
true;
3211 }
else if (arpa_mmsi) {
3212 pTargetData->m_utc_hour = arpa_utc_hour;
3213 pTargetData->m_utc_min = arpa_utc_min;
3214 pTargetData->m_utc_sec = arpa_utc_sec;
3215 pTargetData->MMSI = arpa_mmsi;
3216 pTargetData->NavStatus = 15;
3217 if (str.Mid(3, 3).IsSameAs(_T(
"TLL"))) {
3220 (now.GetTicks() - pTargetData->PositionReportTicks);
3221 if (age_of_last > 0) {
3222 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, arpa_lat,
3223 arpa_lon, &pTargetData->COG, &pTargetData->SOG);
3224 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3227 pTargetData->Lat = arpa_lat;
3228 pTargetData->Lon = arpa_lon;
3229 }
else if (str.Mid(3, 3).IsSameAs(_T(
"TTM"))) {
3230 if (arpa_dist != 0.)
3231 ll_gc_ll(gLat, gLon, arpa_brg, arpa_dist, &pTargetData->Lat,
3235 pTargetData->COG = arpa_cog;
3236 pTargetData->SOG = arpa_sog;
3238 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3239 pTargetData->PositionReportTicks = now.GetTicks();
3240 pTargetData->StaticReportTicks = now.GetTicks();
3241 pTargetData->b_positionOnceValid =
true;
3242 pTargetData->ShipType = 55;
3243 pTargetData->Class = AIS_ARPA;
3245 memcpy(pTargetData->ShipName, arpa_name_str,
sizeof(arpa_name_str));
3246 if (arpa_status != _T(
"Q"))
3247 pTargetData->b_nameValid =
true;
3249 pTargetData->b_nameValid =
false;
3250 pTargetData->b_active = !arpa_lost;
3251 pTargetData->b_lost = arpa_nottracked;
3253 bdecode_result =
true;
3254 }
else if (aprs_mmsi) {
3255 pTargetData->m_utc_hour = now.GetHour();
3256 pTargetData->m_utc_min = now.GetMinute();
3257 pTargetData->m_utc_sec = now.GetSecond();
3258 pTargetData->MMSI = aprs_mmsi;
3259 pTargetData->NavStatus = 15;
3261 int age_of_last = (now.GetTicks() - pTargetData->PositionReportTicks);
3262 if (age_of_last > 0) {
3263 ll_gc_ll_reverse(pTargetData->Lat, pTargetData->Lon, aprs_lat,
3264 aprs_lon, &pTargetData->COG, &pTargetData->SOG);
3265 pTargetData->SOG = pTargetData->SOG * 3600 / age_of_last;
3268 pTargetData->LastPositionReportTicks = pTargetData->PositionReportTicks;
3269 pTargetData->PositionReportTicks = now.GetTicks();
3270 pTargetData->StaticReportTicks = now.GetTicks();
3271 pTargetData->Lat = aprs_lat;
3272 pTargetData->Lon = aprs_lon;
3273 pTargetData->b_positionOnceValid =
true;
3274 pTargetData->ShipType = 56;
3275 pTargetData->Class = AIS_APRS;
3276 memcpy(pTargetData->ShipName, aprs_name_str,
sizeof(aprs_name_str));
3277 pTargetData->b_nameValid =
true;
3278 pTargetData->b_active =
true;
3279 pTargetData->b_lost =
false;
3281 bdecode_result =
true;
3285 Parse_VDXBitstring(&strbit, pTargetData);
3289 getMmsiProperties(pTargetData);
3292 pTargetData->RecentPeriod =
3293 pTargetData->PositionReportTicks - last_report_ticks;
3298 pTargetData =
nullptr;
3303 CommitAISTarget(pTargetData, str, bdecode_result, bnewtarget);
3308 if ((n_msgs % 10000) == 0)
3309 printf(
"n_msgs %10d m_n_targets: %6d n_msg1: %10d n_msg5+24: %10d \n",
3310 n_msgs, m_n_targets, n_msg1, n_msg5 + n_msg24);
3316void AisDecoder::CommitAISTarget(
3317 const std::shared_ptr<AisTargetData> &pTargetData,
const wxString &str,
3318 bool message_valid,
bool new_target) {
3319 m_pLatestTargetData = pTargetData;
3321 if (!str.IsEmpty()) {
3322 if (str.Mid(3, 3).IsSameAs(_T(
"VDO")))
3323 pTargetData->b_OwnShip =
true;
3325 pTargetData->b_OwnShip =
false;
3328 if (!pTargetData->b_OwnShip) {
3330 if (0 == m_persistent_tracks.count(pTargetData->MMSI)) {
3332 pTargetData->b_PersistTrack =
false;
3334 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3335 if (pTargetData->MMSI == g_MMSI_Props_Array[i]->MMSI) {
3337 pTargetData->b_mPropPersistTrack = props->m_bPersistentTrack;
3344 pTargetData->b_NoTrack =
false;
3349 if (message_valid) {
3351 if (pTargetData->MMSI) {
3352 AISshipNameCache(pTargetData.get(), AISTargetNamesC, AISTargetNamesNC,
3355 AISTargetList[pTargetData->MMSI] =
3358 if (!pTargetData->area_notices.empty()) {
3359 auto it = AIS_AreaNotice_Sources.find(pTargetData->MMSI);
3360 if (it == AIS_AreaNotice_Sources.end())
3361 AIS_AreaNotice_Sources[pTargetData->MMSI] = pTargetData;
3366 if (!pTargetData->b_OwnShip) {
3367 if (pTargetData->b_positionOnceValid) {
3368 long mmsi_long = pTargetData->MMSI;
3369 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
3370 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3372 pSel->SetUserData(pTargetData->MMSI);
3376 UpdateOneCPA(pTargetData.get());
3379 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3392 if (!pTargetData->b_OwnShip) {
3393 if (pTargetData->b_positionOnceValid) {
3394 long mmsi_long = pTargetData->MMSI;
3395 SelectItem *pSel = pSelectAIS->AddSelectablePoint(
3396 pTargetData->Lat, pTargetData->Lon, (
void *)mmsi_long,
3398 pSel->SetUserData(pTargetData->MMSI);
3405void AisDecoder::getAISTarget(
long mmsi,
3406 std::shared_ptr<AisTargetData> &pTargetData,
3407 std::shared_ptr<AisTargetData> &pStaleTarget,
3408 bool &bnewtarget,
int &last_report_ticks,
3410 now = wxDateTime::Now();
3411 auto it = AISTargetList.find(mmsi);
3412 if (it == AISTargetList.end())
3414 pTargetData = AisTargetDataMaker::GetInstance().GetTargetData();
3418 pTargetData = it->second;
3419 pStaleTarget = pTargetData;
3426 last_report_ticks = pStaleTarget->PositionReportTicks;
3428 last_report_ticks = now.GetTicks();
3432 pSelectAIS->DeleteSelectablePoint((
void *)mmsi, SELTYPE_AISTARGET);
3435std::shared_ptr<AisTargetData> AisDecoder::ProcessDSx(
const wxString &str,
3437 double dsc_lat = 0.;
3438 double dsc_lon = 0.;
3439 double dsc_mins, dsc_degs, dsc_tmp, dsc_addr;
3441 double dse_lat = 0.;
3442 double dse_lon = 0.;
3443 long dsc_fmt, dsc_quadrant, dsc_cat, dsc_nature;
3446 int dsc_tx_mmsi = 0;
3448 double dse_cog = 0.;
3449 double dse_sog = 0.;
3450 wxString dse_shipName = wxEmptyString;
3455 std::shared_ptr<AisTargetData> pTargetData =
nullptr;
3459 wxStringTokenizer tkz(str,
",*");
3462 token = tkz.GetNextToken();
3464 if (str.Mid(3, 3).IsSameAs(_T(
"DSC"))) {
3465 m_dsc_last_string = str;
3467 token = tkz.GetNextToken();
3471 token = tkz.GetNextToken();
3473 if (dsc_fmt == 12 || dsc_fmt == 16) {
3474 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3476 token.ToDouble(&dsc_addr);
3477 dsc_mmsi = 0 - (int)(dsc_addr / 10);
3480 token = tkz.GetNextToken();
3481 token.ToLong(&dsc_cat);
3483 token = tkz.GetNextToken();
3484 if (!token.IsSameAs(wxEmptyString)) {
3485 token.ToLong(&dsc_nature);
3489 token = tkz.GetNextToken();
3491 token = tkz.GetNextToken();
3492 token.ToDouble(&dsc_tmp);
3494 token = tkz.GetNextToken();
3495 token = tkz.GetNextToken();
3496 if (dsc_fmt == 16 && dsc_cat == 12 && !token.IsSameAs(wxEmptyString)) {
3498 dsc_tx_mmsi = dsc_mmsi;
3499 dsc_mmsi = wxAtoi(token.Mid(0, 9));
3501 token = tkz.GetNextToken();
3502 if (dsc_fmt == 16 && dsc_cat == 12) {
3503 if (!token.IsSameAs(wxEmptyString)) {
3504 token.ToLong(&dsc_nature);
3508 token = tkz.GetNextToken();
3509 token = tkz.GetNextToken();
3511 dsc_quadrant = (int)(dsc_tmp / 1000000000.0);
3513 if (dsc_quadrant > 3)
3516 dsc_lat = (int)(dsc_tmp / 100000.0);
3517 dsc_lon = dsc_tmp - dsc_lat * 100000.0;
3518 dsc_lat = dsc_lat - dsc_quadrant * 10000;
3519 dsc_degs = (int)(dsc_lat / 100.0);
3520 dsc_mins = dsc_lat - dsc_degs * 100.0;
3521 dsc_lat = dsc_degs + dsc_mins / 60.0;
3523 dsc_degs = (int)(dsc_lon / 100.0);
3524 dsc_mins = dsc_lon - dsc_degs * 100.0;
3525 dsc_lon = dsc_degs + dsc_mins / 60.0;
3526 switch (dsc_quadrant) {
3542 if (dsc_fmt != 02) mmsi = (int)dsc_mmsi;
3544 }
else if (str.Mid(3, 3).IsSameAs(_T(
"DSE"))) {
3545 token = tkz.GetNextToken();
3546 token = tkz.GetNextToken();
3547 token = tkz.GetNextToken();
3548 token = tkz.GetNextToken();
3549 dse_mmsi = wxAtoi(token.Mid(
3555 token = tkz.GetNextToken();
3558 token.ToDouble(&dse_tmp);
3559 dse_lat = (int)(dse_tmp / 10000.0);
3560 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3561 dse_lat = dse_lat / 600000.0;
3562 dse_lon = dse_lon / 600000.0;
3565 while (tkz.HasMoreTokens()) {
3566 dseSymbol = tkz.GetNextToken();
3567 token = tkz.GetNextToken();
3568 if (dseSymbol.IsSameAs(_T(
"00"))) {
3569 token.ToDouble(&dse_tmp);
3570 dse_lat = (int)(dse_tmp / 10000.0);
3571 dse_lon = (int)(dse_tmp - dse_lat * 10000.0);
3572 dse_lat = dse_lat / 600000.0;
3573 dse_lon = dse_lon / 600000.0;
3574 }
else if (dseSymbol.IsSameAs(_T(
"01"))) {
3575 }
else if (dseSymbol.IsSameAs(_T(
"02"))) {
3576 token.ToDouble(&dse_tmp);
3577 dse_sog = dse_tmp / 10.0;
3578 }
else if (dseSymbol.IsSameAs(_T(
"03"))) {
3579 token.ToDouble(&dse_tmp);
3580 dse_cog = dse_tmp / 10.0;
3581 }
else if (dseSymbol.IsSameAs(_T(
"04"))) {
3582 dse_shipName = DecodeDSEExpansionCharacters(token);
3583 }
else if (dseSymbol.IsSameAs(_T(
"05"))) {
3584 }
else if (dseSymbol.IsSameAs(_T(
"06"))) {
3587 mmsi = abs((
int)dse_mmsi);
3591 wxDateTime now = wxDateTime::Now();
3593 int last_report_ticks = now.GetTicks();
3596 auto it = AISTargetList.find(mmsi);
3597 std::shared_ptr<AisTargetData> pStaleTarget =
nullptr;
3598 if (it == AISTargetList.end()) {
3600 pStaleTarget = it->second;
3601 last_report_ticks = pStaleTarget->PositionReportTicks;
3607 m_ptentative_dsctarget = AisTargetDataMaker::GetInstance().GetTargetData();
3609 m_ptentative_dsctarget->PositionReportTicks = now.GetTicks();
3610 m_ptentative_dsctarget->StaticReportTicks = now.GetTicks();
3612 m_ptentative_dsctarget->MMSI = mmsi;
3613 m_ptentative_dsctarget->NavStatus =
3615 m_ptentative_dsctarget->Lat = dsc_lat;
3616 m_ptentative_dsctarget->Lon = dsc_lon;
3617 m_ptentative_dsctarget->b_positionOnceValid =
true;
3618 m_ptentative_dsctarget->COG = 0;
3619 m_ptentative_dsctarget->SOG = 0;
3620 m_ptentative_dsctarget->ShipType = dsc_fmt;
3621 m_ptentative_dsctarget->Class = AIS_DSC;
3622 m_ptentative_dsctarget->b_isDSCtarget =
true;
3623 m_ptentative_dsctarget->b_nameValid =
true;
3624 if (dsc_fmt == 12 || (dsc_fmt == 16 && dsc_cat == 12)) {
3625 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"DISTRESS %u",
3627 m_ptentative_dsctarget->m_dscNature = int(dsc_nature);
3628 m_ptentative_dsctarget->m_dscTXmmsi = dsc_tx_mmsi;
3630 snprintf(m_ptentative_dsctarget->ShipName, SHIP_NAME_LEN,
"POSITION %u",
3634 m_ptentative_dsctarget->b_active =
true;
3635 m_ptentative_dsctarget->b_lost =
false;
3636 m_ptentative_dsctarget->RecentPeriod =
3637 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3640 if (!b_take_dsc) m_dsc_timer.Start(1000, wxTIMER_ONE_SHOT);
3645 if (dse_mmsi || b_take_dsc) {
3646 if (m_ptentative_dsctarget) {
3652 m_ptentative_dsctarget->Lat =
3653 m_ptentative_dsctarget->Lat +
3654 ((m_ptentative_dsctarget->Lat) >= 0 ? dse_lat : -dse_lat);
3655 m_ptentative_dsctarget->Lon =
3656 m_ptentative_dsctarget->Lon +
3657 ((m_ptentative_dsctarget->Lon) >= 0 ? dse_lon : -dse_lon);
3658 if (!dse_shipName.empty()) {
3659 memset(m_ptentative_dsctarget->ShipName,
'\0', SHIP_NAME_LEN);
3660 snprintf(m_ptentative_dsctarget->ShipName, dse_shipName.length(),
3661 "%s", dse_shipName.ToAscii().data());
3663 m_ptentative_dsctarget->COG = dse_cog;
3664 m_ptentative_dsctarget->SOG = dse_sog;
3668 m_ptentative_dsctarget->RecentPeriod =
3669 m_ptentative_dsctarget->PositionReportTicks - last_report_ticks;
3674 auto found = AISTargetList.find(mmsi);
3675 if (found == AISTargetList.end()) {
3676 pTargetData = m_ptentative_dsctarget;
3678 pTargetData = found->second;
3679 std::vector<AISTargetTrackPoint> ptrack =
3680 std::move(pTargetData->m_ptrack);
3681 pTargetData->CloneFrom(
3682 m_ptentative_dsctarget
3685 pTargetData->m_ptrack =
3692 m_ptentative_dsctarget =
nullptr;
3694 m_pLatestTargetData = pTargetData;
3696 AISTargetList[pTargetData->MMSI] =
3699 long mmsi_long = pTargetData->MMSI;
3703 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
3706 pSelectAIS->AddSelectablePoint(pTargetData->Lat, pTargetData->Lon,
3707 (
void *)mmsi_long, SELTYPE_AISTARGET);
3708 pSel->SetUserData(pTargetData->MMSI);
3711 UpdateOneCPA(pTargetData.get());
3714 if (pTargetData->b_show_track) UpdateOneTrack(pTargetData.get());
3721bool AisDecoder::NMEACheckSumOK(
const wxString &str_in) {
3722 unsigned char checksum_value = 0;
3723 int sentence_hex_sum;
3725 wxCharBuffer buf = str_in.ToUTF8();
3726 if (!buf.data())
return false;
3728 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
3729 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
3730 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
3732 int string_length = strlen(str_ascii);
3734 int payload_length = 0;
3735 while ((payload_length < string_length) &&
3736 (str_ascii[payload_length] !=
'*'))
3739 if (payload_length == string_length)
3744 while (index < payload_length) {
3745 checksum_value ^= str_ascii[index];
3749 if (string_length > 4) {
3751 scanstr[0] = str_ascii[payload_length + 1];
3752 scanstr[1] = str_ascii[payload_length + 2];
3754 sscanf(scanstr,
"%2x", &sentence_hex_sum);
3756 if (sentence_hex_sum == checksum_value)
return true;
3762void AisDecoder::UpdateAllCPA() {
3764 for (
const auto &it : GetTargetList()) {
3765 std::shared_ptr<AisTargetData> td = it.second;
3767 if (
nullptr != td) UpdateOneCPA(td.get());
3771void AisDecoder::UpdateAllTracks() {
3772 for (
const auto &it : GetTargetList()) {
3773 std::shared_ptr<AisTargetData> td = it.second;
3774 if (td) UpdateOneTrack(td.get());
3780 if (!ptarget->b_positionOnceValid)
return;
3782 if (!ptarget->m_ptrack.empty()) {
3784 if (fabs(LastTrackpoint.m_lat - ptarget->Lat) > .1 ||
3785 fabs(LastTrackpoint.m_lon - ptarget->Lon) > .1) {
3788 ptarget->m_ptrack.pop_back();
3789 ptarget->b_positionDoubtful =
true;
3796 if ((ptarget->PositionReportTicks - ptarget->LastPositionReportTicks) > 2) {
3799 ptrackpoint.m_lat = ptarget->Lat;
3800 ptrackpoint.m_lon = ptarget->Lon;
3801 ptrackpoint.m_time = wxDateTime::Now().GetTicks();
3803 ptarget->m_ptrack.push_back(ptrackpoint);
3805 if (ptarget->b_PersistTrack || ptarget->b_mPropPersistTrack) {
3807 if (0 == m_persistent_tracks.count(ptarget->MMSI)) {
3809 t->SetName(wxString::Format(
3810 _T(
"AIS %s (%u) %s %s"), ptarget->GetFullName().c_str(),
3811 ptarget->MMSI, wxDateTime::Now().FormatISODate().c_str(),
3812 wxDateTime::Now().FormatISOTime().c_str()));
3813 g_TrackList.push_back(t);
3815 m_persistent_tracks[ptarget->MMSI] = t;
3817 t = m_persistent_tracks[ptarget->MMSI];
3820 vector2D point(ptrackpoint.m_lon, ptrackpoint.m_lat);
3822 t->AddNewPoint(point, wxDateTime(ptrackpoint.m_time).ToUTC());
3825 pSelect->AddSelectableTrackSegment(tp->m_lat, tp->m_lon, tp1->m_lat,
3826 tp1->m_lon, tp, tp1, t);
3836 wxDateTime::Now().GetTicks() - (time_t)(g_AISShowTracks_Mins * 60);
3838 ptarget->m_ptrack.erase(
3839 std::remove_if(ptarget->m_ptrack.begin(), ptarget->m_ptrack.end(),
3841 return track.m_time < test_time;
3843 ptarget->m_ptrack.end());
3848void AisDecoder::DeletePersistentTrack(
const Track *track) {
3849 for (
auto it = m_persistent_tracks.begin(); it != m_persistent_tracks.end();
3851 if (it->second == track) {
3852 unsigned mmsi = it->first;
3853 m_persistent_tracks.erase(it);
3855 if (0 == m_persistent_tracks.count(mmsi)) {
3856 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
3857 if (mmsi ==
static_cast<unsigned>(g_MMSI_Props_Array[i]->MMSI)) {
3859 if (props->m_bPersistentTrack) {
3863 std::shared_ptr<AisTargetData> td =
3864 Get_Target_Data_From_MMSI(mmsi);
3866 props->m_bPersistentTrack =
false;
3867 td->b_mPropPersistTrack =
false;
3869 if (!m_callbacks.confirm_stop_track()) {
3870 props->m_bPersistentTrack =
true;
3882void AisDecoder::UpdateAllAlarms() {
3883 m_bGeneralAlert =
false;
3886 for (
const auto &it : GetTargetList()) {
3887 std::shared_ptr<AisTargetData> td = it.second;
3891 if (!m_bGeneralAlert) {
3893 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3894 (td->Class != AIS_ATON) && (td->Class != AIS_BASE))
3895 m_bGeneralAlert =
true;
3898 if (g_bAIS_CPA_Alert_Suppress_Moored && (td->SOG <= g_ShowMoored_Kts))
3899 m_bGeneralAlert =
false;
3902 if ((g_bCPAMax) && (td->Range_NM > g_CPAMax_NM))
3903 m_bGeneralAlert =
false;
3906 if ((g_bTCPA_Max) && (td->TCPA > g_TCPA_Max)) m_bGeneralAlert =
false;
3909 if (td->Class == AIS_SART && td->NavStatus == 14)
3910 m_bGeneralAlert =
true;
3913 if ((td->Class == AIS_DSC) &&
3914 ((td->ShipType == 12) || (td->ShipType == 16)))
3915 m_bGeneralAlert =
true;
3918 ais_alert_type this_alarm = AIS_NO_ALERT;
3921 if (td->Class == AIS_SART && td->NavStatus == 14)
3922 this_alarm = AIS_ALERT_SET;
3925 if ((td->Class == AIS_DSC) &&
3926 ((td->ShipType == 12) || (td->ShipType == 16)))
3927 this_alarm = AIS_ALERT_SET;
3929 if (g_bCPAWarn && td->b_active && td->b_positionOnceValid &&
3930 (td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
3933 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts)) {
3934 td->n_alert_state = AIS_NO_ALERT;
3940 if (g_bAIS_CPA_Alert_Suppress_Moored &&
3941 (td->SOG <= g_ShowMoored_Kts)) {
3942 td->n_alert_state = AIS_NO_ALERT;
3948 if (td->Range_NM > g_CPAMax_NM) {
3949 td->n_alert_state = AIS_NO_ALERT;
3954 if ((td->CPA < g_CPAWarn_NM) && (td->TCPA > 0) &&
3955 (td->Class != AIS_ATON) && (td->Class != AIS_BASE) &&
3956 (td->Class != AIS_METEO)) {
3958 if (td->TCPA < g_TCPA_Max) {
3959 if (td->b_isFollower)
3960 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3962 this_alarm = AIS_ALERT_SET;
3965 if (td->b_isFollower)
3966 this_alarm = AIS_ALERT_NO_DIALOG_SET;
3968 this_alarm = AIS_ALERT_SET;
3976 if (g_bAIS_ACK_Timeout || (td->Class == AIS_SART) ||
3977 ((td->Class == AIS_DSC) &&
3978 ((td->ShipType == 12) || (td->ShipType == 16)))) {
3979 if (td->b_in_ack_timeout) {
3980 wxTimeSpan delta = wxDateTime::Now() - td->m_ack_time;
3981 if (delta.GetMinutes() > g_AckTimeout_Mins)
3982 td->b_in_ack_timeout =
false;
3988 if (td->b_in_ack_timeout) {
3989 if (this_alarm == AIS_NO_ALERT) td->b_in_ack_timeout =
false;
3993 td->n_alert_state = this_alarm;
3999 ptarget->Range_NM = -1.;
4008 DistanceBearingMercator(ptarget->Lat, ptarget->Lon, gLat, gLon, &brg, &dist);
4009 ptarget->Range_NM = dist;
4012 if (dist <= 1e-5) ptarget->Brg = -1.0;
4014 if (!ptarget->b_positionOnceValid || !bGPSValid) {
4015 ptarget->bCPA_Valid =
false;
4019 if (ptarget->Class == AIS_METEO) {
4020 ptarget->bCPA_Valid =
false;
4029 if (ptarget->b_OwnShip) {
4031 ptarget->TCPA = -100;
4032 ptarget->bCPA_Valid =
false;
4036 double cpa_calc_ownship_cog = gCog;
4037 double cpa_calc_target_cog = ptarget->COG;
4040 if (std::isnan(gSog) || (gSog > 102.2)) {
4041 ptarget->bCPA_Valid =
false;
4046 if (std::isnan(gCog) || gCog == 360.0) {
4048 cpa_calc_ownship_cog =
4052 ptarget->bCPA_Valid =
false;
4058 if (ptarget->COG == 360.0) {
4059 if (ptarget->SOG > 102.2) {
4060 ptarget->bCPA_Valid =
false;
4062 }
else if (ptarget->SOG < .01) {
4063 cpa_calc_target_cog = 0.;
4066 ptarget->bCPA_Valid =
false;
4072 double v0 = gSog * 1852.;
4073 double v1 = ptarget->SOG * 1852.;
4075 if ((v0 < 1e-6) && (v1 < 1e-6)) {
4079 ptarget->bCPA_Valid =
false;
4086 double east1 = (ptarget->Lon - gLon) * 60 * 1852;
4087 double north1 = (ptarget->Lat - gLat) * 60 * 1852;
4089 double east = east1 * (cos(gLat * PI / 180.));
4091 double north = north1;
4094 double cosa = cos((90. - cpa_calc_ownship_cog) * PI / 180.);
4095 double sina = sin((90. - cpa_calc_ownship_cog) * PI / 180.);
4096 double cosb = cos((90. - cpa_calc_target_cog) * PI / 180.);
4097 double sinb = sin((90. - cpa_calc_target_cog) * PI / 180.);
4100 double fc = (v0 * cosa) - (v1 * cosb);
4101 double fs = (v0 * sina) - (v1 * sinb);
4103 double d = (fc * fc) + (fs * fs);
4111 tcpa = ((fc * east) + (fs * north)) / d;
4114 ptarget->TCPA = tcpa * 60.;
4119 double OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA, TargetLonCPA;
4121 ll_gc_ll(gLat, gLon, cpa_calc_ownship_cog, gSog * tcpa, &OwnshipLatCPA,
4123 ll_gc_ll(ptarget->Lat, ptarget->Lon, cpa_calc_target_cog,
4124 ptarget->SOG * tcpa, &TargetLatCPA, &TargetLonCPA);
4127 ptarget->CPA = DistGreatCircle(OwnshipLatCPA, OwnshipLonCPA, TargetLatCPA,
4130 ptarget->bCPA_Valid =
true;
4132 if (ptarget->TCPA < 0) ptarget->bCPA_Valid =
false;
4136void AisDecoder::OnTimerDSC(wxTimerEvent &event) {
4139 if (m_ptentative_dsctarget) {
4140 ProcessDSx(m_dsc_last_string,
true);
4144void AisDecoder::OnTimerAIS(wxTimerEvent &event) {
4149 wxDateTime now = wxDateTime::Now();
4152 std::unordered_map<int, std::shared_ptr<AisTargetData>> ¤t_targets =
4155 auto it = current_targets.begin();
4156 std::vector<int> remove_array;
4158 while (it != current_targets.end()) {
4162 current_targets.erase(it);
4167 std::shared_ptr<AisTargetData> xtd = it->second;
4169 int target_posn_age = now.GetTicks() - xtd->PositionReportTicks;
4170 int target_static_age = now.GetTicks() - xtd->StaticReportTicks;
4182 double removelost_Mins = fmax(g_RemoveLost_Mins, g_MarkLost_Mins);
4184 if (g_bInlandEcdis && (xtd->Class != AIS_ARPA)) {
4185 double iECD_LostTimeOut = 0.0;
4189 if (xtd->Class == AIS_CLASS_B) {
4190 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR))
4191 iECD_LostTimeOut = 18 * 60;
4193 iECD_LostTimeOut = 180;
4195 if (xtd->Class == AIS_CLASS_A) {
4196 if ((xtd->NavStatus == MOORED) || (xtd->NavStatus == AT_ANCHOR)) {
4198 iECD_LostTimeOut = 18 * 60;
4200 iECD_LostTimeOut = 60;
4202 iECD_LostTimeOut = 60;
4205 if ((target_posn_age > iECD_LostTimeOut) &&
4206 (xtd->Class != AIS_GPSG_BUDDY))
4207 xtd->b_active =
false;
4209 removelost_Mins = (2 * iECD_LostTimeOut) / 60.;
4210 }
else if (g_bMarkLost) {
4211 if ((target_posn_age > g_MarkLost_Mins * 60) &&
4212 (xtd->Class != AIS_GPSG_BUDDY))
4213 xtd->b_active =
false;
4216 if (xtd->Class == AIS_SART || xtd->Class == AIS_METEO)
4217 removelost_Mins = 18.0;
4221 if (g_bRemoveLost || g_bInlandEcdis) {
4223 (xtd->Class == AIS_ARPA &&
4225 if (((target_posn_age > removelost_Mins * 60) &&
4226 (xtd->Class != AIS_GPSG_BUDDY)) ||
4231 xtd->b_positionOnceValid =
false;
4239 long mmsi_long = xtd->MMSI;
4240 pSelectAIS->DeleteSelectablePoint((
void *)mmsi_long, SELTYPE_AISTARGET);
4245 if (target_static_age > removelost_Mins * 60 * 3 || b_arpalost) {
4246 xtd->b_removed =
true;
4248 remove_array.push_back(xtd->MMSI);
4255 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
4257 if (xtd->MMSI == props->MMSI) {
4258 if (props->m_bignore) {
4259 remove_array.push_back(xtd->MMSI);
4260 xtd->b_removed =
true;
4268 if (
static_cast<unsigned>(xtd->MMSI) == g_OwnShipmmsi) {
4269 remove_array.push_back(xtd->MMSI);
4270 xtd->b_removed =
true;
4278 for (
unsigned int i = 0; i < remove_array.size(); i++) {
4279 auto itd = current_targets.find(remove_array[i]);
4280 if (itd != current_targets.end()) {
4281 std::shared_ptr<AisTargetData> td = itd->second;
4282 current_targets.erase(itd);
4291 m_bSuppressed =
false;
4292 if (g_bAIS_CPA_Alert_Suppress_Moored || g_bHideMoored ||
4293 (g_bShowScaled && g_bAllowShowScaled))
4294 m_bSuppressed =
true;
4296 m_bAIS_Audio_Alert_On =
false;
4305 std::shared_ptr<AisTargetData> palert_target =
nullptr;
4307 if (!g_pais_alert_dialog_active) {
4308 pAISMOBRoute =
nullptr;
4309 double tcpa_min = 1e6;
4310 double sart_range = 1e6;
4311 std::shared_ptr<AisTargetData> palert_target_cpa =
nullptr;
4312 std::shared_ptr<AisTargetData> palert_target_sart =
nullptr;
4313 std::shared_ptr<AisTargetData> palert_target_dsc =
nullptr;
4315 for (it = current_targets.begin(); it != current_targets.end(); ++it) {
4316 std::shared_ptr<AisTargetData> td = it->second;
4318 if ((td->Class != AIS_SART) && (td->Class != AIS_DSC)) {
4319 if (g_bAIS_CPA_Alert && td->b_active) {
4320 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4321 if (td->TCPA < tcpa_min) {
4322 tcpa_min = td->TCPA;
4323 palert_target_cpa = td;
4327 }
else if ((td->Class == AIS_DSC) &&
4328 ((td->ShipType == 12) || (td->ShipType == 16))) {
4330 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4331 palert_target_dsc = td;
4334 td->b_isDSCtarget =
false;
4339 else if (td->Class == AIS_SART) {
4341 if ((AIS_ALERT_SET == td->n_alert_state) && !td->b_in_ack_timeout) {
4342 if (td->Range_NM < sart_range) {
4343 tcpa_min = sart_range;
4344 palert_target_sart = td;
4354 palert_target = palert_target_cpa;
4356 if (palert_target_sart) {
4357 palert_target = palert_target_sart;
4360 if (palert_target_dsc) {
4361 palert_target = palert_target_dsc;
4365 palert_target = Get_Target_Data_From_MMSI(m_callbacks.get_target_mmsi());
4370 TimerAIS.Start(TIMER_AIS_MSEC, wxTIMER_CONTINUOUS);
4373std::shared_ptr<AisTargetData> AisDecoder::Get_Target_Data_From_MMSI(
4375 if (AISTargetList.find(mmsi) == AISTargetList.end())
return nullptr;
4376 return AISTargetList[mmsi];
4379ArrayOfMmsiProperties g_MMSI_Props_Array;
4383MmsiProperties::MmsiProperties(wxString &spec) {
4385 wxStringTokenizer tkz(spec, _T(
";"));
4388 s = tkz.GetNextToken();
4393 s = tkz.GetNextToken();
4395 if (s.Upper() == _T(
"ALWAYS"))
4396 TrackType = TRACKTYPE_ALWAYS;
4397 else if (s.Upper() == _T(
"NEVER"))
4398 TrackType = TRACKTYPE_NEVER;
4401 s = tkz.GetNextToken();
4403 if (s.Upper() == _T(
"IGNORE")) m_bignore =
true;
4406 s = tkz.GetNextToken();
4408 if (s.Upper() == _T(
"MOB")) m_bMOB =
true;
4411 s = tkz.GetNextToken();
4413 if (s.Upper() == _T(
"VDM")) m_bVDM =
true;
4416 s = tkz.GetNextToken();
4418 if (s.Upper() == _T(
"FOLLOWER")) m_bFollower =
true;
4421 s = tkz.GetNextToken();
4423 if (s.Upper() == _T(
"PERSIST")) m_bPersistentTrack =
true;
4426 s = tkz.GetNextToken();
4428 m_ShipName = s.Upper();
4432MmsiProperties::~MmsiProperties() =
default;
4434void MmsiProperties::Init() {
4436 TrackType = TRACKTYPE_DEFAULT;
4440 m_bFollower =
false;
4441 m_bPersistentTrack =
false;
4442 m_ShipName = wxEmptyString;
4445wxString MmsiProperties::Serialize() {
4449 sMMSI.Printf(_T(
"%d"), MMSI);
4453 if (TRACKTYPE_ALWAYS == TrackType)
4454 sMMSI << _T(
"always");
4455 else if (TRACKTYPE_NEVER == TrackType)
4456 sMMSI << _T(
"never");
4461 sMMSI << _T(
"ignore");
4476 sMMSI << _T(
"Follower");
4480 if (m_bPersistentTrack) {
4481 sMMSI << _T(
"PERSIST");
4485 if (m_ShipName == wxEmptyString) {
4486 m_ShipName = GetShipNameFromFile(MMSI);
4488 sMMSI << m_ShipName;
4493 AIS_Target_Name_Hash *AISTargetNamesC,
4494 AIS_Target_Name_Hash *AISTargetNamesNC,
long mmsi) {
4495 if (g_benableAISNameCache) {
4496 wxString ship_name = wxEmptyString;
4499 if (!pTargetData->b_nameValid) {
4500 AIS_Target_Name_Hash::iterator it = AISTargetNamesC->find(mmsi);
4501 if (it != AISTargetNamesC->end()) {
4502 ship_name = (*AISTargetNamesC)[mmsi].Left(20);
4503 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4504 ship_name.length() + 1);
4505 pTargetData->b_nameValid =
true;
4506 pTargetData->b_nameFromCache =
true;
4507 }
else if (!g_bUseOnlyConfirmedAISName) {
4508 it = AISTargetNamesNC->find(mmsi);
4509 if (it != AISTargetNamesNC->end()) {
4510 ship_name = (*AISTargetNamesNC)[mmsi].Left(20);
4511 strncpy(pTargetData->ShipName, ship_name.mb_str(),
4512 ship_name.length() + 1);
4513 pTargetData->b_nameValid =
true;
4514 pTargetData->b_nameFromCache =
true;
4519 else if ((pTargetData->MID == 5) || (pTargetData->MID == 24) ||
4520 (pTargetData->MID == 19) ||
4521 (pTargetData->MID == 123) ||
4522 (pTargetData->MID == 124)) {
4524 pTargetData->b_nameFromCache =
false;
4525 ship_name = trimAISField(pTargetData->ShipName);
4526 AIS_Target_Name_Hash::iterator itC = AISTargetNamesC->find(mmsi);
4527 AIS_Target_Name_Hash::iterator itNC = AISTargetNamesNC->find(mmsi);
4529 AISTargetNamesC->end()) {
4530 if ((*AISTargetNamesC)[mmsi] ==
4532 if (itNC != AISTargetNamesNC->end()) {
4534 AISTargetNamesNC->erase(itNC);
4537 if (itNC != AISTargetNamesNC->end()) {
4539 if ((*AISTargetNamesNC)[mmsi] ==
4542 (*AISTargetNamesC)[mmsi] = ship_name;
4544 AISTargetNamesNC->erase(itNC);
4547 (*AISTargetNamesNC)[mmsi] = ship_name;
4549 if (g_bUseOnlyConfirmedAISName)
4550 strncpy(pTargetData->ShipName, (*AISTargetNamesC)[mmsi].mb_str(),
4551 (*AISTargetNamesC)[mmsi].Left(20).Length() + 1);
4553 (*AISTargetNamesNC)[mmsi] = ship_name;
4558 AISTargetNamesNC->end()) {
4559 if ((*AISTargetNamesNC)[mmsi] ==
4562 (*AISTargetNamesC)[mmsi] = ship_name;
4564 AISTargetNamesNC->erase(itNC);
4566 (*AISTargetNamesNC)[mmsi] = ship_name;
4569 (*AISTargetNamesNC)[mmsi] = ship_name;
4571 if (g_bUseOnlyConfirmedAISName) {
4572 pTargetData->ShipName[SHIP_NAME_LEN - 1] =
'\0';
4573 strncpy(pTargetData->ShipName,
"Unknown ",
4581wxString GetShipNameFromFile(
int nmmsi) {
4582 wxString name = wxEmptyString;
4583 if (g_benableAISNameCache) {
4584 std::ifstream infile(AISTargetNameFileName.mb_str());
4587 while (getline(infile, line)) {
4588 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()), _T(
","));
4589 if (nmmsi == wxAtoi(tokenizer.GetNextToken())) {
4590 name = tokenizer.GetNextToken().Trim();
4593 tokenizer.GetNextToken();
4601void AisDecoder::UpdateMMSItoNameFile(
const wxString &mmsi,
4602 const wxString &name) {
4606 std::map<wxString, wxString> mmsi_name_map;
4609 std::ifstream infile(AISTargetNameFileName.mb_str());
4612 while (getline(infile, line)) {
4613 wxStringTokenizer tokenizer(wxString::FromUTF8(line.c_str()), _T(
","));
4614 wxString file_mmsi = tokenizer.GetNextToken();
4615 wxString file_name = tokenizer.GetNextToken().Trim();
4616 mmsi_name_map[file_mmsi] = file_name;
4622 mmsi_name_map[mmsi] = name.Upper();
4625 std::ofstream outfile(AISTargetNameFileName.mb_str());
4627 for (
const auto &pair : mmsi_name_map) {
4628 std::string line = std::string(pair.first.mb_str()) +
"," +
4629 std::string(pair.second.mb_str()) +
"\n";
4636wxString AisDecoder::GetMMSItoNameEntry(
const wxString &mmsi) {
4637 return GetShipNameFromFile(wxAtoi(mmsi));
4641int AisMeteoNewMmsi(
int orig_mmsi,
int m_lat,
int m_lon,
int lon_bits = 0,
4645 if ((!lon_bits || lon_bits == 999) && siteID) {
4648 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4649 if (points.size()) {
4650 for (
const auto &point : points) {
4652 if (siteID == point.siteID && orig_mmsi == point.orig_mmsi) {
4654 new_mmsi = point.mmsi;
4660 if (!found && !lon_bits) {
4665 double lon_tentative = 181.;
4666 double lat_tentative = 91.;
4668 if (lon_bits == 25) {
4669 if (m_lon & 0x01000000)
4670 m_lon |= 0xFE000000;
4671 lon_tentative = m_lon / 60000.;
4673 if (m_lat & 0x00800000)
4674 m_lat |= 0xFF000000;
4675 lat_tentative = m_lat / 60000.;
4677 }
else if (lon_bits == 28) {
4678 if (m_lon & 0x08000000)
4679 m_lon |= 0xf0000000;
4680 lon_tentative = m_lon / 600000.;
4682 if (m_lat & 0x04000000)
4683 m_lat |= 0xf8000000;
4684 lat_tentative = m_lat / 600000.;
4689 wxString slon = wxString::Format(
"%0.3f", lon_tentative);
4690 wxString slat = wxString::Format(
"%0.3f", lat_tentative);
4698 static int nextMeteommsi = 199400000;
4699 auto &points = AisMeteoPoints::GetInstance().GetPoints();
4701 if (lon_bits != 999 && !points.empty()) {
4702 wxString t_lat, t_lon;
4703 for (
const auto &point : points) {
4705 if (slat.IsSameAs(point.lat) && slon.IsSameAs(point.lon)) {
4707 new_mmsi = point.mmsi;
4716 points.emplace_back(
4717 AisMeteoPoint(nextMeteommsi, slat, slon, siteID, orig_mmsi));
4718 new_mmsi = nextMeteommsi;
Class AisDecoder and helpers.
Global state for AIS decoder.
int GetInt(int sp, int len, bool signed_flag=false)
sp is starting bit, 1-based
EventVar plugin_msg
A JSON message should be sent.
EventVar new_track
Notified on new track creation.
EventVar info_update
Notified when AIS user dialogs should update.
EventVar touch_state
Notified when gFrame->TouchAISActive() should be invoked.
Add a new point to the list of Meteo stations.
void Notify() override
Notify all listeners, no data supplied.
Process incoming AIS messages.
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString &error_msg="") const
Logs an input message with context information.
void Notify(std::shared_ptr< const NavMsg > message)
Accept message received by driver, make it available for upper layers.
A regular Nmea0183 message.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Represents a waypoint or mark within the navigation system.
A parsed SignalK message over ipv4.
Represents a single point in a track.
Represents a track, which is a series of connected track points.
The JSON document writer.
void Write(const wxJSONValue &value, wxString &str)
Write the JSONvalue object to a JSON text.
Raw messages layer, supports sending and recieving navmsg messages.
Multiplexer class and helpers.
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.