From b549d3a4f1a3cb62c96d291a2fab4992a92d501a Mon Sep 17 00:00:00 2001 From: heurist1 Date: Tue, 28 Feb 2023 19:00:11 +0000 Subject: [PATCH 1/6] Add ability to draw text with a transparent background (cherry picked from commit a15da2e136147b31ab53058871815c8eb759576a) --- firmware/common/lcd_ili9341.cpp | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index 58b3f5ddc..51a238d95 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -606,12 +606,35 @@ void ILI9341::draw_bitmap( const ui::Color foreground, const ui::Color background ) { - lcd_start_ram_write(p, size); + // Not a transparent background + if (ui::Color::magenta().v!=background.v) + { + lcd_start_ram_write(p, size); - const size_t count = size.width() * size.height(); - for(size_t i=0; i> 3] & (1U << (i & 0x7)); - io.lcd_write_pixel(pixel ? foreground : background); + const size_t count = size.width() * size.height(); + for(size_t i=0; i> 3] & (1U << (i & 0x7)); + io.lcd_write_pixel(pixel ? foreground : background); + } + } + else + { + int x = p.x(); + int y = p.y(); + int maxX = x + size.width(); + const size_t count = size.width() * size.height(); + for(size_t i=0; i> 3] & (1U << (i & 0x7)); + if (pixel) { + draw_pixel(ui::Point(x,y), foreground); + } + // Move to the next pixel + x++; + if (x>=maxX){ + x = p.x(); + y++; + } + } } } From 567fee1d9880941488aed6b82b5811d075606fe0 Mon Sep 17 00:00:00 2001 From: heurist1 Date: Tue, 28 Feb 2023 19:02:05 +0000 Subject: [PATCH 2/6] Create optimised integer functions to reduce CPU usage (cherry picked from commit a47bfe1da7bffe9f752e4c522e11593cce6dffd0) --- firmware/common/ui.cpp | 7 ++++ firmware/common/ui.hpp | 2 ++ firmware/common/utility.cpp | 72 +++++++++++++++++++++++++++++++++++++ firmware/common/utility.hpp | 4 +++ 4 files changed, 85 insertions(+) diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index 406f5e495..c1a44894f 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -21,6 +21,7 @@ #include "ui.hpp" #include "sine_table.hpp" +#include "utility.hpp" #include @@ -96,4 +97,10 @@ Point polar_to_point(float angle, uint32_t distance) { sin_f32(DEG_TO_RAD(-angle) - (pi / 2)) * distance); } +Point fast_polar_to_point(int32_t angle, uint32_t distance) { + //polar to compass with y negated for screen drawing + return Point((int16_sin_s4(((1<<16)*(-angle + 180))/360) * distance)/(1<<16), + (int16_sin_s4(((1<<16)*(-angle - 90))/360) * distance)/(1<<16)); +} + } /* namespace ui */ diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index ee8610fbf..4dcf4fbfe 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -343,6 +343,8 @@ struct TouchEvent { Point polar_to_point(float angle, uint32_t distance); +Point fast_polar_to_point(int32_t angle, uint32_t distance); + } /* namespace ui */ #endif/*__UI_H__*/ diff --git a/firmware/common/utility.cpp b/firmware/common/utility.cpp index b29a9b745..7e1a8ace4 100644 --- a/firmware/common/utility.cpp +++ b/firmware/common/utility.cpp @@ -91,6 +91,78 @@ float mag2_to_dbv_norm(const float mag2) { return (fast_log2(mag2) - mag2_log2_max) * mag2_to_db_factor; } +// Integer in and out approximation +// >40 times faster float sqrt(x*x+y*y) on Cortex M0 +// derived from https://dspguru.com/dsp/tricks/magnitude-estimator/ +int fast_int_magnitude(int y, int x) +{ + if(y<0){y=-y;} + if(x<0){x=-x;} + if (x>y) { + return ((x*61)+(y*26)+32)/64; + } else { + return ((y*61)+(x*26)+32)/64; + } +} + +// Integer x and y returning an integer bearing in degrees +// Accurate to 0.5 degrees, so output scaled to whole degrees +// >60 times faster than float atan2 on Cortex M0 +int int_atan2(int y, int x) +{ + // Number of bits to shift up before doing the maths. A larger shift + // may beable to gain accuracy, but it would cause the correction + // entries to be larger than 1 byte + static const int bits = 10; + static const int pi4 = (1 << bits); + static const int pi34 = (3 << bits); + + // Special case + if (x == 0 && y == 0) { return 0; } + + // Form an approximate angle + const int yabs = y >= 0 ? y : -y; + int angle; + if (x >= 0) { + angle = pi4 - pi4 * (x - yabs) / (x + yabs); + } else { + angle = pi34 - pi4 * (x + yabs) / (yabs - x); + } + // Correct the result using a lookup table + static const int8_t correct[32] = { 0, -23, -42, -59, -72, -83 ,-89 ,-92 ,-92 ,-88 ,-81, -71, -58, -43 ,-27, -9, 9, 27, 43, 58, 71, 81, 88, 92, 92, 89, 83, 72, 59, 42, 23, 0 }; + static const int rnd = (1 << (bits - 1)) / 45; // Minor correction to round to correction values better (add 0.5) + const int idx = ((angle + rnd) >> (bits - 4)) & 0x1F; + angle += correct[idx]; + + // Scale for output in degrees + static const int half = (1 << (bits - 1)); + angle = ((angle * 45)+half) >> bits; // Add on half before rounding + if (y < 0) { angle = -angle; } + return angle; +} + +// 16 bit value represents a full cycle in but can handle multiples of this. +// Output in range +/- 16 bit value representing +/- 1.0 +// 4th order cosine approximation has very small error +// >200 times faster tan float sin on Cortex M0 +// see https://www.coranac.com/2009/07/sines/ +int32_t int16_sin_s4(int32_t x) +{ + static const int qN = 14, qA = 16, qR=12, B = 19900, C = 3516; + + const int32_t c = x << (30 - qN); // Semi-circle info into carry. + x -= 1 << qN; // sine -> cosine calc + + x = x << (31 - qN); // Mask with PI + x = x >> (31 - qN); // Note: SIGNED shift! (to qN) + x = x*x >> (2 * qN - 14); // x=x^2 To Q14 + + int32_t y = B - (x*C >> 14); // B - x^2*C + y = (1 << qA) - (x*y >> qR); // A - x^2*(B-x^2*C) + + return c >= 0 ? y : -y; +} + /* GCD implementation derived from recursive implementation at * http://en.wikipedia.org/wiki/Binary_GCD_algorithm */ diff --git a/firmware/common/utility.hpp b/firmware/common/utility.hpp index 2cd3fcc68..f60097bd8 100644 --- a/firmware/common/utility.hpp +++ b/firmware/common/utility.hpp @@ -90,6 +90,10 @@ inline float magnitude_squared(const std::complex c) { return r2 + i2; } +int fast_int_magnitude(int y, int x); +int int_atan2(int y, int x); +int32_t int16_sin_s4(int32_t x); + template struct range_t { const T minimum; From ef151e243b0895ecc426120d85dcdb5ac7677383 Mon Sep 17 00:00:00 2001 From: heurist1 Date: Tue, 28 Feb 2023 19:04:03 +0000 Subject: [PATCH 3/6] Imprment string trimr to remove spaces at the end of aircraft name. (cherry picked from commit 70fd4039938cdd45f03d541e0aff1238de85fb3e) --- firmware/application/string_format.cpp | 6 ++++++ firmware/application/string_format.hpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index 3529a2d7b..0af9dc3d2 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -255,3 +255,9 @@ double get_decimals(double num, int16_t mult, bool round) { if (num > .5) intnum++; //Round up return intnum; } + +std::string trimr(std::string str) +{ + size_t last = str.find_last_not_of(' '); + return (last!=std::string::npos) ? str.substr(0, last+1) : ""; // Remove the trailing spaces +} \ No newline at end of file diff --git a/firmware/application/string_format.hpp b/firmware/application/string_format.hpp index db32072e2..848a53e3e 100644 --- a/firmware/application/string_format.hpp +++ b/firmware/application/string_format.hpp @@ -58,4 +58,7 @@ std::string to_string_FAT_timestamp(const FATTimestamp& timestamp); std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision); double get_decimals(double num, int16_t mult, bool round = false); //euquiq added + +std::string trimr(std::string str); // Remove trailing spaces + #endif/*__STRING_FORMAT_H__*/ From 00b75eacdaeafee372fc63c471e376d283564ad2 Mon Sep 17 00:00:00 2001 From: heurist1 Date: Tue, 28 Feb 2023 19:09:05 +0000 Subject: [PATCH 4/6] Update map to allow multiple markers --- firmware/application/ui/ui_geomap.cpp | 140 ++++++++++++++++++++------ firmware/application/ui/ui_geomap.hpp | 47 ++++++++- 2 files changed, 158 insertions(+), 29 deletions(-) diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index 4d0d7e29c..e2a30aa90 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -134,7 +134,7 @@ int32_t GeoPos::altitude() { GeoMap::GeoMap( Rect parent_rect -) : Widget { parent_rect } +) : Widget { parent_rect }, markerListLen(0) { //set_focusable(true); } @@ -144,34 +144,44 @@ void GeoMap::paint(Painter& painter) { std::array map_line_buffer; const auto r = screen_rect(); - // Ony redraw map if it moved by at least 1 pixel - if ((x_pos != prev_x_pos) || (y_pos != prev_y_pos)) { + // Ony redraw map if it moved by at least 1 pixel + // or the markers list was updated + int x_diff = abs(x_pos-prev_x_pos); + int y_diff = abs(y_pos-prev_y_pos); + if (markerListUpdated || (x_diff>=3) || (y_diff>=3)) { for (line = 0; line < r.height(); line++) { map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1)); map_file.read(map_line_buffer.data(), r.width() << 1); display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer); } - prev_x_pos = x_pos; prev_y_pos = y_pos; + + // Draw the other markers + for ( int i=0; i=0) && (x10) && (y=32) { // Dont draw text if it would overlap top + // Text and symbol + draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta() ); + } else { + // Only symbol + draw_bearing( itemPoint, item.angle, 10, Color::blue()); + } + } + markerListUpdated = false; + } // Draw the other markers } - //center tag above point - if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces - painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_); - } - if (mode_ == PROMPT) { - // Cross - display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red()); - display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red()); - } else if (angle_ < 360){ - //if we have a valid angle draw bearing - draw_bearing(r.center(), angle_, 10, Color::red()); - } - else { - //draw a small cross - display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red()); - display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red()); - } + + //Draw the marker in the center + draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black() ); } bool GeoMap::on_touch(const TouchEvent event) { @@ -196,10 +206,7 @@ void GeoMap::move(const float lon, const float lat) { x_pos = map_width * (lon_+180)/360 - (map_rect.width() / 2); // Latitude calculation based on https://stackoverflow.com/a/10401734/2278659 - double map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat double lat_rad = sin(lat * pi / 180); - double map_world_lon = map_width / (2 * pi); - double map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom))); y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI // Cap position @@ -222,7 +229,11 @@ bool GeoMap::init() { lon_ratio = 180.0 / map_center_x; lat_ratio = -90.0 / map_center_y; - + + map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat + map_world_lon = map_width / (2 * pi); + map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom))); + return true; } @@ -234,9 +245,9 @@ void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t siz Point arrow_a, arrow_b, arrow_c; for (size_t thickness = 0; thickness < 3; thickness++) { - arrow_a = polar_to_point(angle, size) + origin; - arrow_b = polar_to_point(angle + 180 - 35, size) + origin; - arrow_c = polar_to_point(angle + 180 + 35, size) + origin; + arrow_a = fast_polar_to_point((int)angle, size) + origin; + arrow_b = fast_polar_to_point((int)(angle + 180 - 35), size) + origin; + arrow_c = fast_polar_to_point((int)(angle + 180 + 35), size) + origin; display.draw_line(arrow_a, arrow_b, color); display.draw_line(arrow_b, arrow_c, color); @@ -246,6 +257,65 @@ void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t siz } } +void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, + const Color color, const Color fontColor, const Color backColor ) +{ + int tagOffset = 10; + if (mode_ == PROMPT) { + // Cross + display.fill_rectangle({ itemPoint - Point(16, 1), { 32, 2 } }, color); + display.fill_rectangle({ itemPoint - Point(1, 16), { 2, 32 } }, color); + tagOffset = 16; + } else if (angle_ < 360){ + //if we have a valid angle draw bearing + draw_bearing( itemPoint, itemAngle, 10, color); + tagOffset = 10; + } + else { + //draw a small cross + display.fill_rectangle({ itemPoint - Point(8, 1), { 16, 2 } }, color); + display.fill_rectangle({ itemPoint - Point(1, 8), { 2, 16 } }, color); + tagOffset = 8; + } + //center tag above point + if(itemTag.find_first_not_of(' ') != itemTag.npos){ //only draw tag if we have something other than spaces + painter.draw_string( itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset ), + style().font, fontColor, backColor, itemTag); + } +} + +void GeoMap::clear_markers() +{ + markerListLen = 0; +} + +MapMarkerStored GeoMap::store_marker(GeoMarker & marker) +{ + MapMarkerStored ret; + + // Check if it could be on screen + // Only checking one direction to reduce CPU + const auto r = screen_rect(); + double lat_rad = sin(marker.lat * pi / 180); + int x = (map_width * (marker.lon+180)/360) - x_pos; + int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI + if (false==((x>=0) && (x10) && (y on_move { }; @@ -133,20 +157,35 @@ public: angle_ = new_angle; } + static const int NumMarkerListElements = 30; + + void clear_markers(); + MapMarkerStored store_marker(GeoMarker & marker); + private: void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); - + void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, + const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black() ); + GeoMapMode mode_ { }; File map_file { }; uint16_t map_width { }, map_height { }; int32_t map_center_x { }, map_center_y { }; float lon_ratio { }, lat_ratio { }; + double map_bottom { }; + double map_world_lon { }; + double map_offset { }; + int32_t x_pos { }, y_pos { }; int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF }; float lat_ { }; float lon_ { }; uint16_t angle_ { }; std::string tag_ { }; + + int markerListLen {0}; + GeoMarker markerList[NumMarkerListElements]; + bool markerListUpdated {false}; }; class GeoMapView : public View { @@ -181,6 +220,12 @@ public: std::string title() const override { return "Map view"; }; + void clear_markers(); + MapMarkerStored store_marker(GeoMarker & marker); + + void update_tag(const std::string tag); + + private: NavigationView& nav_; From 778111d4665e4d7b00c2b659c63d08d914a0602f Mon Sep 17 00:00:00 2001 From: heurist1 Date: Tue, 28 Feb 2023 19:17:57 +0000 Subject: [PATCH 5/6] Updates to put the multiple aircraft on the map Also manage the CPU in on the one second tick to keep GUI responsive Some other small edits that fix minor problems from my previous checkins --- firmware/application/apps/ui_adsb_rx.cpp | 172 +++++++++++++++-------- firmware/application/apps/ui_adsb_rx.hpp | 46 +++--- firmware/baseband/proc_adsbrx.cpp | 7 +- firmware/common/adsb.cpp | 5 +- 4 files changed, 142 insertions(+), 88 deletions(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 1ae332ee8..c2b8669d0 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -46,10 +46,10 @@ void RecentEntriesTable::draw( auto entry_age = entry.age; // Color decay for flights not being updated anymore - if (entry_age < ADSB_DECAY_A) { + if (entry_age < ADSB_CURRENT) { aged_color = 0x10; target_color = Color::green(); - } else if (entry_age < ADSB_DECAY_B) { + } else if (entry_age < ADSB_RECENT) { aged_color = 0x07; target_color = Color::light_grey(); } else { @@ -59,21 +59,13 @@ void RecentEntriesTable::draw( std::string entry_string = "\x1B"; entry_string += aged_color; -#if false - entry_string += to_string_hex(entry.ICAO_address, 6) + " " + - entry.callsign + " " + - (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+") + " " + - entry.time_string; -#else - // SBT entry_string += - (entry.callsign[0]!=' ' ? entry.callsign + " " : to_string_hex(entry.ICAO_address, 6) + " ") + - to_string_dec_uint((unsigned int)((entry.pos.altitude+50)/100),4) + + (entry.callsign[0]!=' ' ? entry.callsign + " " : entry.icaoStr + " ") + + to_string_dec_uint((unsigned int)(entry.pos.altitude/100),4) + to_string_dec_uint((unsigned int)entry.velo.speed,4) + to_string_dec_uint((unsigned int)(entry.amp>>9),4) + " " + (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") + to_string_dec_uint(entry.age, 4); -#endif painter.draw_string( target_rect.location(), @@ -124,8 +116,6 @@ ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView( }); std::unique_ptr logger { }; - //update(entry_copy); - icao_code = to_string_hex(entry_copy.ICAO_address, 6); text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6)); @@ -225,7 +215,10 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) { text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14)); if (send_updates) + { + geomap_view->update_tag(trimr(entry.callsign[0]!=' ' ? entry.callsign : to_string_hex(entry.ICAO_address, 6))); geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude); + } } ADSBRxDetailsView::~ADSBRxDetailsView() { @@ -277,19 +270,14 @@ ADSBRxDetailsView::ADSBRxDetailsView( text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6)); button_aircraft_details.on_select = [this, &nav](Button&) { - //detailed_entry_key = entry.key(); - aircraft_details_view = nav.push( - entry_copy, - [this]() { - send_updates = false; - }); + aircraft_details_view = nav.push(entry_copy, [this]() { send_updates = false;}); send_updates = false; }; button_see_map.on_select = [this, &nav](Button&) { if (!send_updates) { // Prevent recursively launching the map geomap_view = nav.push( - entry_copy.callsign, + trimr(entry_copy.callsign[0]!=' ' ? entry_copy.callsign : entry_copy.icaoStr), entry_copy.pos.altitude, GeoPos::alt_unit::FEET, entry_copy.pos.latitude, @@ -309,6 +297,7 @@ void ADSBRxView::focus() { } ADSBRxView::~ADSBRxView() { + receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit // save app settings settings.save("rx_adsb", &app_settings); @@ -326,8 +315,6 @@ AircraftRecentEntry ADSBRxView::find_or_create_entry(uint32_t ICAO_address) { // If not found if (it == std::end(recent)){ recent.emplace_front(ICAO_address); // Add it - truncate_entries(recent); // Truncate the list - sort_entries_by_state(); it = find(recent, ICAO_address); // Find it again } return *it; @@ -342,6 +329,21 @@ void ADSBRxView::replace_entry(AircraftRecentEntry & entry) entry); } +void ADSBRxView::remove_old_entries() +{ + auto it = recent.rbegin(); + auto end = recent.rend(); + while (it != end) + { + if (it->age_state>=4) { + std::advance(it, 1); + recent.erase( it.base() ); + } else { + break; // stop looking because the list is sorted + } + } +} + void ADSBRxView::sort_entries_by_state() { // Sorting List pn age_state using lambda function as comparator @@ -349,8 +351,8 @@ void ADSBRxView::sort_entries_by_state() } void ADSBRxView::on_frame(const ADSBFrameMessage * message) { + logger = std::make_unique(); rtc::RTC datetime; - std::string str_timestamp; std::string callsign; std::string str_info; std::string logentry; @@ -365,16 +367,16 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { entry.reset_age(); if (entry.hits==0) { - entry.amp = message->amp; + entry.amp = message->amp; // Store amplitude on first hit } else { - entry.amp = ((entry.amp*15)+message->amp)>>4; + entry.amp = ((entry.amp*15)+message->amp)>>4; // Update smoothed amplitude on updates } - str_timestamp = to_string_datetime(datetime, HMS); - entry.set_time_string(str_timestamp); entry.inc_hit(); - logentry += to_string_hex_array(frame.get_raw_data(), 14) + " "; - logentry += "ICAO:" + to_string_hex(ICAO_address, 6) + " "; + if (logger) { + logentry += to_string_hex_array(frame.get_raw_data(), 14) + " "; + logentry += "ICAO:" + entry.icaoStr + " "; + } if (frame.get_DF() == DF_ADSB) { uint8_t msg_type = frame.get_msg_type(); @@ -385,7 +387,9 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { if ((msg_type >= AIRCRAFT_ID_L) && (msg_type <= AIRCRAFT_ID_H)) { callsign = decode_frame_id(frame); entry.set_callsign(callsign); - logentry+=callsign+" "; + if (logger) { + logentry+=callsign+" "; + } } // 9: // 18: { // Extended squitter/non-transponder @@ -400,30 +404,33 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { " Lat:" + to_string_decimal(entry.pos.latitude, 2) + " Lon:" + to_string_decimal(entry.pos.longitude, 2); - // printing the coordinates in the log file with more - // resolution, as we are not constrained by screen - // real estate there: - - std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + - " Lat:" + to_string_decimal(entry.pos.latitude, 7) + - " Lon:" + to_string_decimal(entry.pos.longitude, 7); entry.set_info_string(str_info); - logentry+=log_info + " "; + + if (logger) { + // printing the coordinates in the log file with more + // resolution, as we are not constrained by screen + // real estate there: + + std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + + " Lat:" + to_string_decimal(entry.pos.latitude, 7) + + " Lon:" + to_string_decimal(entry.pos.longitude, 7); + logentry+=log_info + " "; + } } } else if(msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC){ entry.set_frame_velo(frame); - logentry += "Type:" + to_string_dec_uint(msg_sub) + - " Hdg:" + to_string_dec_uint(entry.velo.heading) + - " Spd: "+ to_string_dec_int(entry.velo.speed); + if (logger) { + logentry += "Type:" + to_string_dec_uint(msg_sub) + + " Hdg:" + to_string_dec_uint(entry.velo.heading) + + " Spd: "+ to_string_dec_int(entry.velo.speed); + } } - } + replace_entry(entry); + } // frame.get_DF() == DF_ADSB - replace_entry(entry); - - logger = std::make_unique(); if (logger) { logger->append(u"adsb.txt"); // will log each frame in format: @@ -433,20 +440,69 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { } } -void ADSBRxView::on_tick_second() { - // Decay and refresh if needed - for (auto& entry : recent) { - entry.inc_age(); - - if (details_view) { - if (send_updates && (entry.key() == detailed_entry_key)) // Check if the ICAO address match - details_view->update(entry); +void ADSBRxView::on_tick_second() { + if (recent.size() <= 16){ // Not many entries update everything (16 is one screen full) + updateDetailsAndMap(1); + updateRecentEntries(); + } else if (updateState==0) { // Even second + updateState = 1; + updateDetailsAndMap(2); + } else { // Odd second only performed when there are many entries + updateState = 0; + updateRecentEntries(); + } +} + +void ADSBRxView::updateDetailsAndMap(int ageStep) { + ui::GeoMarker marker; + bool storeNewMarkers = false; + + // Sort and truncate the entries, grouped, newest group first + sort_entries_by_state(); + truncate_entries(recent); + remove_old_entries(); + + // Calculate if it is time to update markers + if (send_updates && details_view && details_view->geomap_view) { + ticksSinceMarkerRefresh += ageStep; + if (ticksSinceMarkerRefresh >= MARKER_UPDATE_SECONDS) { // Update other aircraft every few seconds + storeNewMarkers = true; + ticksSinceMarkerRefresh=0; } + } else { + ticksSinceMarkerRefresh = MARKER_UPDATE_SECONDS; // Send the markers as soon as the geoview exists } - // Sort the list if it is being displayed - if (!send_updates) { - sort_entries_by_state(); + // Increment age, and pass updates to the details and map + const bool otherMarkersCanBeSent = send_updates && storeNewMarkers && details_view && details_view->geomap_view; // Save retesting all of this + MapMarkerStored markerStored = MARKER_NOT_STORED; + if (otherMarkersCanBeSent) {details_view->geomap_view->clear_markers();} + // Loop through all entries + for (auto& entry : recent) { + entry.inc_age(ageStep); + + // Only if there is a details view + if (send_updates && details_view) { + if (entry.key() == detailed_entry_key) // Check if the ICAO address match + { + details_view->update(entry); + } + // Store if the view is present and the list isn't full + else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && (entry.age_state==0)) + { + marker.lon = entry.pos.longitude; + marker.lat = entry.pos.latitude; + marker.angle = entry.velo.heading; + marker.tag = trimr(entry.callsign[0]!=' ' ? entry.callsign : entry.icaoStr); + markerStored = details_view->geomap_view->store_marker(marker); + } + } + } // Loop through all entries, if only to update the age +} + +void ADSBRxView::updateRecentEntries() { + // Redraw the list of aircraft + if (!send_updates){ recent_entries_view.set_dirty(); } } @@ -492,7 +548,7 @@ ADSBRxView::ADSBRxView(NavigationView& nav) { on_tick_second(); }; - prevFreq = receiver_model.tuning_frequency(); + prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation baseband::set_adsb(); diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index e882b8db5..4764d7b9d 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -25,6 +25,7 @@ #include "ui_receiver.hpp" #include "ui_geomap.hpp" #include "ui_font_fixed_8x16.hpp" +#include "string_format.hpp" #include "file.hpp" #include "database.hpp" @@ -39,9 +40,9 @@ using namespace adsb; namespace ui { -#define ADSB_DECAY_A 10 // In seconds -#define ADSB_DECAY_B 30 -#define ADSB_DECAY_C 60 // Can be used for removing old entries, RecentEntries already caps to 64 +#define ADSB_CURRENT 10 // Seconds +#define ADSB_RECENT 30 // Seconds +#define ADSB_REMOVE 300 // Used for removing old entries #define AIRCRAFT_ID_L 1 // aircraft ID message type (lowest type id) #define AIRCRAFT_ID_H 4 // aircraft ID message type (highest type id) @@ -83,14 +84,15 @@ struct AircraftRecentEntry { ADSBFrame frame_pos_even { }; ADSBFrame frame_pos_odd { }; + std::string icaoStr {" "}; std::string callsign { " " }; - std::string time_string { "" }; std::string info_string { "" }; AircraftRecentEntry( const uint32_t ICAO_address ) : ICAO_address { ICAO_address } { + this->icaoStr = to_string_hex(ICAO_address, 6); } Key key() const { @@ -125,23 +127,20 @@ struct AircraftRecentEntry { info_string = new_info_string; } - void set_time_string(std::string& new_time_string) { - time_string = new_time_string; - } - void reset_age() { age = 0; } - void inc_age() { - age++; - if (age < ADSB_DECAY_A) - { + void inc_age(int delta) { + age+=delta; + if (age < ADSB_CURRENT){ age_state = pos.valid ? 0 : 1; - } - else - { - age_state = (age < ADSB_DECAY_B) ? 2 : 3; + } else if(age < ADSB_RECENT){ + age_state = 2; + } else if(age < ADSB_REMOVE){ + age_state = 3; + } else{ + age_state = 4; } } }; @@ -276,7 +275,6 @@ public: private: AircraftRecentEntry entry_copy { 0 }; std::function on_close_ { }; - GeoMapView* geomap_view { nullptr }; ADSBRxAircraftDetailsView* aircraft_details_view { nullptr }; bool send_updates { false }; std::database db = { }; @@ -364,6 +362,7 @@ public: std::string title() const override { return "ADS-B RX"; }; void replace_entry(AircraftRecentEntry & entry); + void remove_old_entries(); AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address); void sort_entries_by_state(); @@ -372,24 +371,23 @@ private: std::unique_ptr logger { }; void on_frame(const ADSBFrameMessage * message); void on_tick_second(); + int updateState = { 0 }; + void updateRecentEntries(); + void updateDetailsAndMap(int ageStep); + + #define MARKER_UPDATE_SECONDS (5) + int ticksSinceMarkerRefresh { MARKER_UPDATE_SECONDS-1 }; // app save settings std::app_settings settings { }; std::app_settings::AppSettings app_settings { }; const RecentEntriesColumns columns { { -#if false - { "ICAO", 6 }, - { "Callsign", 9 }, - { "Hits", 4 }, - { "Time", 8 } -#else { "ICAO/Call", 9 }, { "Lvl", 3 }, { "Spd", 3 }, { "Amp", 3 }, { "Hit", 3 }, { "Age", 4 } -#endif } }; AircraftRecentEntries recent { }; RecentEntriesView> recent_entries_view { columns, recent }; diff --git a/firmware/baseband/proc_adsbrx.cpp b/firmware/baseband/proc_adsbrx.cpp index 4520f1c9b..f52d51013 100644 --- a/firmware/baseband/proc_adsbrx.cpp +++ b/firmware/baseband/proc_adsbrx.cpp @@ -45,8 +45,8 @@ void ADSBRXProcessor::execute(const buffer_c8_t& buffer) { for (size_t i = 0; i < buffer.count; i++) { // Compute sample's magnitude - re = (int32_t)buffer.p[i].real(); // make re float and scale it - im = (int32_t)buffer.p[i].imag(); // make re float and scale it + re = (int32_t)buffer.p[i].real(); + im = (int32_t)buffer.p[i].imag(); mag = ((uint32_t)(re*re) + (uint32_t)(im*im)); if (decoding) { @@ -63,7 +63,6 @@ void ADSBRXProcessor::execute(const buffer_c8_t& buffer) { } else { - //confidence = true; bit = (prev_mag > mag) ? 1 : 0; } @@ -94,7 +93,7 @@ void ADSBRXProcessor::execute(const buffer_c8_t& buffer) { } // Continue looking for preamble even if in a packet - // switch is new preamble id higher magnitude + // switch if new preamble is higher magnitude // Shift the preamble for (c = 0; c < (ADSB_PREAMBLE_LENGTH ); c++) { shifter[c] = shifter[c + 1]; } diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp index c209cdf8a..5560745cc 100644 --- a/firmware/common/adsb.cpp +++ b/firmware/common/adsb.cpp @@ -22,6 +22,7 @@ #include "adsb.hpp" #include "sine_table.hpp" +#include "utility.hpp" #include @@ -369,11 +370,11 @@ adsb_vel decode_frame_velo(ADSBFrame& frame){ if(frame_data[5]&0x04) velo_ew *= -1; //check ew direction sign if(frame_data[7]&0x80) velo_ns *= -1; //check ns direction sign - velo.speed = sqrt(velo_ns*velo_ns + velo_ew*velo_ew); + velo.speed = fast_int_magnitude(velo_ns,velo_ew); if(velo.speed){ //calculate heading in degrees from ew/ns velocities - int16_t heading_temp = (int16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi); + int16_t heading_temp = (int16_t)(int_atan2(velo_ew,velo_ns)); // Nearest degree // We don't want negative values but a 0-360 scale. if (heading_temp < 0) heading_temp += 360.0; velo.heading = (uint16_t)heading_temp; From daf63de55fe7a0f8913aa246d9fec607ed3e6917 Mon Sep 17 00:00:00 2001 From: heurist1 Date: Tue, 28 Feb 2023 23:00:51 +0000 Subject: [PATCH 6/6] Final corrections to make merged code compile, and reduce the adding and removing of other markers --- firmware/application/apps/ui_adsb_rx.cpp | 2 +- firmware/application/apps/ui_adsb_rx.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index c2b8669d0..ca899ffc9 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -488,7 +488,7 @@ void ADSBRxView::updateDetailsAndMap(int ageStep) { details_view->update(entry); } // Store if the view is present and the list isn't full - else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && (entry.age_state==0)) + else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state<=2)) { marker.lon = entry.pos.longitude; marker.lat = entry.pos.latitude; diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index 4764d7b9d..b22d39309 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -272,6 +272,7 @@ public: std::database::AirlinesDBRecord airline_record = {}; + GeoMapView* geomap_view { nullptr }; private: AircraftRecentEntry entry_copy { 0 }; std::function on_close_ { };