From 65be02c5b16979b8c96e4c08235e892cd6d9bf63 Mon Sep 17 00:00:00 2001 From: Arne Luehrs Date: Sun, 14 Aug 2022 23:48:04 +0800 Subject: [PATCH 1/4] Add AIS Class B Decoding --- firmware/application/apps/ais_app.cpp | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index 1561fee2f..1a423d42a 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -76,7 +76,7 @@ static std::string mid( mid_code = to_string_dec_uint(mmsi, 9, ' ').substr(0, 3); return_code = db.retrieve_mid_record(&mid_record, mid_code); switch(return_code) { - case DATABASE_RECORD_FOUND: return mid_record.country; + case DATABASE_RECORD_FOUND: return mid_record.country; case DATABASE_NOT_FOUND: return "No mids.db file"; default: return ""; } @@ -164,7 +164,7 @@ void AISLogger::on_packet(const ais::Packet& packet) { } log_file.write_entry(packet.received_at(), entry); -} +} void AISRecentEntry::update(const ais::Packet& packet) { received_count++; @@ -195,6 +195,15 @@ void AISRecentEntry::update(const ais::Packet& packet) { destination = packet.text(302, 20); break; + case 18: + last_position.timestamp = packet.received_at(); + last_position.speed_over_ground = packet.read(46, 10); + last_position.latitude = packet.latitude(85); + last_position.longitude = packet.longitude(57); + last_position.course_over_ground = packet.read(112, 12); + last_position.true_heading = packet.read(124, 9); + break; + case 21: name = packet.text(43, 20); last_position.timestamp = packet.received_at(); @@ -202,6 +211,21 @@ void AISRecentEntry::update(const ais::Packet& packet) { last_position.longitude = packet.longitude(164); break; + case 24: + switch (packet.read(38, 2)) + { + case 0: + name = packet.text(40, 20); + break; + + case 1: + call_sign = packet.text(90, 7); + default: + break; + } + + break; + default: break; } @@ -252,7 +276,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) { }); send_updates = true; - + }; } @@ -262,7 +286,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(const AISRecentEntryDetailVie (void)Entry; } -AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry) +AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry) { (void)Entry; return *this; @@ -352,7 +376,7 @@ AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } { receiver_model.set_sampling_rate(sampling_rate); receiver_model.set_baseband_bandwidth(baseband_bandwidth); receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. - + options_channel.on_change = [this](size_t, OptionsField::value_t v) { this->on_frequency_changed(v); }; From 5096f91986f3b0031efeb9100d0445f961d1a90a Mon Sep 17 00:00:00 2001 From: Arne Luehrs Date: Sun, 14 Aug 2022 23:48:04 +0800 Subject: [PATCH 2/4] Add AIS Class B Decoding --- firmware/application/apps/ais_app.cpp | 36 +++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index 1561fee2f..1e9de626e 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -76,7 +76,7 @@ static std::string mid( mid_code = to_string_dec_uint(mmsi, 9, ' ').substr(0, 3); return_code = db.retrieve_mid_record(&mid_record, mid_code); switch(return_code) { - case DATABASE_RECORD_FOUND: return mid_record.country; + case DATABASE_RECORD_FOUND: return mid_record.country; case DATABASE_NOT_FOUND: return "No mids.db file"; default: return ""; } @@ -164,7 +164,7 @@ void AISLogger::on_packet(const ais::Packet& packet) { } log_file.write_entry(packet.received_at(), entry); -} +} void AISRecentEntry::update(const ais::Packet& packet) { received_count++; @@ -195,6 +195,15 @@ void AISRecentEntry::update(const ais::Packet& packet) { destination = packet.text(302, 20); break; + case 18: + last_position.timestamp = packet.received_at(); + last_position.speed_over_ground = packet.read(46, 10); + last_position.latitude = packet.latitude(85); + last_position.longitude = packet.longitude(57); + last_position.course_over_ground = packet.read(112, 12); + last_position.true_heading = packet.read(124, 9); + break; + case 21: name = packet.text(43, 20); last_position.timestamp = packet.received_at(); @@ -202,6 +211,23 @@ void AISRecentEntry::update(const ais::Packet& packet) { last_position.longitude = packet.longitude(164); break; + case 24: + switch (packet.read(38, 2)) + { + case 0: + name = packet.text(40, 20); + break; + + case 1: + call_sign = packet.text(90, 7); + break; + + default: + break; + } + + break; + default: break; } @@ -252,7 +278,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) { }); send_updates = true; - + }; } @@ -262,7 +288,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(const AISRecentEntryDetailVie (void)Entry; } -AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry) +AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry) { (void)Entry; return *this; @@ -352,7 +378,7 @@ AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } { receiver_model.set_sampling_rate(sampling_rate); receiver_model.set_baseband_bandwidth(baseband_bandwidth); receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias. - + options_channel.on_change = [this](size_t, OptionsField::value_t v) { this->on_frequency_changed(v); }; From 1e14ca4df17274f5ca3bc3bfbe14dd869a3c9c24 Mon Sep 17 00:00:00 2001 From: Arne Luehrs Date: Mon, 15 Aug 2022 13:56:40 +0800 Subject: [PATCH 3/4] Enable AIS-B decoding and display Implement decoding for AIS messages 18 and 24A 24B clean up display of text messages by removing the trailing null character '@' --- firmware/application/apps/ais_app.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index 1e9de626e..8ad1cc061 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -82,6 +82,12 @@ static std::string mid( } } +static std::string text(const std::string &text) { + size_t end = text.find_last_not_of("@"); + return (end == std::string::npos) ? "" : text.substr(0, end + 1); + +} + static std::string navigational_status(const unsigned int value) { switch(value) { case 0: return "under way w/engine"; @@ -244,9 +250,9 @@ void RecentEntriesTable::draw( ) { std::string line = ais::format::mmsi(entry.mmsi) + " "; if( !entry.name.empty() ) { - line += entry.name; + line += ais::format::text(entry.name); } else { - line += entry.call_sign; + line += ais::format::text(entry.call_sign); } line.resize(target_rect.width() / 8, ' '); @@ -267,7 +273,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) { button_see_map.on_select = [this, &nav](Button&) { geomap_view = nav.push( - entry_.name, + ais::format::text(entry_.name), 0, GeoPos::alt_unit::METERS, ais::format::latlon_float(entry_.last_position.latitude.normalized()), @@ -328,8 +334,8 @@ void AISRecentEntryDetailView::paint(Painter& painter) { field_rect = draw_field(painter, field_rect, s, "MMSI", ais::format::mmsi(entry_.mmsi)); field_rect = draw_field(painter, field_rect, s, "Ctry", ais::format::mid(entry_.mmsi)); - field_rect = draw_field(painter, field_rect, s, "Name", entry_.name); - field_rect = draw_field(painter, field_rect, s, "Call", entry_.call_sign); + field_rect = draw_field(painter, field_rect, s, "Name", ais::format::text(entry_.name)); + field_rect = draw_field(painter, field_rect, s, "Call", ais::format::text(entry_.call_sign)); field_rect = draw_field(painter, field_rect, s, "Dest", entry_.destination); field_rect = draw_field(painter, field_rect, s, "Last", to_string_datetime(entry_.last_position.timestamp)); field_rect = draw_field(painter, field_rect, s, "Pos ", ais::format::latlon(entry_.last_position.latitude, entry_.last_position.longitude)); From a89d418202b8b9293980ea03fd194e8e14cf44c5 Mon Sep 17 00:00:00 2001 From: Arne Luehrs Date: Mon, 15 Aug 2022 14:41:07 +0800 Subject: [PATCH 4/4] AIS Decoding, destination text display fix Remove trailing null characters '@' from display of destination field --- firmware/application/apps/ais_app.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index 8ad1cc061..d45efc0d4 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -336,7 +336,7 @@ void AISRecentEntryDetailView::paint(Painter& painter) { field_rect = draw_field(painter, field_rect, s, "Ctry", ais::format::mid(entry_.mmsi)); field_rect = draw_field(painter, field_rect, s, "Name", ais::format::text(entry_.name)); field_rect = draw_field(painter, field_rect, s, "Call", ais::format::text(entry_.call_sign)); - field_rect = draw_field(painter, field_rect, s, "Dest", entry_.destination); + field_rect = draw_field(painter, field_rect, s, "Dest", ais::format::text(entry_.destination)); field_rect = draw_field(painter, field_rect, s, "Last", to_string_datetime(entry_.last_position.timestamp)); field_rect = draw_field(painter, field_rect, s, "Pos ", ais::format::latlon(entry_.last_position.latitude, entry_.last_position.longitude)); field_rect = draw_field(painter, field_rect, s, "Stat", ais::format::navigational_status(entry_.navigational_status));