OpenCPN Partial API docs
ais_target_data.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  *
5  ***************************************************************************
6  * Copyright (C) 2010 by David S. Register *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the *
20  * Free Software Foundation, Inc., *
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22  ***************************************************************************
23  */
24 #include <unordered_map>
25 
26 #include <wx/datetime.h>
27 #include <wx/intl.h>
28 #include <wx/string.h>
29 
30 #include "model/ais_state_vars.h"
31 #include "model/ais_target_data.h"
32 #include "model/config_vars.h"
33 #include "model/navutil_base.h"
34 #include "model/own_ship.h"
35 
36 static std::unordered_map<int, wxString> s_ERI_hash;
37 
38 void make_hash_ERI(int key, const wxString &description) {
39  s_ERI_hash[key] = description;
40 }
41 
42 void clear_hash_ERI() { s_ERI_hash.clear(); }
43 
44 static wxString FormatTimeAdaptive(int seconds) {
45  int m = seconds / 60;
46  if (seconds < 100)
47  return wxString::Format(_T("%3ds"), seconds);
48  else if (seconds < 3600) {
49  int m = seconds / 60;
50  int s = seconds % 60;
51  return wxString::Format(_T("%2dmin %02ds"), m, s);
52  }
53  int h = seconds / 3600;
54  m -= h * 60;
55  return wxString::Format(_T("%2dh %02dmin"), h, m);
56 }
57 
58 static wxString html_escape(const wxString &src) {
59  // Escape &, <, > as well as single and double quotes for HTML.
60  wxString ret = src;
61 
62  ret.Replace(_T("<"), _T("&lt;"));
63  ret.Replace(_T(">"), _T("&gt;"));
64 
65  // only < and > in 6 bits AIS ascii
66  // ret.Replace(_T("\""), _T("&quot;"));
67  // ret.Replace(_T("&"), _T("&amp;"));
68  // ret.Replace(_T("'"), _T("&#39;"));
69 
70  // Do we care about multiple spaces?
71  // ret.Replace(_T(" "), _T("&nbsp;"));
72  return ret;
73 }
74 
75 wxString trimAISField(char *data) {
76  // Clip any unused characters (@) from data
77 
78  wxString field = wxString::From8BitData(data);
79  while (field.Right(1) == '@' || field.Right(1) == ' ') field.RemoveLast();
80 
81  // And remove any leading spaces to properly sort and display
82  field.Trim(false);
83 
84  return field;
85 }
86 
87 
88 wxString ais_get_status(int index) {
89  static const wxString ais_status[] = {
90  _("Underway using Engine"),
91  _("At Anchor"),
92  _("Not Under Command"),
93  _("Restricted Manoeuvrability"),
94  _("Constrained by draught"),
95  _("Moored"),
96  _("Aground"),
97  _("Engaged in Fishing"),
98  _("Underway Sailing"),
99  _("High Speed Craft"),
100  _("Wing In Ground Effect"),
101  _("Power-driven vessel towing astern (regional use)"),
102  _("Power-driven vessel pushing ahead or towing alongside (regional use)"),
103  _("Reserved 13"),
104  _("Reserved 14"),
105  _("Undefined"),
106  _("AtoN Virtual"),
107  _("AtoN Virtual (On Position)"),
108  _("AtoN Virtual (Off Position)"),
109  _("AtoN Real"),
110  _("AtoN Real (On Position)"),
111  _("AtoN Real(Off Position)")};
112 
113  return ais_status[index];
114 }
115 
116 wxString ais_meteo_get_trend(int tend) {
117  wxString trend = wxEmptyString;
118  if (tend < 3) {
119  if (tend == 0)
120  trend = _("steady");
121  else if (tend == 1)
122  trend = _("decreasing");
123  else if (tend == 2)
124  trend = _("increasing");
125  }
126  return trend;
127 }
128 
129 wxString aisMeteoPrecipType(int precip) {
130  wxString prec = wxEmptyString;
131  switch (precip) {
132  case 0:
133  prec = "Reserved";
134  break;
135  case 1:
136  prec = "Rain";
137  break;
138  case 2:
139  prec = "Thunderstorm";
140  break;
141  case 3:
142  prec = "Freezing rain";
143  break;
144  case 4:
145  prec = "Mixed / ice";
146  case 5:
147  prec = "Snow";
148  break;
149  default:
150  prec = "not available";
151  // 6 = reserved 7 = not available = default
152  }
153  return prec;
154 }
155 
156 wxString aisMeteoWaterLevelRef(int refID) {
157  wxString ref = wxEmptyString;
158  switch (refID) {
159  case 0:
160  ref = "MLLW";
161  break;
162  case 1:
163  ref = "IGLD-85";
164  break;
165  case 2:
166  ref = "Local river";
167  break;
168  case 3:
169  ref = "STND";
170  break;
171  case 4:
172  ref = "MHHW";
173  case 5:
174  ref = "MHW";
175  break;
176  case 6:
177  ref = "MSL";
178  break;
179  case 7:
180  ref = "MLW";
181  break;
182  case 8:
183  ref = "NGVD-29";
184  break;
185  case 9:
186  ref = "NAVD-88";
187  break;
188  case 10:
189  ref = "WGS-84";
190  break;
191  case 11:
192  ref = "LAT";
193  break;
194  case 12:
195  ref = "Pool";
196  break;
197  case 13:
198  ref = "Gauge";
199  break;
200  }
201  return ref;
202 }
203 
204 AisTargetData::AisTargetData(AisTargetCallbacks cb ) : m_callbacks(cb) {
205  strncpy(ShipName, "Unknown ", SHIP_NAME_LEN);
206  strncpy(CallSign, " ", 8);
207  strncpy(Destination, " ", DESTINATION_LEN);
208  ShipNameExtension[0] = 0;
209  b_show_AIS_CPA = false;
210 
211  SOG = 555.;
212  COG = 666.;
213  HDG = 511.;
214  ROTAIS = -128;
215  Lat = 0.;
216  Lon = 0.;
217 
218  wxDateTime now = wxDateTime::Now();
219  now.MakeGMT();
220  PositionReportTicks = now.GetTicks(); // Default is my idea of NOW
221  StaticReportTicks = now.GetTicks();
222  b_lost = false;
223  b_removed = false;
224 
225  IMO = 0;
226  MID = 555;
227  MMSI = 666;
228  NavStatus = UNDEFINED;
229  SyncState = 888;
230  SlotTO = 999;
231  ShipType = 19; // "Unknown"
232  b_isDSCtarget = false;
233  m_dscNature = 99;
234  m_dscTXmmsi = 666;
235 
236  CPA = 100; // Large values avoid false alarms
237  TCPA = 100;
238 
239  Range_NM = -1.;
240  Brg = -1.;
241 
242  DimA = DimB = DimC = DimD = 0;
243  ;
244 
245  ETA_Mo = 0;
246  ETA_Day = 0;
247  ETA_Hr = 24;
248  ETA_Min = 60;
249 
250  Draft = 0.;
251 
252  RecentPeriod = 0;
253 
254  m_utc_hour = 0;
255  m_utc_min = 0;
256  m_utc_sec = 0;
257 
258  Class = AIS_CLASS_A; // default
259  n_alert_state = AIS_NO_ALERT;
260  b_suppress_audio = false;
261  b_positionDoubtful = false;
262  b_positionOnceValid = false;
263  b_nameValid = false;
264 
265  Euro_Length = 0; // Extensions for European Inland AIS
266  Euro_Beam = 0;
267  Euro_Draft = 0;
268  strncpy(Euro_VIN, " ", 8);
269  UN_shiptype = 0;
270 
271  b_isEuroInland = false;
272  b_blue_paddle = false;
273 
274  b_NoTrack = false;
275  b_OwnShip = false;
276  b_PersistTrack = false;
277  b_mPropPersistTrack = false;
278  b_in_ack_timeout = false;
279 
280  b_active = false;
281  blue_paddle = 0;
282  bCPA_Valid = false;
283  b_isFollower = false;
284  ROTIND = 0;
285  b_show_track = g_bAISShowTracks;
286  b_SarAircraftPosnReport = false;
287  altitude = 0;
288  b_nameFromCache = false;
289  importance = 0.0;
290  for (unsigned int i = 0; i < AIS_TARGETDATA_MAX_CANVAS; i++)
291  last_scale[i] = 50;
292  met_data.original_mmsi = 0;
293  met_data.stationID = 0;
294  met_data.month = 0;
295  met_data.day = 0;
296  met_data.hour = 24;
297  met_data.minute = 60;
298  met_data.pos_acc = 1;
299  met_data.wind_kn = 122;
300  met_data.wind_gust_kn = 127;
301  met_data.wind_dir = 360;
302  met_data.wind_gust_dir = 360;
303  met_data.air_temp = -102.4;
304  met_data.rel_humid = 101;
305  met_data.dew_point = 50.1;
306  met_data.airpress = 1310;
307  met_data.airpress_tend = 3;
308  met_data.hor_vis = 12.7;
309  met_data.hor_vis_GT = false;
310  met_data.water_lev_dev = 4001 / 100 - 10;
311  met_data.water_level = -32;
312  met_data.water_lev_trend = 3;
313  met_data.current = 25.5;
314  met_data.curr_dir = 360;
315  met_data.wave_height = 25.5;
316  met_data.wave_period = 63;
317  met_data.wave_dir = 360;
318  met_data.swell_height = 25.5;
319  met_data.swell_per = 63;
320  met_data.swell_dir = 360;
321  met_data.seastate = 13;
322  met_data.water_temp = 501;
323  met_data.precipitation = 7;
324  met_data.salinity = 51.;
325  met_data.ice = 3;
326  met_data.vertical_ref = 14;
327 }
328 
329 void AisTargetData::CloneFrom(AisTargetData *q) {
330  strncpy(ShipName, q->ShipName, SHIP_NAME_LEN);
331  strncpy(CallSign, q->CallSign, 8);
332  strncpy(Destination, q->Destination, DESTINATION_LEN);
333  ShipNameExtension[0] = 0;
334  b_show_AIS_CPA = q->b_show_AIS_CPA;
335  ;
336 
337  SOG = q->SOG;
338  COG = q->COG;
339  HDG = q->HDG;
340  ROTAIS = q->ROTAIS;
341  Lat = q->Lat;
342  Lon = q->Lon;
343 
344  PositionReportTicks = q->PositionReportTicks;
345  StaticReportTicks = q->StaticReportTicks;
346  b_lost = q->b_lost;
347  b_removed = q->b_removed;
348 
349  IMO = q->IMO;
350  MID = q->MID;
351  MMSI = q->MMSI;
352  NavStatus = q->NavStatus;
353  SyncState = q->SyncState;
354  SlotTO = q->SlotTO;
355  ShipType = q->ShipType;
356  b_isDSCtarget = q->b_isDSCtarget;
357  m_dscNature = q->m_dscNature;
358  m_dscTXmmsi = q->m_dscTXmmsi;
359 
360  CPA = q->CPA;
361  TCPA = q->TCPA;
362 
363  Range_NM = q->Range_NM;
364  Brg = q->Brg;
365 
366  DimA = q->DimA;
367  DimB = q->DimB;
368  DimC = q->DimC;
369  DimD = q->DimD;
370 
371  ETA_Mo = q->ETA_Mo;
372  ETA_Day = q->ETA_Day;
373  ETA_Hr = q->ETA_Hr;
374  ETA_Min = q->ETA_Min;
375 
376  Draft = q->Draft;
377 
378  RecentPeriod = q->RecentPeriod;
379 
380  m_utc_hour = q->m_utc_hour;
381  m_utc_min = q->m_utc_min;
382  m_utc_sec = q->m_utc_sec;
383 
384  Class = q->Class;
385  n_alert_state = q->n_alert_state;
386  b_suppress_audio = q->b_suppress_audio;
387  b_positionDoubtful = q->b_positionDoubtful;
388  b_positionOnceValid = q->b_positionOnceValid;
389  b_nameValid = q->b_nameValid;
390 
391  Euro_Length = q->Euro_Length; // Extensions for European Inland AIS
392  Euro_Beam = q->Euro_Beam;
393  Euro_Draft = q->Euro_Draft;
394  memcpy(Euro_VIN, q->Euro_VIN, EURO_VIN_LEN);
395  UN_shiptype = q->UN_shiptype;
396 
397  b_isEuroInland = q->b_isEuroInland;
398  b_blue_paddle = q->b_blue_paddle;
399 
400  b_OwnShip = q->b_OwnShip;
401  b_in_ack_timeout = q->b_in_ack_timeout;
402 
403  m_ptrack = q->m_ptrack;
404 
405  b_active = q->b_active;
406  blue_paddle = q->blue_paddle;
407  bCPA_Valid = q->bCPA_Valid;
408  ROTIND = q->ROTIND;
409  b_show_track = q->b_show_track;
410  b_SarAircraftPosnReport = q->b_SarAircraftPosnReport;
411  altitude = q->altitude;
412 }
413 
414 AisTargetData::~AisTargetData() { m_ptrack.clear(); }
415 //AisTargetData::~AisTargetData() { m_pMetPoint.clear(); } //TODO Needed?
416 
417 wxString AisTargetData::GetFullName(void) {
418  wxString retName;
419  if (b_nameValid) {
420  wxString shipName = trimAISField(ShipName);
421  if (shipName == _T("Unknown"))
422  retName = wxGetTranslation(shipName);
423  else
424  retName = shipName;
425 
426  if (strlen(ShipNameExtension)) {
427  wxString shipNameExt = trimAISField(ShipNameExtension);
428  retName += shipNameExt;
429  }
430  }
431 
432  return retName;
433 }
434 
435 wxString AisTargetData::BuildQueryResult(void) {
436  wxString html;
437  wxDateTime now = wxDateTime::Now();
438 
439  wxString tableStart = _T("\n<table border=0 cellpadding=1 cellspacing=0>\n");
440  wxString tableEnd = _T("</table>\n\n");
441  wxString rowStart = _T("<tr><td><font size=-2>");
442  wxString rowStartH = _T("<tr><td nowrap>");
443  wxString rowSeparator = _T("</font></td><td></td><td><b>");
444  wxString rowSeparatorH = _T("</td><td></td><td>");
445  wxString colSeparator = _T("<td></td>");
446  wxString rowEnd = _T("</b></td></tr>\n");
447  wxString vertSpacer =
448  _T("<tr><td></td></tr><tr><td></td></tr><tr><td></td></tr>\n\n");
449 
450  wxString IMOstr, MMSIstr, ClassStr;
451 
452  html << tableStart << _T("<tr><td nowrap colspan=2>");
453  if (b_nameValid) {
454  html << _T("<font size=+2><i><b>") << GetFullName();
455  html << _T("</b></i></font>&nbsp;&nbsp;<b>");
456  }
457 
458  if ((Class != AIS_ATON) && (Class != AIS_BASE) && (Class != AIS_GPSG_BUDDY) &&
459  (Class != AIS_SART) && (Class != AIS_METEO)) {
460  html << trimAISField(CallSign) << _T("</b>") << rowEnd;
461 
462  if (Class != AIS_CLASS_B) {
463  if (IMO > 0) IMOstr = wxString::Format(_T("%08d"), abs(IMO));
464  }
465  } else
466  html << _T("</b>") << rowEnd;
467 
468  html << vertSpacer;
469 
470  if (Class != AIS_GPSG_BUDDY) {
471  MMSIstr = wxString::Format(_T("%09d"), abs(MMSI));
472  }
473  ClassStr = wxGetTranslation(Get_class_string(false));
474 
475  if (Class == AIS_ATON) {
476  wxString cls(_T("AtoN: "));
477  cls += Get_vessel_type_string(false);
478  ClassStr = wxGetTranslation(cls);
479  }
480 
481  if (b_SarAircraftPosnReport) {
482  int airtype = (MMSI % 1000) / 100;
483  ClassStr = airtype == 5 ? _("SAR Helicopter") : _("SAR Aircraft");
484  }
485 
486  if (IMOstr.Length())
487  html << _T("<tr><td colspan=2><table width=100% border=0 cellpadding=0 ")
488  _T("cellspacing=0>")
489  << rowStart << _("MMSI")
490  << _T("</font></td><td>&nbsp;</td><td><font size=-2>") << _("Class")
491  << _T("</font></td><td>&nbsp;</td><td align=right><font size=-2>")
492  << _("IMO") << _T("</font></td></tr>") << rowStartH << _T("<b>")
493  << MMSIstr << _T("</b></td><td>&nbsp;</td><td><b>") << ClassStr
494  << _T("</b></td><td>&nbsp;</td><td align=right><b>") << IMOstr
495  << rowEnd << _T("</table></td></tr>");
496 
497  else if (Class == AIS_METEO) {
498  MMSIstr = wxString::Format(_T("%09d"), abs(met_data.original_mmsi));
499  html << _T("<tr><td colspan=2><table width=100% border=0 cellpadding=0 ")
500  _T("cellspacing=0>")
501  << rowStart << _("MMSI")
502  << _T("</font></td><td>&nbsp;</td><td align=right><font size=-2>")
503  << _("Class") << _T("</font></td></tr>") << rowStartH << _T("<b>")
504  << MMSIstr << _T("</b></td><td>&nbsp;</td><td align=right><b>")
505  << _T("<font size=-1>") << ClassStr << rowEnd << rowStart
506  << _T("<b>ID: ") << MMSI;
507  if (met_data.stationID) { // Facilitate to find a Meteo target on SignalK
508  wxString SK_ID =
509  wxString::Format(_T("%06d"), (met_data.stationID - 1000000));
510  html << "<td>&nbsp;</td><td align=right>" << "SK-ID: " << SK_ID;
511  }
512  html << rowEnd << _T("</b></table></td></tr>");
513  }
514  else
515  html << _T("<tr><td colspan=2><table width=100% border=0 cellpadding=0 ")
516  _T("cellspacing=0>")
517  << rowStart << _("MMSI")
518  << _T("</font></td><td>&nbsp;</td><td align=right><font size=-2>")
519  << _("Class") << _T("</font></td></tr>") << rowStartH << _T("<b>")
520  << MMSIstr << _T("</b></td><td>&nbsp;</td><td align=right><b>")
521  << ClassStr << rowEnd << _T("</table></td></tr>");
522 
523  if ((Class != AIS_SART)) //&& (Class != AIS_DSC))
524  html << _T("<tr><td colspan=2><table width=100% border=0 cellpadding=0 ")
525  _T("cellspacing=0>")
526  << rowStart
527  << ((Class == AIS_BASE || Class == AIS_ATON || Class == AIS_METEO)
528  ? _("Nation")
529  : _("Flag"))
530  << rowEnd << _T("</font></td></tr>") << rowStartH
531  << _T("<font size=-1><b>")
532  << GetCountryCode(true) << rowEnd << _T("</font></table></td></tr>");
533 
534  wxString navStatStr;
535  if ((Class != AIS_BASE) && (Class != AIS_CLASS_B) && (Class != AIS_SART) &&
536  (Class != AIS_METEO)) {
537  html << vertSpacer;
538  if ((NavStatus <= 21) && (NavStatus >= 0))
539  navStatStr = wxGetTranslation(ais_get_status(NavStatus));
540  } else if (Class == AIS_SART) {
541  if (NavStatus == RESERVED_14)
542  navStatStr = _("Active");
543  else if (NavStatus == UNDEFINED)
544  navStatStr = _("Testing");
545  }
546 
547  wxString sart_sub_type;
548  if (Class == AIS_SART) {
549  int mmsi_start = MMSI / 1000000;
550  switch (mmsi_start) {
551  case 970:
552  // sart_sub_type = _T("SART");
553  break;
554  case 972:
555  sart_sub_type = _T("MOB");
556  break;
557  case 974:
558  sart_sub_type = _T("EPIRB");
559  break;
560  default:
561  sart_sub_type = _("Unknown");
562  break;
563  }
564  }
565 
566  wxString AISTypeStr, UNTypeStr, sizeString;
567  if ((Class != AIS_BASE) && (Class != AIS_SART) && (Class != AIS_DSC) &&
568  (Class != AIS_METEO)) {
569  // Ship type
570  AISTypeStr = wxGetTranslation(Get_vessel_type_string());
571 
572  if (b_isEuroInland && UN_shiptype) {
573  auto it = s_ERI_hash.find(UN_shiptype);
574  wxString type;
575  if (it == s_ERI_hash.end())
576  type = _("Undefined");
577  else
578  type = it->second;
579 
580  UNTypeStr = wxGetTranslation(type);
581  }
582 
583  if (b_SarAircraftPosnReport) {
584  AISTypeStr.Clear();
585  UNTypeStr.Clear();
586  navStatStr.Clear();
587  }
588 
589  if (Class == AIS_SART) {
590  if (MSG_14_text.Len()) {
591  html << rowStart << _("Safety Broadcast Message") << rowEnd << rowStartH
592  << _T("<b>") << MSG_14_text << rowEnd;
593  }
594  }
595 
596  // Dimensions
597 
598  if (NavStatus != ATON_VIRTUAL && Class != AIS_ARPA && Class != AIS_APRS) {
599  if ((Class == AIS_CLASS_B) || (Class == AIS_ATON)) {
600  sizeString =
601  wxString::Format(_T("%dm x %dm"), (DimA + DimB), (DimC + DimD));
602  } else if (!b_SarAircraftPosnReport) {
603  if ((DimA + DimB + DimC + DimD) == 0) {
604  if (b_isEuroInland) {
605  if (Euro_Length == 0.0) {
606  if (Euro_Draft > 0.01) {
607  sizeString << wxString::Format(_T("---m x ---m x %4.1fm"),
608  Euro_Draft);
609  } else {
610  sizeString << _T("---m x ---m x ---m");
611  }
612  } else {
613  if (Euro_Draft > 0.01) {
614  sizeString << wxString::Format(_T("%5.1fm x %4.1fm x %4.1fm"),
615  Euro_Length, Euro_Beam,
616  Euro_Draft);
617  } else {
618  sizeString << wxString::Format(_T("%5.1fm x %4.1fm x ---m\n\n"),
619  Euro_Length, Euro_Beam);
620  }
621  }
622  } else {
623  if (Draft > 0.01) {
624  sizeString << wxString::Format(_T("---m x ---m x %4.1fm"), Draft);
625  } else {
626  sizeString << _T("---m x ---m x ---m");
627  }
628  }
629  } else if (Draft < 0.01) {
630  sizeString << wxString::Format(_T("%dm x %dm x ---m"), (DimA + DimB),
631  (DimC + DimD));
632  } else {
633  sizeString << wxString::Format(_T("%dm x %dm x %4.1fm"),
634  (DimA + DimB), (DimC + DimD), Draft);
635  }
636  }
637  }
638  }
639 
640  if (Class == AIS_SART) {
641  html << _T("<tr><td colspan=2>")
642  << _T("<b>") << AISTypeStr;
643  if (sart_sub_type.Length()) html << _T(" (") << sart_sub_type << _T("), ");
644  html << navStatStr;
645  html << rowEnd << _T("<tr><td colspan=2>")
646  << _T("<b>") << sizeString << rowEnd;
647  }
648 
649  else if (Class == AIS_ATON) {
650  html << _T("<tr><td colspan=2>")
651  << _T("<b>") << navStatStr;
652  html << rowEnd << _T("<tr><td colspan=2>")
653  << _T("<b>") << sizeString << rowEnd;
654  }
655  else if (Class == AIS_DSC && (ShipType == 12 || ShipType == 16) ) {
656  if (ShipType == 16) { //Distress relay
657  html << _T("<tr><td colspan=2>") << _T("<b>") << _("Distress relay");
658  if (m_dscTXmmsi > 2000000) {
659  wxString mmsirelay = wxString::Format(_T(" %09d"), abs(m_dscTXmmsi));
660  html << _T(" ") << _("by:") << mmsirelay;
661  }
662  html << _T("<b>") << sizeString << rowEnd;
663  }
664  html << _T("<tr><td colspan=2>") << _("Nature of distress: ")
665  << rowEnd << _T("<tr><td colspan=2>");
666  if (m_dscNature < 13) {
667  html << _T("<tr><td colspan=2>") << _T("<b>") << GetNatureofDistress(m_dscNature)
668  << _T("<b>") << sizeString << rowEnd << _T("<tr><td colspan=2>");
669  }
670  }
671  else if ((Class != AIS_BASE) && (Class != AIS_DSC)) {
672  html << _T("<tr><td colspan=2>")
673  << _T("<b>") << AISTypeStr;
674  if (navStatStr.Length()) html << _T(", ") << navStatStr;
675  if (UNTypeStr.Length()) html << _T(" (UN Type ") << UNTypeStr << _T(")");
676  html << rowEnd << _T("<tr><td colspan=2>")
677  << _T("<b>") << sizeString << rowEnd;
678  }
679 
680  if (b_positionOnceValid) {
681  wxString posTypeStr;
682  if (b_positionDoubtful) posTypeStr << _(" (Last Known)");
683 
684  now.MakeGMT();
685  int target_age = now.GetTicks() - PositionReportTicks;
686  // wxLogMessage(wxString::Format(_T("** PositionReportTicks %ld %ld %d"),
687  // now.GetTicks(), PositionReportTicks,
688  // target_age));
689 
690  html << vertSpacer << rowStart << _("Position") << posTypeStr
691  << _T("</font></td><td align=right><font size=-2>") << _("Report Age")
692  << _T("</font></td></tr>")
693 
694  << rowStartH << _T("<b>") << toSDMM(1, Lat)
695  << _T("</b></td><td align=right><b>") << FormatTimeAdaptive(target_age)
696  << rowEnd << rowStartH << _T("<b>") << toSDMM(2, Lon);
697  if (Class != AIS_METEO) html << rowEnd;
698  else {
699  wxString meteoTime =
700  wxString::Format(" %02d:%02d", met_data.hour, met_data.minute);
701  html << " </td><td align=right></b></font><font size=-3>"
702  << _("Issued (UTC)") << "</font><font size=-1><b>" << meteoTime
703  << "</font>" << rowEnd;
704  }
705  }
706 
707  wxString courseStr, sogStr, hdgStr, rotStr, rngStr, brgStr, destStr, etaStr;
708 
709  if (Class == AIS_GPSG_BUDDY) {
710  long month, year, day;
711  m_date_string.Mid(0, 2).ToLong(&day);
712  m_date_string.Mid(2, 2).ToLong(&month);
713  m_date_string.Mid(4, 2).ToLong(&year);
714  wxDateTime date;
715  date.SetDay(day);
716  date.SetMonth((wxDateTime::Month)(month - 1));
717  date.SetYear(year + 2000);
718 
719  wxString f_date = date.FormatISODate();
720 
721  html << vertSpacer << rowStart << _("Report as of") << rowEnd << rowStartH
722  << _T("<b>") << f_date + _T("</b> at <b>")
723  << wxString::Format(_T("%d:%d UTC "), m_utc_hour, m_utc_min) << rowEnd;
724  } else {
725  if (Class == AIS_CLASS_A && !b_SarAircraftPosnReport) {
726  html << vertSpacer << rowStart << _("Destination")
727  << _T("</font></td><td align=right><font size=-2>") << _("ETA (UTC)")
728  << _T("</font></td></tr>\n") << rowStartH << _T("<b>");
729  wxString dest = trimAISField(Destination);
730  if (dest.Length())
731  html << html_escape(dest);
732  else
733  html << _T("---");
734  html << _T("</b></td><td nowrap align=right><b>");
735 
736  if ((ETA_Mo) && (ETA_Hr < 24)) {
737  int yearOffset = 0;
738  if (now.GetMonth() > (ETA_Mo - 1)) yearOffset = 1;
739  wxDateTime eta(ETA_Day, wxDateTime::Month(ETA_Mo - 1),
740  now.GetYear() + yearOffset, ETA_Hr, ETA_Min);
741  html << eta.Format(_T("%b %d %H:%M"));
742  } else
743  html << _T("---");
744  html << rowEnd;
745  }
746 
747  if (Class == AIS_CLASS_A || Class == AIS_CLASS_B || Class == AIS_ARPA ||
748  Class == AIS_APRS || Class == AIS_SART) {
749  int crs = wxRound(COG);
750  if (crs < 360) {
751  wxString magString, trueString;
752  if (g_bShowMag)
753  magString << wxString::Format(wxString("%03d%c(M)"),
754  static_cast<int>(m_callbacks.get_mag(COG)),
755  0x00B0);
756  if (g_bShowTrue)
757  trueString << wxString::Format( wxString("%03d%c "), (int)crs, 0x00B0 );
758 
759  courseStr << trueString << magString;
760  } else if (COG == 360.0)
761  courseStr = _T("---");
762  else if (crs == 360)
763  courseStr = _T("0&deg;");
764 
765  double speed_show = toUsrSpeed(SOG);
766 
767  if ((SOG <= 102.2) || b_SarAircraftPosnReport) {
768  if (speed_show < 10.0)
769  sogStr =
770  wxString::Format(_T("%.2f "), speed_show) + getUsrSpeedUnit();
771  else if (speed_show < 100.0)
772  sogStr =
773  wxString::Format(_T("%.1f "), speed_show) + getUsrSpeedUnit();
774  else
775  sogStr =
776  wxString::Format(_T("%.0f "), speed_show) + getUsrSpeedUnit();
777  }
778  // sogStr = wxString::Format( _T("%5.2f ") +
779  // getUsrSpeedUnit(), toUsrSpeed( SOG ) );
780  else
781  sogStr = _T("---");
782 
783  if ((int)HDG != 511)
784  hdgStr = wxString::Format(_T("%03d&deg;"), (int)HDG);
785  else
786  hdgStr = _T("---");
787 
788  if (ROTAIS != -128) {
789  if (ROTAIS == 127)
790  rotStr << _T("> 5&deg;/30s ") << _("Right");
791  else if (ROTAIS == -127)
792  rotStr << _T("> 5&deg;/30s ") << _("Left");
793  else {
794  if (ROTIND > 0)
795  rotStr << wxString::Format(_T("%3d&deg;/Min "), ROTIND)
796  << _("Right");
797  else if (ROTIND < 0)
798  rotStr << wxString::Format(_T("%3d&deg;/Min "), -ROTIND)
799  << _("Left");
800  else
801  rotStr = _T("0");
802  }
803  } else if (!b_SarAircraftPosnReport)
804  rotStr = _T("---");
805  }
806  }
807 
808  if (b_positionOnceValid && bGPSValid && (Range_NM >= 0.))
809  rngStr = FormatDistanceAdaptive(Range_NM);
810  else
811  rngStr = _T("---");
812 
813  int brg = (int)wxRound(Brg);
814  if (Brg > 359.5) brg = 0;
815  if (b_positionOnceValid && bGPSValid && (Brg >= 0.) && (Range_NM > 0.) &&
816  (fabs(Lat) < 85.)) {
817  wxString magString, trueString;
818  if (g_bShowMag)
819  magString << wxString::Format(wxString("%03d%c(M)"),
820  static_cast<int>(m_callbacks.get_mag(Brg)),
821  0x00B0);
822  if (g_bShowTrue)
823  trueString << wxString::Format( wxString("%03d%c "), (int)Brg, 0x00B0 );
824 
825  brgStr << trueString << magString;
826  } else
827  brgStr = _T("---");
828 
829  wxString turnRateHdr; // Blank if ATON or BASE or Special Position Report (9)
830  if ((Class != AIS_ATON) && (Class != AIS_BASE) && (Class != AIS_DSC) &&
831  (Class != AIS_METEO)) {
832  html << vertSpacer
833  << _T("<tr><td colspan=2><table width=100% border=0 cellpadding=0 ")
834  _T("cellspacing=0>")
835  << rowStart << _("Speed")
836  << _T("</font></td><td>&nbsp;</td><td><font size=-2>") << _("Course")
837  << _T("</font></td><td>&nbsp;</td><td align=right><font size=-2>");
838  if (!b_SarAircraftPosnReport) html << _("Heading");
839 
840  html << _T("</font></td></tr>") << rowStartH << _T("<b>") << sogStr
841  << _T("</b></td><td>&nbsp;</td><td><b>") << courseStr
842  << _T("</b></td><td>&nbsp;</td><td align=right><b>");
843  if (!b_SarAircraftPosnReport) html << hdgStr;
844  html << rowEnd << _T("</table></td></tr>") << vertSpacer;
845 
846  if (!b_SarAircraftPosnReport) turnRateHdr = _("Turn Rate");
847  }
848  if (Class != AIS_METEO) {
849  html << _T("<tr><td colspan=2><table width=100% border=0 cellpadding=0 ")
850  _T("cellspacing=0>")
851  << rowStart << _("Range")
852  << _T("</font></td><td>&nbsp;</td><td><font size=-2>") << _("Bearing")
853  << _T("</font></td><td>&nbsp;</td><td align=right><font size=-2>")
854  << turnRateHdr << _T("</font></td></tr>") << rowStartH << _T("<b>")
855  << rngStr << _T("</b></td><td>&nbsp;</td><td><b>") << brgStr
856  << _T("</b></td><td>&nbsp;</td><td align=right><b>");
857  if (!b_SarAircraftPosnReport) html << rotStr;
858  html << rowEnd << _T("</table></td></tr>") << vertSpacer;
859  }
860 
861  if (bCPA_Valid && Class != AIS_METEO) {
862  wxString tcpaStr;
863  tcpaStr << _T("</b> ") << _("in ") << _T("</td><td align=right><b>")
864  << FormatTimeAdaptive((int)(TCPA * 60.));
865 
866  html << /*vertSpacer << */ rowStart << _T("<font size=-2>") << _("CPA")
867  << _T("</font>") << rowEnd << rowStartH << _T("<b>")
868  << FormatDistanceAdaptive(CPA) << tcpaStr << rowEnd;
869  }
870 
871  if (Class != AIS_BASE && Class != AIS_METEO) {
872  if (blue_paddle == 1) {
873  html << rowStart << _("Inland Blue Flag") << rowEnd << rowStartH
874  << _T("<b>") << _("Clear") << rowEnd;
875  } else if (blue_paddle == 2) {
876  html << rowStart << _("Inland Blue Flag") << rowEnd << rowStartH
877  << _T("<b>") << _("Set") << rowEnd;
878  }
879  }
880 
881  if (b_SarAircraftPosnReport) {
882  wxString altStr;
883  if (altitude != 4095)
884  altStr.Printf(_T("%4d m"), altitude);
885  else
886  altStr = _("Unknown");
887 
888  html << rowStart << _("Altitude")
889  << _T("</font></td><td>&nbsp;</td><td><font size=-0>") << rowStartH
890  << _T("<b>") << altStr << _T("</b></td><td>&nbsp;</td><td><b>")
891  << rowEnd << _T("</table></td></tr>") << vertSpacer;
892  }
893 
894  if (Class == AIS_METEO) {
895  if (met_data.wind_kn < 122) {
896  double userwindspeed = toUsrWindSpeed(met_data.wind_kn);
897  wxString wspeed = wxString::Format("%0.1f %s %d%c", userwindspeed, getUsrWindSpeedUnit(),
898  met_data.wind_dir, 0x00B0);
899 
900  double userwindgustspeed = toUsrWindSpeed(met_data.wind_gust_kn);
901  wxString wspeedGust = wxString::Format("%.0f %s %d%c", userwindgustspeed,
902  getUsrWindSpeedUnit(), met_data.wind_gust_dir, 0x00B0);
903  if (met_data.wind_gust_kn >= 126) wspeedGust = wxEmptyString;
904 
905  html << vertSpacer << rowStart << _("Wind speed")
906  << _T("</font></td><td align=right><font size=-2>")
907  << _("Wind gust")
908  << _T("</font></td></tr>") << rowStartH << _T("<b>") << wspeed
909  << _T("</b></td><td align=right><b>") << wspeedGust << rowEnd;
910  }
911 
912  if (met_data.water_lev_dev < 30. || met_data.water_level > -32. || met_data.current < 25.5) {
913  wxString wlevel_txt = _("Water level deviation");
914  wxString wlevel;
915  if (met_data.water_lev_dev < 30.) {
916  double userlevel = toUsrDepth(met_data.water_lev_dev);
917  wlevel = wxString::Format("%.1f %s %s", userlevel, getUsrDepthUnit(),
918  ais_meteo_get_trend(met_data.water_lev_trend));
919  if (met_data.vertical_ref < 14) {
920  wlevel_txt = _("Water level dev. Ref: ");
921  wlevel_txt << aisMeteoWaterLevelRef(met_data.vertical_ref);
922  }
923 
924  if (met_data.water_lev_dev >= 30.) wlevel = wxEmptyString;
925 
926  } else if (met_data.water_level > -32.) {
927  double userlevel = toUsrDepth(met_data.water_level);
928  wlevel = wxString::Format("%.1f %s %s", userlevel, getUsrDepthUnit(),
929  ais_meteo_get_trend(met_data.water_lev_trend));
930  wlevel_txt = _("Water level");
931  if (met_data.water_level <= -32.) wlevel = wxEmptyString;
932  }
933 
934  wxString current = wxString::Format("%.1f kts %d%c", met_data.current,
935  met_data.curr_dir, 0x00B0);
936  if (met_data.current >= 25.5) current = wxEmptyString;
937 
938  html << vertSpacer << rowStart << wlevel_txt
939  << _T("</font></td><td align=right><font size=-2>")
940  << _("Surface current ") << _T("</font></td></tr>") << rowStartH
941  << _T("<b>") << wlevel << _T("</b></td><td align=right><b>")
942  << current << rowEnd;
943  }
944 
945  if (met_data.wave_height < 24.6 || met_data.swell_height < 24.6) {
946  double userwave = toUsrDepth(met_data.wave_height);
947  wxString wave =
948  wxString::Format("%.1f %s %d%c %d %s ", userwave, getUsrDepthUnit(),
949  met_data.wave_dir, 0x00B0,
950  met_data.wave_period, _("s"));
951  if (met_data.wave_height >= 24.6) wave = wxEmptyString;
952 
953  double userswell = toUsrDepth(met_data.swell_height);
954  wxString swell =
955  wxString::Format("%.1f %s %d%c %d %s", userswell, getUsrDepthUnit(),
956  met_data.swell_dir, 0x00B0,
957  met_data.swell_per, _("s"));
958  if (met_data.swell_height >= 25.) swell = wxEmptyString;
959 
960  html << vertSpacer << rowStart << _("Waves height & period")
961  << _T("</font></td><td align=right><font size=-2>")
962  << _("Swell height & period ")
963  << _T("</font></td></tr>") << rowStartH << _T("<b>") << wave
964  << _T("</b></td><td align=right><b>") << swell << rowEnd;
965  }
966 
967  if (met_data.air_temp != -102.4 || met_data.airpress < 1310) {
968  double usertemp = toUsrTemp(met_data.air_temp);
969  wxString airtemp =
970  wxString::Format("%.1f%c%s", usertemp, 0x00B0, getUsrTempUnit());
971  if (met_data.air_temp == -102.4) airtemp = wxEmptyString;
972 
973  wxString airpress = wxString::Format(
974  "%d hPa %s", met_data.airpress,
975  ais_meteo_get_trend(met_data.airpress_tend));
976  const int ap = met_data.airpress;
977  if (ap < 800 || ap >= 1310) airpress = wxEmptyString;
978 
979  html << vertSpacer << rowStart << _("Air Temperatur")
980  << _T("</font></td><td align=right><font size=-2>") << _("Air pressure")
981  << _T("</font></td></tr>") << rowStartH << _T("<b>") << airtemp
982  << _T("</b></td><td align=right><b>") << airpress << rowEnd;
983  }
984 
985  if (met_data.rel_humid < 101 || met_data.dew_point < 50.) {
986  wxString humid = wxString::Format("%d%c", met_data.rel_humid, '%');
987  if (met_data.rel_humid >= 101) humid = wxEmptyString;
988 
989  double usertempDew = toUsrTemp(met_data.dew_point);
990  wxString dewpoint =
991  wxString::Format("%.1f%c%s", usertempDew, 0x00B0, getUsrTempUnit());
992  if (met_data.dew_point >= 50.) dewpoint = wxEmptyString;
993 
994  html << vertSpacer << rowStart << _("Relative Humidity")
995  << _T("</font></td><td align=right><font size=-2>")
996  << _("Dew Point ") << _T("</font></td></tr>") << rowStartH
997  << _T("<b>") << humid << _T("</b></td><td align=right><b>")
998  << dewpoint << rowEnd;
999  }
1000 
1001  if (met_data.water_temp < 50.1 || met_data.seastate < 13) {
1002  double usertemp = toUsrTemp(met_data.water_temp);
1003  wxString watertemp =
1004  wxString::Format("%.1f%c%s", usertemp, 0x00B0, getUsrTempUnit());
1005  if (met_data.water_temp >= 50.1) watertemp = wxEmptyString;
1006 
1007  wxString seastate = wxString::Format("%d Bf ", met_data.seastate);
1008  if (met_data.seastate == 13) seastate = wxEmptyString;
1009 
1010  html << vertSpacer << rowStart << _("Water Temperatur")
1011  << _T("</font></td><td align=right><font size=-2>") << _("Sea state")
1012  << _T("</font></td></tr>") << rowStartH << _T("<b>") << watertemp
1013  << _T("</b></td><td align=right><b>") << seastate << rowEnd;
1014  }
1015 
1016  if (met_data.precipitation < 7 || met_data.hor_vis < 12.7) {
1017  wxString precip = wxString::Format("%s",
1018  aisMeteoPrecipType(met_data.precipitation));
1019  if (met_data.precipitation >= 6) precip = wxEmptyString;
1020 
1021  double userVisDist = toUsrDistance(met_data.hor_vis);
1022  wxString horVis =
1023  wxString::Format("%s%.1f %s", (met_data.hor_vis_GT ? ">" : ""),
1024  userVisDist, getUsrDistanceUnit());
1025  if (met_data.hor_vis >= 12.7) horVis = wxEmptyString;
1026  html << vertSpacer << rowStart << _("Precipitation")
1027  << _T("</font></td><td align=right><font size=-2>")
1028  << _("Horizontal Visibility") << _T("</font></td></tr>") << rowStartH
1029  << _T("<b>") << precip << _T("</b></td><td align=right><b>")
1030  << horVis << rowEnd;
1031  }
1032 
1033  if (met_data.salinity < 50. || met_data.ice < 2) {
1034  wxString sal = wxString::Format("%.1f%c", met_data.salinity, 0x2030);
1035  if (met_data.salinity >= 50.) sal = wxEmptyString;
1036 
1037  wxString icestatus = _("No");
1038  if (met_data.ice == 1) icestatus = _("Yes");
1039  if (met_data.ice >= 2) icestatus = wxEmptyString;
1040 
1041  html << vertSpacer << rowStart << _("Sea salinity")
1042  << _T("</font></td><td align=right><font size=-2>")
1043  << _("Ice status") << _T("</font></td></tr>") << rowStartH
1044  << _T("<b>") << sal << _T("</b></td><td align=right><b>")
1045  << icestatus << rowEnd;
1046  }
1047  }
1048  html << _T("</table>");
1049  return html;
1050 }
1051 
1052 wxString AisTargetData::GetRolloverString(void) {
1053  wxString result;
1054  wxString t;
1055  if (b_nameValid) {
1056  result.Append(_T("\""));
1057  result.Append(GetFullName());
1058  result.Append(_T("\" "));
1059  }
1060  if (Class != AIS_GPSG_BUDDY) {
1061  t.Printf(_T("%09d"), abs(MMSI));
1062  result.Append(t);
1063  result.Append(_T(" "));
1064  result.Append(GetCountryCode(false));
1065  }
1066  t = trimAISField(CallSign);
1067  if (t.Len()) {
1068  result.Append(_T(" ("));
1069  result.Append(t);
1070  result.Append(_T(")"));
1071  }
1072  if (g_bAISRolloverShowClass || (Class == AIS_SART)) {
1073  if (result.Len()) result.Append(_T("\n"));
1074  result.Append(_T("["));
1075  if (Class == AIS_ATON) {
1076  result.Append(wxGetTranslation(Get_class_string(true)));
1077  result.Append(_T(": "));
1078  result.Append(wxGetTranslation(Get_vessel_type_string(false)));
1079  } else if (b_SarAircraftPosnReport) {
1080  int airtype = (MMSI % 1000) / 100;
1081  result.Append(airtype == 5 ? _("SAR Helicopter") : _("SAR Aircraft"));
1082  } else
1083  result.Append(wxGetTranslation(Get_class_string(false)));
1084 
1085  result.Append(_T("] "));
1086  if ((Class != AIS_ATON) && (Class != AIS_BASE)) {
1087  if (Class == AIS_SART) {
1088  int mmsi_start = MMSI / 1000000;
1089  switch (mmsi_start) {
1090  case 970:
1091  break;
1092  case 972:
1093  result += _T("MOB");
1094  break;
1095  case 974:
1096  result += _T("EPIRB");
1097  break;
1098  default:
1099  result += _("Unknown");
1100  break;
1101  }
1102  }
1103 
1104  if (Class != AIS_SART && Class != AIS_METEO) {
1105  if (!b_SarAircraftPosnReport)
1106  result.Append(wxGetTranslation(Get_vessel_type_string(false)));
1107  }
1108 
1109  if ((Class != AIS_CLASS_B) && (Class != AIS_SART) && Class != AIS_DSC &&
1110  Class != AIS_METEO && !b_SarAircraftPosnReport) {
1111  if ((NavStatus <= 15) && (NavStatus >= 0)) {
1112  result.Append(_T(" ("));
1113  result.Append(wxGetTranslation(ais_get_status(NavStatus)));
1114  result.Append(_T(")"));
1115  }
1116  } else if (Class == AIS_SART) {
1117  result.Append(_T(" ("));
1118  if (NavStatus == RESERVED_14)
1119  result.Append(_("Active"));
1120  else if (NavStatus == UNDEFINED)
1121  result.Append(_("Testing"));
1122  result.Append(_T(")"));
1123  } else if (Class == AIS_DSC) {
1124  result.Append(_T(" ("));
1125  result.Append(GetNatureofDistress(m_dscNature));
1126  result.Append(_T(")"));
1127  }
1128  }
1129  }
1130 
1131  if (g_bAISRolloverShowCOG && ((SOG <= 102.2) || b_SarAircraftPosnReport) &&
1132  !((Class == AIS_ATON) || (Class == AIS_BASE) || (Class == AIS_METEO))) {
1133  if (result.Len()) result << _T("\n");
1134 
1135  double speed_show = toUsrSpeed(SOG);
1136  if (speed_show < 10.0)
1137  result << wxString::Format(_T("SOG %.2f "), speed_show)
1138  << getUsrSpeedUnit() << _T(" ");
1139  else if (speed_show < 100.0)
1140  result << wxString::Format(_T("SOG %.1f "), speed_show)
1141  << getUsrSpeedUnit() << _T(" ");
1142  else
1143  result << wxString::Format(_T("SOG %.0f "), speed_show)
1144  << getUsrSpeedUnit() << _T(" ");
1145 
1146  int crs = wxRound(COG);
1147  if (b_positionOnceValid) {
1148  if (crs < 360) {
1149  wxString magString, trueString;
1150  if (g_bShowMag)
1151  magString << wxString::Format(wxString("%03d%c(M) "),
1152  static_cast<int>(m_callbacks.get_mag(COG)),
1153  0x00B0);
1154  if (g_bShowTrue)
1155  trueString << wxString::Format( wxString("%03d%c "), (int)crs, 0x00B0 );
1156 
1157  result << trueString << magString;
1158  }
1159 
1160  else if (COG == 360.0)
1161  result << _(" COG Unavailable");
1162  else if (crs == 360)
1163  result << wxString(" COG 000\u00B0");
1164  } else
1165  result << _(" COG Unavailable");
1166  }
1167 
1168  if (g_bAISRolloverShowCPA && bCPA_Valid && Class != AIS_METEO) {
1169  if (result.Len()) result << _T("\n");
1170  result << _("CPA") << _T(" ") << FormatDistanceAdaptive(CPA) << _T(" ")
1171  << _("in") << _T(" ") << wxString::Format(_T("%.0f"), TCPA)
1172  << _T(" ") << _("min");
1173  }
1174  if (Class == AIS_METEO) {
1175  if (met_data.wind_kn < 122) {
1176  if (result.Len()) result << "\n";
1177  double userwindspeed = toUsrWindSpeed(met_data.wind_kn);
1178  result << _("Wind speed");
1179  result << wxString::Format(": %0.1f %s", userwindspeed, getUsrWindSpeedUnit())
1180  << wxString::Format(" %d%c ", met_data.wind_dir, 0x00B0);
1181  }
1182 
1183  if (met_data.water_lev_dev < 30.) {
1184  if (result.Len()) result << "\n";
1185  result << _("Water level deviation");
1186  double userdepth;
1187  userdepth = toUsrDepth(met_data.water_lev_dev);
1188  result << wxString::Format(": %.1f %s", userdepth,
1189  getUsrDepthUnit());
1190 
1191  } else if (met_data.water_level > -32.) {
1192  if (result.Len()) result << "\n";
1193  result << _("Water level");
1194  double userdepth;
1195  userdepth = toUsrDepth(met_data.water_level);
1196  result << wxString::Format(": %.1f %s", userdepth, getUsrDepthUnit());
1197  }
1198 
1199  if (met_data.current < 25.) {
1200  if (result.Len()) result << "\n";
1201  result << _("Current");
1202  result << wxString::Format(": %.1f ", met_data.current) << _("kts")
1203  << wxString::Format(" %d%c ", met_data.curr_dir, 0x00B0);
1204  }
1205 
1206  if (met_data.wave_height < 24.6) {
1207  if (result.Len()) result << "\n";
1208  double userwh = toUsrDepth(met_data.wave_height);
1209  result << _("Wave height")
1210  << wxString::Format(": %.1f %s", userwh, getUsrDepthUnit())
1211  << " / " << met_data.wave_period << " " << _("s");
1212  }
1213 
1214  if (met_data.water_temp < 50.) {
1215  if (result.Len()) result << "\n";
1216  double usertemp = toUsrTemp(met_data.water_temp);
1217  result << _("Water temp");
1218  result << wxString::Format(": %.1f%c", usertemp, 0x00B0)
1219  << getUsrTempUnit();
1220  }
1221 
1222  if (met_data.air_temp != -102.4) {
1223  if (result.Len()) result << "\n";
1224  double usertemp = toUsrTemp(met_data.air_temp);
1225  result << _("Air temp");
1226  result << wxString::Format(": %.1f%c", usertemp, 0x00B0)
1227  << getUsrTempUnit() << " ";
1228  }
1229 
1230  if (met_data.airpress > 799 && met_data.airpress < 1310) {
1231  if (met_data.air_temp == -102.4 && result.Len()) result << "\n";
1232  result << _("Air press");
1233  result << wxString::Format(": %d hPa", met_data.airpress);
1234  }
1235 
1236  if (met_data.hor_vis < 12.) {
1237  if (result.Len()) result << "\n";
1238  double userVisDist = toUsrDistance(met_data.hor_vis);
1239  wxString horVis =
1240  wxString::Format(": %s%.1f %s", (met_data.hor_vis_GT ? ">" : ""),
1241  userVisDist, getUsrDistanceUnit());
1242  result << _("Visibility") << horVis;
1243  }
1244  }
1245  return result;
1246 }
1247 
1248 wxString AisTargetData::Get_vessel_type_string(bool b_short) {
1249  int i = 19;
1250  if (Class == AIS_ATON) {
1251  i = ShipType + 20;
1252  } else
1253  switch (ShipType) {
1254  case 30:
1255  i = 0;
1256  break;
1257  case 31:
1258  i = 1;
1259  break;
1260  case 32:
1261  i = 2;
1262  break;
1263  case 33:
1264  i = 3;
1265  break;
1266  case 34:
1267  i = 4;
1268  break;
1269  case 35:
1270  i = 5;
1271  break;
1272  case 36:
1273  i = 6;
1274  break;
1275  case 37:
1276  i = 7;
1277  break;
1278  case 50:
1279  i = 9;
1280  break;
1281  case 51:
1282  i = 10;
1283  break;
1284  case 52:
1285  i = 11;
1286  break;
1287  case 53:
1288  i = 12;
1289  break;
1290  case 54:
1291  i = 13;
1292  break;
1293  case 55:
1294  i = 14;
1295  break;
1296  case 58:
1297  i = 15;
1298  break;
1299  default:
1300  i = 19;
1301  break;
1302  }
1303 
1304  if ((Class == AIS_CLASS_B) || (Class == AIS_CLASS_A)) {
1305  if ((ShipType >= 40) && (ShipType < 50)) i = 8;
1306 
1307  if ((ShipType >= 60) && (ShipType < 70)) i = 16;
1308 
1309  if ((ShipType >= 70) && (ShipType < 80)) i = 17;
1310 
1311  if ((ShipType >= 80) && (ShipType < 90)) i = 18;
1312  } else if (Class == AIS_GPSG_BUDDY)
1313  i = 52;
1314  else if (Class == AIS_ARPA)
1315  i = 55;
1316  else if (Class == AIS_APRS)
1317  i = 56;
1318  else if (Class == AIS_DSC)
1319  i = (ShipType == 12 || ShipType == 16) ? 54 : 53; // 12 & 16 is distress
1320 
1321  if (!b_short)
1322  return ais_get_type(i);
1323  else
1324  return ais_get_short_type(i);
1325 }
1326 
1327 wxString AisTargetData::Get_class_string(bool b_short) {
1328  switch (Class) {
1329  case AIS_CLASS_A:
1330  return _("A");
1331  case AIS_CLASS_B:
1332  return _("B");
1333  case AIS_ATON:
1334  return b_short ? _("AtoN") : _("Aid to Navigation");
1335  case AIS_BASE:
1336  return b_short ? _("Base") : _("Base Station");
1337  case AIS_GPSG_BUDDY:
1338  return b_short ? _("Buddy") : _("GPSGate Buddy");
1339  case AIS_DSC:
1340  if (ShipType == 12 || ( ShipType == 16 && m_dscNature < 13))
1341  return b_short ? _("DSC") : _("DSC Distress");
1342  else
1343  return b_short ? _("DSC") : _("DSC Position Report");
1344  case AIS_SART:
1345  return b_short ? _("SART") : _("SART");
1346  case AIS_ARPA:
1347  return b_short ? _("ARPA") : _("ARPA");
1348  case AIS_APRS:
1349  return b_short ? _("APRS") : _("APRS Position Report");
1350  case AIS_METEO:
1351  return b_short ? _("METEO") : _("Meteorologic");
1352 
1353  default:
1354  return b_short ? _("Unk") : _("Unknown");
1355  }
1356 }
1357 
1358 wxString AisTargetData::GetNatureofDistress(int dscnature) {
1359  // Natures of distress from: Rec. ITU-R M.493-10.
1360  wxString dscDistressType[] = { _("Fire, explosion"), _("Flooding"),
1361  _("Collision"), _("Grounding"),
1362  _("Listing, in danger of capsizing"), _("Sinking"),
1363  _("Disabled and adrift"), _("Undesignated distress"),
1364  _("Abandoning ship"), _("Piracy/armed robbery attack"),
1365  _("Man overboard"), _T("-"), _("EPIRB emission") };
1366  if (dscnature >= 0 && dscnature < 13)
1367  return dscDistressType[dscnature];
1368 
1369  return wxEmptyString;
1370 }
1371 
1372 void AisTargetData::Toggle_AIS_CPA(void) {
1373  b_show_AIS_CPA = !b_show_AIS_CPA ? true : false;
1374 }
1375 
1376 void AisTargetData::ToggleShowTrack(void) {
1377  b_show_track = !b_show_track ? true : false;
1378 }
1379 
1380 bool AisTargetData::IsValidMID(int mid) {
1381  if (mid >= 201 && mid <= 775) return true;
1382  return false;
1383 }
1384 
1385 // Get country name and code according to ITU 2023-02
1386 // (http://www.itu.int/en/ITU-R/terrestrial/fmd/Pages/mid.aspx)
1387 wxString AisTargetData::GetCountryCode( bool b_CntryLongStr) {
1388  /***** Check for a valid MID *****/
1389  // Meteo adaption
1390  int tmpMmsi = met_data.original_mmsi ? met_data.original_mmsi : MMSI;
1391  // First check the most common case
1392  int nMID = tmpMmsi / 1000000;
1393  if (!IsValidMID(nMID) || Class == AIS_ATON) {
1394  // SART, MOB, EPIRB starts with 97 and don't use MID (ITU-R M.1371-5)
1395  // or healthy check
1396  if (tmpMmsi < 1000 || 97 == tmpMmsi / 10000000) return wxEmptyString;
1397 
1398  // Find MID when not in first position like e.g. SAR/ATON
1399  wxString s_mmsi;
1400  s_mmsi << tmpMmsi;
1401  bool foundMID = false;
1402  size_t i;
1403  i = nMID > 900 ? 2 : 0; // AIS_ATON or others where MMSI starts with 9x
1404  for (i; i < s_mmsi.length() - 3; i++) {
1405  nMID = wxAtoi(s_mmsi.Mid(i, 3));
1406  if (IsValidMID(nMID)) {
1407  foundMID = true;
1408  break;
1409  }
1410  }
1411  if (!foundMID) return wxEmptyString;
1412  }
1413 
1414 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1415 
1416  switch (nMID) {
1417  case 201:
1418  return b_CntryLongStr ? _("Albania") : _T("AL");
1419  case 202:
1420  return b_CntryLongStr ? _("Andorra") : _T("AD");
1421  case 203:
1422  return b_CntryLongStr ? _("Austria") : _T("AT");
1423  case 204:
1424  return b_CntryLongStr ? _("Azores") : _T("AZ");
1425  case 205:
1426  return b_CntryLongStr ? _("Belgium") : _T("BE");
1427  case 206:
1428  return b_CntryLongStr ? _("Belarus") : _T("BY");
1429  case 207:
1430  return b_CntryLongStr ? _("Bulgaria") : _T("BG");
1431  case 208:
1432  return b_CntryLongStr ? _("Vatican City State") : _T("VA");
1433  case 209:
1434  case 210:
1435  return b_CntryLongStr ? _("Cyprus") : _T("CY");
1436  case 211:
1437  return b_CntryLongStr ? _("Germany") : _T("DE");
1438  case 212:
1439  return b_CntryLongStr ? _("Cyprus") : _T("CY");
1440  case 213:
1441  return b_CntryLongStr ? _("Georgia") : _T("GE");
1442  case 214:
1443  return b_CntryLongStr ? _("Moldova") : _T("MD");
1444  case 215:
1445  return b_CntryLongStr ? _("Malta") : _T("MT");
1446  case 216:
1447  return b_CntryLongStr ? _("Armenia") : _T("AM");
1448  case 218:
1449  return b_CntryLongStr ? _("Germany") : _T("DE");
1450  case 219:
1451  case 220:
1452  return b_CntryLongStr ? _("Denmark") : _T("DK");
1453  case 224:
1454  return b_CntryLongStr ? _("Spain") : _T("ES");
1455  case 225:
1456  return b_CntryLongStr ? _("Spain") : _T("ES");
1457  case 226:
1458  case 227:
1459  case 228:
1460  return b_CntryLongStr ? _("France") : _T("FR");
1461  case 229:
1462  return b_CntryLongStr ? _("Malta") : _T("MT");
1463  case 230:
1464  return b_CntryLongStr ? _("Finland") : _T("FI");
1465  case 231:
1466  return b_CntryLongStr ? _("Faroe Islands") : _T("FO");
1467  case 232:
1468  case 233:
1469  case 234:
1470  case 235:
1471  return b_CntryLongStr ? _("Great Britain") : _T("GB");
1472  case 236:
1473  return b_CntryLongStr ? _("Gibraltar") : _T("GI");
1474  case 237:
1475  return b_CntryLongStr ? _("Greece") : _T("GR");
1476  case 238:
1477  return b_CntryLongStr ? _("Croatia") : _T("HR");
1478  case 239:
1479  case 240:
1480  case 241:
1481  return b_CntryLongStr ? _("Greece") : _T("GR");
1482  case 242:
1483  return b_CntryLongStr ? _("Morocco") : _T("MA");
1484  case 243:
1485  return b_CntryLongStr ? _("Hungary") : _T("HU");
1486  case 244:
1487  case 245:
1488  case 246:
1489  return b_CntryLongStr ? _("Netherlands") : _T("NL");
1490  case 247:
1491  return b_CntryLongStr ? _("Italy") : _T("IT");
1492  case 248:
1493  case 249:
1494  return b_CntryLongStr ? _("Malta") : _T("MT");
1495  case 250:
1496  return b_CntryLongStr ? _("Ireland") : _T("IE");
1497  case 251:
1498  return b_CntryLongStr ? _("Iceland") : _T("IS");
1499  case 252:
1500  return b_CntryLongStr ? _("Liechtenstein") : _T("LI");
1501  case 253:
1502  return b_CntryLongStr ? _("Luxembourg") : _T("LU");
1503  case 254:
1504  return b_CntryLongStr ? _("Monaco") : _T("MC");
1505  case 255:
1506  return b_CntryLongStr ? _("Madeira") : _T("PT");
1507  case 256:
1508  return b_CntryLongStr ? _("Malta") : _T("MT");
1509  case 257:
1510  case 258:
1511  case 259:
1512  return b_CntryLongStr ? _("Norway") : _T("NO");
1513  case 261:
1514  return b_CntryLongStr ? _("Poland") : _T("PL");
1515  case 262:
1516  return b_CntryLongStr ? _("Montenegro") : _T("ME");
1517  case 263:
1518  return b_CntryLongStr ? _("Portugal") : _T("PT");
1519  case 264:
1520  return b_CntryLongStr ? _("Romania") : _T("RO");
1521  case 265:
1522  case 266:
1523  return b_CntryLongStr ? _("Sweden") : _T("SE");
1524  case 267:
1525  return b_CntryLongStr ? _("Slovak Republic") : _T("SK");
1526  case 268:
1527  return b_CntryLongStr ? _("San Marino") : _T("SM");
1528  case 269:
1529  return b_CntryLongStr ? _("Switzerland") : _T("CH");
1530  case 270:
1531  return b_CntryLongStr ? _("Czech Republic") : _T("CZ");
1532  case 271:
1533  return b_CntryLongStr ? _("Turkey") : _T("TR");
1534  case 272:
1535  return b_CntryLongStr ? _("Ukraine") : _T("UA");
1536  case 273:
1537  return b_CntryLongStr ? _("Russian") : _T("RU");
1538  case 274:
1539  return b_CntryLongStr ? _("Macedonia") : _T("MK");
1540  case 275:
1541  return b_CntryLongStr ? _("Latvia") : _T("LV");
1542  case 276:
1543  return b_CntryLongStr ? _("Estonia") : _T("EE");
1544  case 277:
1545  return b_CntryLongStr ? _("Lithuania") : _T("LT");
1546  case 278:
1547  return b_CntryLongStr ? _("Slovenia") : _T("SI");
1548  case 279:
1549  return b_CntryLongStr ? _("Serbia") : _T("RS");
1550  case 301:
1551  return b_CntryLongStr ? _("Anguilla") : _T("AI");
1552  case 303:
1553  return b_CntryLongStr ? _("Alaska") : _T("AK");
1554  case 304:
1555  case 305:
1556  return b_CntryLongStr ? _("Antigua and Barbuda") : _T("AG");
1557  case 306:
1558  return b_CntryLongStr ? _("Antilles") : _T("AN");
1559  case 307:
1560  return b_CntryLongStr ? _("Aruba") : _T("AW");
1561  case 308:
1562  case 309:
1563  return b_CntryLongStr ? _("Bahamas") : _T("BS");
1564  case 310:
1565  return b_CntryLongStr ? _("Bermuda") : _T("BM");
1566  case 311:
1567  return b_CntryLongStr ? _("Bahamas") : _T("BS");
1568  case 312:
1569  return b_CntryLongStr ? _("Belize") : _T("BZ");
1570  case 314:
1571  return b_CntryLongStr ? _("Barbados") : _T("BB");
1572  case 316:
1573  return b_CntryLongStr ? _("Canada") : _T("CA");
1574  case 319:
1575  return b_CntryLongStr ? _("Cayman Islands") : _T("KY");
1576  case 321:
1577  return b_CntryLongStr ? _("Costa Rica") : _T("CR");
1578  case 323:
1579  return b_CntryLongStr ? _("Cuba") : _T("CU");
1580  case 325:
1581  return b_CntryLongStr ? _("Dominica") : _T("DM");
1582  case 327:
1583  return b_CntryLongStr ? _("Dominican Republic") : _T("DM");
1584  case 329:
1585  return b_CntryLongStr ? _("Guadeloupe") : _T("GP");
1586  case 330:
1587  return b_CntryLongStr ? _("Grenada") : _T("GD");
1588  case 331:
1589  return b_CntryLongStr ? _("Greenland") : _T("GL");
1590  case 332:
1591  return b_CntryLongStr ? _("Guatemala") : _T("GT");
1592  case 334:
1593  return b_CntryLongStr ? _("Honduras") : _T("HN");
1594  case 336:
1595  return b_CntryLongStr ? _("Haiti") : _T("HT");
1596  case 338:
1597  return b_CntryLongStr ? _("United States of America") : _T("US");
1598  case 339:
1599  return b_CntryLongStr ? _("Jamaica") : _T("JM");
1600  case 341:
1601  return b_CntryLongStr ? _("Saint Kitts and Nevis") : _T("KN");
1602  case 343:
1603  return b_CntryLongStr ? _("Saint Lucia") : _T("LC");
1604  case 345:
1605  return b_CntryLongStr ? _("Mexico") : _T("MX");
1606  case 347:
1607  return b_CntryLongStr ? _("Martinique") : _T("MQ");
1608  case 348:
1609  return b_CntryLongStr ? _("Montserrat") : _T("MS");
1610  case 350:
1611  return b_CntryLongStr ? _("Nicaragua") : _T("NI");
1612  case 351:
1613  case 352:
1614  case 353:
1615  case 354:
1616  case 355:
1617  case 356:
1618  case 357:
1619  return b_CntryLongStr ? _("Panama") : _T("PA");
1620  case 358:
1621  return b_CntryLongStr ? _("Puerto Rico") : _T("PR");
1622  case 359:
1623  return b_CntryLongStr ? _("El Salvador") : _T("SV");
1624  case 361:
1625  return b_CntryLongStr ? _("Saint Pierre and Miquelon") : _T("PM");
1626  case 362:
1627  return b_CntryLongStr ? _("Trinidad and Tobago") : _T("TT");
1628  case 364:
1629  return b_CntryLongStr ? _("Turks and Caicos Islands") : _T("TC");
1630  case 366:
1631  case 367:
1632  case 368:
1633  case 369:
1634  return b_CntryLongStr ? _("United States of America") : _T("US");
1635  case 370:
1636  case 371:
1637  case 372:
1638  case 373:
1639  case 374:
1640  return b_CntryLongStr ? _("Panama") : _T("PA");
1641  case 375:
1642  case 376:
1643  case 377:
1644  return b_CntryLongStr ? _("Saint Vincent and the Grenadines") : _T("VC");
1645  case 378:
1646  return b_CntryLongStr ? _("British Virgin Islands") : _T("VG");
1647  case 379:
1648  return b_CntryLongStr ? _("United States Virgin Islands") : _T("AE");
1649  case 401:
1650  return b_CntryLongStr ? _("Afghanistan") : _T("AF");
1651  case 403:
1652  return b_CntryLongStr ? _("Saudi Arabia") : _T("SA");
1653  case 405:
1654  return b_CntryLongStr ? _("Bangladesh") : _T("BD");
1655  case 408:
1656  return b_CntryLongStr ? _("Bahrain") : _T("BH");
1657  case 410:
1658  return b_CntryLongStr ? _("Bhutan") : _T("BT");
1659  case 412:
1660  case 413:
1661  case 414:
1662  return b_CntryLongStr ? _("China") : _T("CN");
1663  case 416:
1664  return b_CntryLongStr ? _("Taiwan") : _T("TW");
1665  case 417:
1666  return b_CntryLongStr ? _("Sri Lanka") : _T("LK");
1667  case 419:
1668  return b_CntryLongStr ? _("India") : _T("IN");
1669  case 422:
1670  return b_CntryLongStr ? _("Iran") : _T("IR");
1671  case 423:
1672  return b_CntryLongStr ? _("Azerbaijani Republic") : _T("AZ");
1673  case 425:
1674  return b_CntryLongStr ? _("Iraq") : _T("IQ");
1675  case 428:
1676  return b_CntryLongStr ? _("Israel") : _T("IL");
1677  case 431:
1678  return b_CntryLongStr ? _("Japan") : _T("JP");
1679  case 432:
1680  return b_CntryLongStr ? _("Japan") : _T("JP");
1681  case 434:
1682  return b_CntryLongStr ? _("Turkmenistan") : _T("TM");
1683  case 436:
1684  return b_CntryLongStr ? _("Kazakhstan") : _T("KZ");
1685  case 437:
1686  return b_CntryLongStr ? _("Uzbekistan") : _T("UZ");
1687  case 438:
1688  return b_CntryLongStr ? _("Jordan") : _T("JO");
1689  case 440:
1690  case 441:
1691  return b_CntryLongStr ? _("Korea") : _T("KR");
1692  case 443:
1693  return b_CntryLongStr ? _("Palestine") : _T("PS");
1694  case 445:
1695  return b_CntryLongStr ? _("People's Rep. of Korea") : _T("KP");
1696  case 447:
1697  return b_CntryLongStr ? _("Kuwait") : _T("KW");
1698  case 450:
1699  return b_CntryLongStr ? _("Lebanon") : _T("LB");
1700  case 451:
1701  return b_CntryLongStr ? _("Kyrgyz Republic") : _T("KG");
1702  case 453:
1703  return b_CntryLongStr ? _("Macao") : _T("MO");
1704  case 455:
1705  return b_CntryLongStr ? _("Maldives") : _T("MV");
1706  case 457:
1707  return b_CntryLongStr ? _("Mongolia") : _T("MN");
1708  case 459:
1709  return b_CntryLongStr ? _("Nepal") : _T("NP");
1710  case 461:
1711  return b_CntryLongStr ? _("Oman") : _T("OM");
1712  case 463:
1713  return b_CntryLongStr ? _("Pakistan") : _T("PK");
1714  case 466:
1715  return b_CntryLongStr ? _("Qatar") : _T("QA");
1716  case 468:
1717  return b_CntryLongStr ? _("Syrian Arab Republic") : _T("SY");
1718  case 470:
1719  case 471:
1720  return b_CntryLongStr ? _("United Arab Emirates") : _T("AE");
1721  case 472:
1722  return b_CntryLongStr ? _("Tajikistan") : _T("TJ");
1723  case 473:
1724  case 475:
1725  return b_CntryLongStr ? _("Yemen") : _T("YE");
1726  case 477:
1727  return b_CntryLongStr ? _("Hong Kong") : _T("HK");
1728  case 478:
1729  return b_CntryLongStr ? _("Bosnia and Herzegovina") : _T("BA");
1730  case 501:
1731  return b_CntryLongStr ? _("Adelie Land") : _T("TF");
1732  case 503:
1733  return b_CntryLongStr ? _("Australia") : _T("AU");
1734  case 506:
1735  return b_CntryLongStr ? _("Myanmar") : _T("MM");
1736  case 508:
1737  return b_CntryLongStr ? _("Brunei Darussalam") : _T("BN");
1738  case 510:
1739  return b_CntryLongStr ? _("Micronesia") : _T("FM");
1740  case 511:
1741  return b_CntryLongStr ? _("Palau") : _T("PW");
1742  case 512:
1743  return b_CntryLongStr ? _("New Zealand") : _T("NZ");
1744  case 514:
1745  case 515:
1746  return b_CntryLongStr ? _("Cambodia") : _T("KH");
1747  case 516:
1748  return b_CntryLongStr ? _("Christmas Island") : _T("CX");
1749  case 518:
1750  return b_CntryLongStr ? _("Cook Islands") : _T("CK");
1751  case 520:
1752  return b_CntryLongStr ? _("Fiji") : _T("FJ");
1753  case 523:
1754  return b_CntryLongStr ? _("Cocos (Keeling) Islands") : _T("CC");
1755  case 525:
1756  return b_CntryLongStr ? _("Indonesia") : _T("ID");
1757  case 529:
1758  return b_CntryLongStr ? _("Kiribati") : _T("KI");
1759  case 531:
1760  return b_CntryLongStr ? _("Lao People's Dem. Rep.") : _T("LA");
1761  case 533:
1762  return b_CntryLongStr ? _("Malaysia") : _T("MY");
1763  case 536:
1764  return b_CntryLongStr ? _("Northern Mariana Islands") : _T("MP");
1765  case 538:
1766  return b_CntryLongStr ? _("Marshall Islands") : _T("MH");
1767  case 540:
1768  return b_CntryLongStr ? _("New Caledonia") : _T("NC");
1769  case 542:
1770  return b_CntryLongStr ? _("Niue") : _T("NU");
1771  case 544:
1772  return b_CntryLongStr ? _("Nauru") : _T("NR");
1773  case 546:
1774  return b_CntryLongStr ? _("French Polynesia") : _T("PF");
1775  case 548:
1776  return b_CntryLongStr ? _("Philippines") : _T("PH");
1777  case 550:
1778  return b_CntryLongStr ? _("East Timor") : _T("TL");
1779  case 553:
1780  return b_CntryLongStr ? _("Papua New Guinea") : _T("PG");
1781  case 555:
1782  return b_CntryLongStr ? _("Pitcairn Island") : _T("PN");
1783  case 557:
1784  return b_CntryLongStr ? _("Solomon Islands") : _T("SB");
1785  case 559:
1786  return b_CntryLongStr ? _("American Samoa") : _T("AS");
1787  case 561:
1788  return b_CntryLongStr ? _("Samoa") : _T("WS");
1789  case 563:
1790  case 564:
1791  case 565:
1792  case 566:
1793  return b_CntryLongStr ? _("Singapore") : _T("SG");
1794  case 567:
1795  return b_CntryLongStr ? _("Thailand") : _T("TH");
1796  case 570:
1797  return b_CntryLongStr ? _("Tonga") : _T("TO");
1798  case 572:
1799  return b_CntryLongStr ? _("Tuvalu") : _T("TV");
1800  case 574:
1801  return b_CntryLongStr ? _("Viet Nam") : _T("VN");
1802  case 576:
1803  case 577:
1804  return b_CntryLongStr ? _("Vanuatu") : _T("VU");
1805  case 578:
1806  return b_CntryLongStr ? _("Wallis and Futuna Islands") : _T("WF");
1807  case 601:
1808  return b_CntryLongStr ? _("South Africa") : _T("ZA");
1809  case 603:
1810  return b_CntryLongStr ? _("Angola") : _T("AO");
1811  case 605:
1812  return b_CntryLongStr ? _("Algeria") : _T("DZ");
1813  case 607:
1814  return b_CntryLongStr ? _("Saint Paul") : _T("TF");
1815  case 608:
1816  return b_CntryLongStr ? _("Ascension Island") : _T("SH");
1817  case 609:
1818  return b_CntryLongStr ? _("Burundi") : _T("BI");
1819  case 610:
1820  return b_CntryLongStr ? _("Benin") : _T("BJ");
1821  case 611:
1822  return b_CntryLongStr ? _("Botswana") : _T("BW");
1823  case 612:
1824  return b_CntryLongStr ? _("Central African Republic") : _T("CF");
1825  case 613:
1826  return b_CntryLongStr ? _("Cameroon") : _T("CM");
1827  case 615:
1828  return b_CntryLongStr ? _("Congo") : _T("CD");
1829  case 616:
1830  return b_CntryLongStr ? _("Comoros") : _T("KM");
1831  case 617:
1832  return b_CntryLongStr ? _("Capo Verde") : _T("CV");
1833  case 618:
1834  return b_CntryLongStr ? _("Crozet Archipelago") : _T("TF");
1835  case 619:
1836  return b_CntryLongStr ? _("Ivory Coast") : _T("CI");
1837  case 620:
1838  return b_CntryLongStr ? _("Comoros (Union of the)") : _T("KM");
1839  case 621:
1840  return b_CntryLongStr ? _("Djibouti") : _T("DJ");
1841  case 622:
1842  return b_CntryLongStr ? _("Egypt") : _T("EG");
1843  case 624:
1844  return b_CntryLongStr ? _("Ethiopia") : _T("ET");
1845  case 625:
1846  return b_CntryLongStr ? _("Eritrea") : _T("ER");
1847  case 626:
1848  return b_CntryLongStr ? _("Gabonese Republic") : _T("GA");
1849  case 627:
1850  return b_CntryLongStr ? _("Ghana") : _T("GH");
1851  case 629:
1852  return b_CntryLongStr ? _("Gambia") : _T("GM");
1853  case 630:
1854  return b_CntryLongStr ? _("Guinea-Bissau") : _T("GW");
1855  case 631:
1856  return b_CntryLongStr ? _("Equatorial Guinea") : _T("GQ");
1857  case 632:
1858  return b_CntryLongStr ? _("Guinea") : _T("GN");
1859  case 633:
1860  return b_CntryLongStr ? _("Burkina Faso") : _T("BF");
1861  case 634:
1862  return b_CntryLongStr ? _("Kenya") : _T("KE");
1863  case 635:
1864  return b_CntryLongStr ? _("Kerguelen Islands") : _T("TF");
1865  case 636:
1866  case 637:
1867  return b_CntryLongStr ? _("Liberia") : _T("LR");
1868  case 638:
1869  return b_CntryLongStr ? _("South Sudan (Republic of)") : _T("SS");
1870  case 642:
1871  return b_CntryLongStr ? _("Libya") : _T("LY");
1872  case 644:
1873  return b_CntryLongStr ? _("Lesotho") : _T("LS");
1874  case 645:
1875  return b_CntryLongStr ? _("Mauritius") : _T("MU");
1876  case 647:
1877  return b_CntryLongStr ? _("Madagascar") : _T("MG");
1878  case 649:
1879  return b_CntryLongStr ? _("Mali") : _T("ML");
1880  case 650:
1881  return b_CntryLongStr ? _("Mozambique") : _T("MZ");
1882  case 654:
1883  return b_CntryLongStr ? _("Mauritania") : _T("MR");
1884  case 655:
1885  return b_CntryLongStr ? _("Malawi") : _T("MW");
1886  case 656:
1887  return b_CntryLongStr ? _("Niger") : _T("NE");
1888  case 657:
1889  return b_CntryLongStr ? _("Nigeria") : _T("NG");
1890  case 659:
1891  return b_CntryLongStr ? _("Namibia") : _T("NA");
1892  case 660:
1893  return b_CntryLongStr ? _("Reunion") : _T("RE");
1894  case 661:
1895  return b_CntryLongStr ? _("Rwanda") : _T("RW");
1896  case 662:
1897  return b_CntryLongStr ? _("Sudan") : _T("SD");
1898  case 663:
1899  return b_CntryLongStr ? _("Senegal") : _T("SN");
1900  case 664:
1901  return b_CntryLongStr ? _("Seychelles") : _T("SC");
1902  case 665:
1903  return b_CntryLongStr ? _("Saint Helena") : _T("SH");
1904  case 666:
1905  return b_CntryLongStr ? _("Somalia") : _T("SO");
1906  case 667:
1907  return b_CntryLongStr ? _("Sierra Leone") : _T("SL");
1908  case 668:
1909  return b_CntryLongStr ? _("Sao Tome and Principe") : _T("ST");
1910  case 669:
1911  return b_CntryLongStr ? _("Eswatini") : _T("SZ");
1912  case 670:
1913  return b_CntryLongStr ? _("Chad") : _T("TD");
1914  case 671:
1915  return b_CntryLongStr ? _("Togolese Republic") : _T("TG");
1916  case 672:
1917  return b_CntryLongStr ? _("Tunisia") : _T("TN");
1918  case 674:
1919  return b_CntryLongStr ? _("Tanzania") : _T("TZ");
1920  case 675:
1921  return b_CntryLongStr ? _("Uganda") : _T("UG");
1922  case 676:
1923  return b_CntryLongStr ? _("Dem Rep.of the Congo") : _T("CD");
1924  case 677:
1925  return b_CntryLongStr ? _("Tanzania") : _T("TZ");
1926  case 678:
1927  return b_CntryLongStr ? _("Zambia") : _T("ZM");
1928  case 679:
1929  return b_CntryLongStr ? _("Zimbabwe") : _T("ZW");
1930  case 701:
1931  return b_CntryLongStr ? _("Argentine Republic") : _T("AR");
1932  case 710:
1933  return b_CntryLongStr ? _("Brazil") : _T("BR");
1934  case 720:
1935  return b_CntryLongStr ? _("Bolivia") : _T("BO");
1936  case 725:
1937  return b_CntryLongStr ? _("Chile") : _T("CL");
1938  case 730:
1939  return b_CntryLongStr ? _("Colombia") : _T("CO");
1940  case 735:
1941  return b_CntryLongStr ? _("Ecuador") : _T("EC");
1942  case 740:
1943  return b_CntryLongStr ? _("Falkland Islands") : _T("FK");
1944  case 745:
1945  return b_CntryLongStr ? _("France - Guiana") : _T("GY");
1946  case 750:
1947  return b_CntryLongStr ? _("Guyana") : _T("GY");
1948  case 755:
1949  return b_CntryLongStr ? _("Paraguay") : _T("PY");
1950  case 760:
1951  return b_CntryLongStr ? _("Peru") : _T("PE");
1952  case 765:
1953  return b_CntryLongStr ? _("Suriname") : _T("SR");
1954  case 770:
1955  return b_CntryLongStr ? _("Uruguay") : _T("UY");
1956  case 775:
1957  return b_CntryLongStr ? _("Venezuela") : _T("VE");
1958 
1959  default:
1960  return wxEmptyString;
1961  }
1962 #else
1963  return wxEmptyString;
1964 #endif
1965 }
1966 
1967 
1968 wxString ais_get_type(int index) {
1969  static const wxString ais_type[] = {
1970  _("Fishing Vessel"), // 30 0
1971  _("Towing Vessel"), // 31 1
1972  _("Towing Vessel, Long"), // 32 2
1973  _("Dredger"), // 33 3
1974  _("Diving Ops Vessel"), // 34 4
1975  _("Military Vessel"), // 35 5
1976  _("Sailing Vessel"), // 36 6
1977  _("Pleasure craft"), // 37 7
1978  _("High Speed Craft"), // 4x 8
1979  _("Pilot Vessel"), // 50 9
1980  _("Search and Rescue Vessel"), // 51 10
1981  _("Tug"), // 52 11
1982  _("Port Tender"), // 53 12
1983  _("Pollution Control Vessel"), // 54 13
1984  _("Law Enforcement Vessel"), // 55 14
1985  _("Medical Transport"), // 58 15
1986  _("Passenger Ship"), // 6x 16
1987  _("Cargo Ship"), // 7x 17
1988  _("Tanker"), // 8x 18
1989  _("Unknown"), // 19
1990  _("Unspecified"), // 00 20
1991  _("Reference Point"), // 01 21
1992  _("RACON"), // 02 22
1993  _("Fixed Structure"), // 03 23
1994  _("Spare"), // 04 24
1995  _("Light"), // 05 25
1996  _("Light w/Sectors"), // 06 26
1997  _("Leading Light Front"), // 07 27
1998  _("Leading Light Rear"), // 08 28
1999  _("Cardinal N Beacon"), // 09 29
2000  _("Cardinal E Beacon"), // 10 30
2001  _("Cardinal S Beacon"), // 11 31
2002  _("Cardinal W Beacon"), // 12 32
2003  _("Beacon, Port Hand"), // 13 33
2004  _("Beacon, Starboard Hand"), // 14 34
2005  _("Beacon, Preferred Channel Port Hand"), // 15 35
2006  _("Beacon, Preferred Channel Starboard Hand"), // 16 36
2007  _("Beacon, Isolated Danger"), // 17 37
2008  _("Beacon, Safe Water"), // 18 38
2009  _("Beacon, Special Mark"), // 19 39
2010  _("Cardinal Mark N"), // 20 40
2011  _("Cardinal Mark E"), // 21 41
2012  _("Cardinal Mark S"), // 22 42
2013  _("Cardinal Mark W"), // 23 43
2014  _("Port Hand Mark"), // 24 44
2015  _("Starboard Hand Mark"), // 25 45
2016  _("Preferred Channel Port Hand"), // 26 46
2017  _("Preferred Channel Starboard Hand"), // 27 47
2018  _("Isolated Danger"), // 28 48
2019  _("Safe Water"), // 29 49
2020  _("Special Mark"), // 30 50
2021  _("Light Vessel/Rig"), // 31 51
2022  _("GpsGate Buddy"), // xx 52
2023  _("Position Report"), // xx 53
2024  _("Distress"), // xx 54
2025  _("ARPA radar target"), // xx 55
2026  _("APRS Position Report") // xx 56
2027  };
2028 
2029  return ais_type[index];
2030 }
2031 
2032 wxString ais_get_short_type(int index) {
2033  static const wxString short_ais_type[] = {
2034  _("F/V"), // 30 0
2035  _("Tow"), // 31 1
2036  _("Long Tow"), // 32 2
2037  _("Dredge"), // 33 3
2038  _("D/V"), // 34 4
2039  _("Mil/V"), // 35 5
2040  _("S/V"), // 36 6
2041  _("Yat"), // 37 7
2042  _("HSC"), // 4x 8
2043  _("P/V"), // 50 9
2044  _("SAR/V"), // 51 10
2045  _("Tug"), // 52 11
2046  _("Tender"), // 53 12
2047  _("PC/V"), // 54 13
2048  _("LE/V"), // 55 14
2049  _("Med/V"), // 58 15
2050  _("Pass/V"), // 6x 16
2051  _("M/V"), // 7x 17
2052  _("M/T"), // 8x 18
2053  _("?"), // 19
2054 
2055  _("AtoN"), // 00 20
2056  _("Ref. Pt"), // 01 21
2057  _("RACON"), // 02 22
2058  _("Fix.Struct."), // 03 23
2059  _("?"), // 04 24
2060  _("Lt"), // 05 25
2061  _("Lt sect."), // 06 26
2062  _("Ldg Lt Front"), // 07 27
2063  _("Ldg Lt Rear"), // 08 28
2064  _("Card. N"), // 09 29
2065  _("Card. E"), // 10 30
2066  _("Card. S"), // 11 31
2067  _("Card. W"), // 12 32
2068  _("Port"), // 13 33
2069  _("Stbd"), // 14 34
2070  _("Pref. Chnl"), // 15 35
2071  _("Pref. Chnl"), // 16 36
2072  _("Isol. Dngr"), // 17 37
2073  _("Safe Water"), // 18 38
2074  _("Special"), // 19 39
2075  _("Card. N"), // 20 40
2076  _("Card. E"), // 21 41
2077  _("Card. S"), // 22 42
2078  _("Card. W"), // 23 43
2079  _("Port Hand"), // 24 44
2080  _("Stbd Hand"), // 25 45
2081  _("Pref. Chnl"), // 26 46
2082  _("Pref. Chnl"), // 27 47
2083  _("Isol. Dngr"), // 28 48
2084  _("Safe Water"), // 29 49
2085  _("Special"), // 30 50
2086  _("LtV/Rig"), // 31 51
2087  _("Buddy"), // xx 52
2088  _("DSC"), // xx 53
2089  _("Distress"), // xx 54
2090  _("ARPA"), // xx 55
2091  _("APRS") // xx 56
2092  };
2093  return short_ais_type[index];
2094 }
Global state for AIS decoder.