27#include <wx/tokenzr.h>
29#include <wx/datetime.h>
34static const long long lNaN = 0xfff8000000000000;
35#define NAN (*(double *)&lNaN)
41AisError DecodeSingleVDO(
const wxString &str,
44 if (str.Len() > 100)
return AIS_NMEAVDX_TOO_LONG;
46 if (!NMEA_AISCheckSumOK(str))
return AIS_NMEAVDX_CHECKSUM_BAD;
48 if (!pos)
return AIS_GENERIC_ERROR;
53 if (!str.Mid(1, 5).IsSameAs(_T(
"AIVDO")))
return AIS_GENERIC_ERROR;
56 wxStringTokenizer tkz(str, _T(
","));
59 token = tkz.GetNextToken();
61 token = tkz.GetNextToken();
62 int nsentences = atoi(token.mb_str());
64 token = tkz.GetNextToken();
65 int isentence = atoi(token.mb_str());
67 token = tkz.GetNextToken();
68 token = tkz.GetNextToken();
70 wxString string_to_parse;
71 string_to_parse.Clear();
84 if ((1 == nsentences) && (1 == isentence)) {
85 string_to_parse = tkz.GetNextToken();
88 wxASSERT_MSG(
false, wxT(
"Multipart AIVDO detected"));
89 return AIS_INCOMPLETE_MULTIPART;
95 auto TargetData = std::make_unique<AisTargetData>(
96 *AisTargetDataMaker::GetInstance().GetTargetData());
98 bool bdecode_result = Parse_VDXBitstring(&strbit, TargetData.get());
100 if (bdecode_result) {
101 switch (TargetData->MID) {
106 if (!TargetData->b_positionDoubtful) {
107 pos->kLat = TargetData->Lat;
108 pos->kLon = TargetData->Lon;
114 if (TargetData->COG == 360.0)
117 pos->kCog = TargetData->COG;
119 if (TargetData->SOG > 102.2)
122 pos->kSog = TargetData->SOG;
124 if ((
int)TargetData->HDG == 511)
127 pos->kHdt = TargetData->HDG;
135 return AIS_GENERIC_ERROR;
140 return AIS_GENERIC_ERROR;
148 bool parse_result =
false;
149 bool b_posn_report =
false;
151 wxDateTime now = wxDateTime::Now();
153 int message_ID = bstr->
GetInt(1, 6);
154 ptd->MID = message_ID;
157 ptd->MMSI = bstr->
GetInt(9, 30);
159 switch (message_ID) {
164 ptd->NavStatus = bstr->
GetInt(39, 4);
165 ptd->SOG = 0.1 * (bstr->
GetInt(51, 10));
167 int lon = bstr->
GetInt(62, 28);
168 if (lon & 0x08000000)
170 double lon_tentative = lon / 600000.;
172 int lat = bstr->
GetInt(90, 27);
173 if (lat & 0x04000000)
175 double lat_tentative = lat / 600000.;
177 if ((lon_tentative <= 180.) && (lat_tentative <= 90.))
180 ptd->Lon = lon_tentative;
181 ptd->Lat = lat_tentative;
182 ptd->b_positionDoubtful =
false;
183 ptd->b_positionOnceValid =
true;
184 ptd->PositionReportTicks = now.GetTicks();
186 ptd->b_positionDoubtful =
true;
189 ptd->COG = 0.1 * (bstr->
GetInt(117, 12));
190 ptd->HDG = 1.0 * (bstr->
GetInt(129, 9));
192 ptd->ROTAIS = bstr->
GetInt(43, 8);
193 double rot_dir = 1.0;
195 if (ptd->ROTAIS == 128)
197 else if ((ptd->ROTAIS & 0x80) == 0x80) {
198 ptd->ROTAIS = ptd->ROTAIS - 256;
203 ptd->ROTIND = round(rot_dir * pow((((
double)ptd->ROTAIS) / 4.733), 2));
205 ptd->m_utc_sec = bstr->
GetInt(138, 6);
207 if ((1 == message_ID) || (2 == message_ID))
210 ptd->SyncState = bstr->
GetInt(151, 2);
211 ptd->SlotTO = bstr->
GetInt(153, 2);
212 if ((ptd->SlotTO == 1) && (ptd->SyncState == 0))
214 ptd->m_utc_hour = bstr->
GetInt(155, 5);
216 ptd->m_utc_min = bstr->
GetInt(160, 7);
218 if ((ptd->m_utc_hour < 24) && (ptd->m_utc_min < 60) &&
219 (ptd->m_utc_sec < 60)) {
220 wxDateTime rx_time(ptd->m_utc_hour, ptd->m_utc_min, ptd->m_utc_sec);
222 rx_ticks = rx_time.GetTicks();
224 first_rx_ticks = rx_ticks;
233 ptd->blue_paddle = bstr->
GetInt(144, 2);
234 ptd->b_blue_paddle = (ptd->blue_paddle == 2);
236 if (!ptd->b_isDSCtarget)
237 ptd->Class = AIS_CLASS_A;
240 int mmsi_start = ptd->MMSI / 10000000;
242 if (mmsi_start == 97) {
243 ptd->Class = AIS_SART;
244 ptd->StaticReportTicks =
259 b_posn_report =
true;
266 ptd->NavStatus = UNDEFINED;
268 ptd->SOG = 0.1 * (bstr->
GetInt(47, 10));
270 int lon = bstr->
GetInt(58, 28);
271 if (lon & 0x08000000)
273 double lon_tentative = lon / 600000.;
275 int lat = bstr->
GetInt(86, 27);
276 if (lat & 0x04000000)
278 double lat_tentative = lat / 600000.;
280 if ((lon_tentative <= 180.) && (lat_tentative <= 90.))
283 ptd->Lon = lon_tentative;
284 ptd->Lat = lat_tentative;
285 ptd->b_positionDoubtful =
false;
286 ptd->b_positionOnceValid =
true;
287 ptd->PositionReportTicks = now.GetTicks();
289 ptd->b_positionDoubtful =
true;
291 ptd->COG = 0.1 * (bstr->
GetInt(113, 12));
292 ptd->HDG = 1.0 * (bstr->
GetInt(125, 9));
294 ptd->m_utc_sec = bstr->
GetInt(134, 6);
296 if (!ptd->b_isDSCtarget)
297 ptd->Class = AIS_CLASS_B;
300 b_posn_report =
true;
310 if (b_posn_report) ptd->b_lost =
false;
312 if (
true == parse_result) {
314 if (!ptd->b_active && !ptd->b_positionDoubtful && b_posn_report)
315 ptd->b_active =
true;
321bool NMEA_AISCheckSumOK(
const wxString &str_in) {
322 unsigned char checksum_value = 0;
323 int sentence_hex_sum;
325 wxCharBuffer buf = str_in.ToUTF8();
326 if (!buf.data())
return false;
328 char str_ascii[AIS_MAX_MESSAGE_LEN + 1];
329 strncpy(str_ascii, buf.data(), AIS_MAX_MESSAGE_LEN);
330 str_ascii[AIS_MAX_MESSAGE_LEN] =
'\0';
332 int string_length = strlen(str_ascii);
334 int payload_length = 0;
335 while ((payload_length < string_length) &&
336 (str_ascii[payload_length] !=
'*'))
339 if (payload_length == string_length)
344 while (index < payload_length) {
345 checksum_value ^= str_ascii[index];
349 if (string_length > 4) {
351 scanstr[0] = str_ascii[payload_length + 1];
352 scanstr[1] = str_ascii[payload_length + 2];
354 sscanf(scanstr,
"%2x", &sentence_hex_sum);
356 if (sentence_hex_sum == checksum_value)
return true;
int GetInt(int sp, int len, bool signed_flag=false)
sp is starting bit, 1-based