diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 1ae332ee8..ca899ffc9 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.pos.valid && (entry.age_state<=2)) + { + 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..b22d39309 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; } } }; @@ -273,10 +272,10 @@ public: std::database::AirlinesDBRecord airline_record = {}; + GeoMapView* geomap_view { nullptr }; 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 +363,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 +372,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/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__*/ 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_; 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; 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++; + } + } } } 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;