From bcff97afd9ce963c4e94ba0062ccb31356aee8c1 Mon Sep 17 00:00:00 2001 From: liu weikai Date: Fri, 24 Jul 2026 18:22:18 +0800 Subject: [PATCH] fix: bind uconsole GPS page to AIO2 runtime --- .../common/src/platform/ui/gps_runtime.cpp | 66 ++++++- .../uconsole/src/uconsole_desktop_shell.cpp | 172 +++++++++++++++--- 2 files changed, 207 insertions(+), 31 deletions(-) diff --git a/platform/linux/common/src/platform/ui/gps_runtime.cpp b/platform/linux/common/src/platform/ui/gps_runtime.cpp index 68add2fe..4eaf85d7 100644 --- a/platform/linux/common/src/platform/ui/gps_runtime.cpp +++ b/platform/linux/common/src/platform/ui/gps_runtime.cpp @@ -317,6 +317,37 @@ std::string gps_candidate_identity(const std::string& path) return path; } +std::vector discover_clockwork_pi_serial_candidates() +{ + std::vector out; + std::error_code ec; + const std::filesystem::path by_id("/dev/serial/by-id"); + if (!std::filesystem::exists(by_id, ec) || ec) + { + return out; + } + + for (const auto& entry : std::filesystem::directory_iterator(by_id, ec)) + { + if (ec) + { + break; + } + + const std::string name = entry.path().filename().string(); + if (name.find("ClockworkPI") == std::string::npos || + name.find("uConsole") == std::string::npos) + { + continue; + } + + out.push_back(entry.path().string()); + } + + std::sort(out.begin(), out.end()); + return out; +} + std::vector deduplicate_auto_serial_candidates( const std::vector& candidates) { @@ -342,13 +373,36 @@ std::vector deduplicate_auto_serial_candidates( const std::vector& auto_serial_candidates_locked() { const char* env = std::getenv(kGpsDeviceCandidatesEnv); - const std::string signature = - env && env[0] != '\0' ? std::string(env) : std::string(kDefaultGpsDeviceCandidates); + std::vector candidates; + if (env && env[0] != '\0') + { + candidates = split_device_candidates(env); + } + else + { + const auto aio2_candidates = discover_clockwork_pi_serial_candidates(); + candidates = aio2_candidates; + const auto default_candidates = + split_device_candidates(kDefaultGpsDeviceCandidates); + candidates.insert(candidates.end(), + default_candidates.begin(), + default_candidates.end()); + } + + std::string signature; + for (const auto& candidate : candidates) + { + if (!signature.empty()) + { + signature.push_back(':'); + } + signature += candidate; + } if (signature != s_runtime.auto_candidate_signature) { s_runtime.auto_candidate_signature = signature; - s_runtime.auto_candidate_paths = deduplicate_auto_serial_candidates( - split_device_candidates(signature.c_str())); + s_runtime.auto_candidate_paths = + deduplicate_auto_serial_candidates(candidates); s_runtime.auto_candidate_index = 0; if (s_runtime.source_is_auto_candidate) { @@ -1160,7 +1214,9 @@ int serial_baud_for_path(const std::string& path) const int default_baud = (!env_configured(kGpsBaudEnv) && - (path == "/dev/ttyS0" || path == "/dev/serial0")) + (path == "/dev/ttyS0" || path == "/dev/serial0" || + path.find("ClockworkPI") != std::string::npos || + path.find("uConsole") != std::string::npos)) ? 9600 : 38400; const int env_baud = env_int_or_default(kGpsBaudEnv, default_baud); diff --git a/platform/linux/uconsole/src/uconsole_desktop_shell.cpp b/platform/linux/uconsole/src/uconsole_desktop_shell.cpp index 8ffa6b36..1dd890b3 100644 --- a/platform/linux/uconsole/src/uconsole_desktop_shell.cpp +++ b/platform/linux/uconsole/src/uconsole_desktop_shell.cpp @@ -19,6 +19,7 @@ #include "app/linux_app_services.h" #include "core/canvas.h" #include "lvgl.h" +#include "platform/ui/gps_runtime.h" #include "uconsole/uconsole_chat_workspace_model.h" #include "uconsole/uconsole_dashboard_model.h" #include "uconsole/uconsole_map_workspace_model.h" @@ -399,6 +400,13 @@ class UConsoleDesktopShell services_.tick(); refreshMapPreview(false); const auto now = clock::now(); + if (active_section_ == Section::Gps && + (now - last_gps_preview_refresh_) >= + std::chrono::milliseconds(1000)) + { + last_gps_preview_refresh_ = now; + rebuildDesktopPage(); + } if ((now - last_refresh_) >= std::chrono::milliseconds(500)) { refreshDashboard(false); @@ -748,7 +756,11 @@ class UConsoleDesktopShell return false; } - refreshMapPreview(true); + // Apply the map model change synchronously, but defer the expensive + // tile/image refresh to the normal frame tick. Refreshing LVGL image + // sources here decodes several PNGs on the input loop and makes a + // burst of WASD presses look like only every fourth or fifth press + // was accepted. refreshFooter(); return true; } @@ -1833,6 +1845,15 @@ class UConsoleDesktopShell void buildGpsPreview() { + std::array + live_satellites{}; + std::size_t live_satellite_count = 0; + platform::ui::gps::GnssStatus live_status{}; + const bool have_gnss_snapshot = platform::ui::gps::get_gnss_snapshot( + live_satellites.data(), live_satellites.size(), + &live_satellite_count, &live_status); + const auto gps_state = platform::ui::gps::get_data(); + lv_obj_t* sky = createPreviewPanel( desktop_page_panel_, "Satellite sky plot", 0, embedded_palette::kSurface, true); @@ -1939,26 +1960,70 @@ class UConsoleDesktopShell std::uint32_t border_color; std::uint32_t text_color; }; - constexpr std::array satellites{{ - {12, "GPS", 332.0F, 67.0F, 42, true, 0xE3B11F, 0x3E7D3E, - embedded_palette::kHeaderText}, - {31, "GPS", 296.0F, 52.0F, 39, true, 0xE3B11F, 0x3E7D3E, - embedded_palette::kHeaderText}, - {7, "BDS", 8.0F, 61.0F, 38, true, 0xB94A2C, 0x3E7D3E, - embedded_palette::kWhite}, - {4, "GAL", 158.0F, 48.0F, 36, true, 0x3E7D3E, 0x3E7D3E, - embedded_palette::kWhite}, - {11, "GAL", 132.0F, 35.0F, 33, true, 0x3E7D3E, 0x8FBF4D, - embedded_palette::kWhite}, - {19, "BDS", 226.0F, 29.0F, 30, true, 0xB94A2C, 0x8FBF4D, - embedded_palette::kWhite}, - {22, "GPS", 82.0F, 23.0F, 28, false, 0xE3B11F, 0xB94A2C, - embedded_palette::kHeaderText}, - {5, "GPS", 278.0F, 16.0F, 18, false, 0xE3B11F, 0x6E6E6E, - embedded_palette::kHeaderText}, - }}; - for (const auto& sat : satellites) + std::array satellites{}; + std::size_t satellite_count = 0; + const auto system_name = [](gps::GnssSystem system) { + switch (system) + { + case gps::GnssSystem::GPS: + return "GPS"; + case gps::GnssSystem::GLN: + return "GLN"; + case gps::GnssSystem::GAL: + return "GAL"; + case gps::GnssSystem::BD: + return "BDS"; + case gps::GnssSystem::UNKNOWN: + break; + } + return "UNK"; + }; + const auto system_color = [](gps::GnssSystem system) + { + switch (system) + { + case gps::GnssSystem::GPS: + return std::uint32_t{0xE3B11F}; + case gps::GnssSystem::GLN: + return std::uint32_t{0x2D6FB6}; + case gps::GnssSystem::GAL: + return std::uint32_t{0x3E7D3E}; + case gps::GnssSystem::BD: + return std::uint32_t{0xB94A2C}; + case gps::GnssSystem::UNKNOWN: + break; + } + return std::uint32_t{0x6E6E6E}; + }; + for (std::size_t index = 0; + index < live_satellite_count && index < satellites.size(); ++index) + { + const auto& source = live_satellites[index]; + const auto fill_color = system_color(source.sys); + const auto border_color = + source.used + ? embedded_palette::kStatusGreen + : (source.snr >= 0 && source.snr < 20 + ? embedded_palette::kWarn + : embedded_palette::kTextDim); + satellites[satellite_count++] = { + static_cast(source.id), + system_name(source.sys), + static_cast(source.azimuth), + static_cast(source.elevation), + static_cast(source.snr), + source.used, + fill_color, + border_color, + (source.sys == gps::GnssSystem::BD || + source.sys == gps::GnssSystem::GAL + ? embedded_palette::kWhite + : embedded_palette::kHeaderText)}; + } + for (std::size_t index = 0; index < satellite_count; ++index) + { + const auto& sat = satellites[index]; constexpr int dot_size = 31; const float radius = static_cast(sky_radius) * @@ -2065,13 +2130,43 @@ class UConsoleDesktopShell lv_obj_set_style_text_line_space(legend_note, 3, 0); lv_obj_set_pos(legend_note, legend_x, 376); - createLabel(sky, "North up / live 1 Hz / altitude 18.4 m", - &lv_font_montserrat_12, embedded_palette::kTextWarm); + char sky_meta[96]; + if (gps_state.has_alt) + { + std::snprintf(sky_meta, sizeof(sky_meta), + "North up / live 1 Hz / altitude %.1f m", + gps_state.alt_m); + } + else + { + std::snprintf(sky_meta, sizeof(sky_meta), + "North up / live 1 Hz / altitude --"); + } + createLabel(sky, sky_meta, &lv_font_montserrat_12, + embedded_palette::kTextWarm); lv_obj_t* receiver = createPreviewPanel( desktop_page_panel_, "Satellite status", 330, embedded_palette::kSurface, true); + char summary[96]; + const char* fix_name = "NO FIX"; + switch (live_status.fix) + { + case gps::GnssFix::FIX2D: + fix_name = "2D"; + break; + case gps::GnssFix::FIX3D: + fix_name = "3D"; + break; + case gps::GnssFix::NOFIX: + break; + } + std::snprintf(summary, sizeof(summary), "USE %u/%u HDOP %.1f FIX %s", + static_cast(live_status.sats_in_use), + static_cast(live_status.sats_in_view), + static_cast(live_status.hdop), fix_name); + lv_obj_t* receiver_summary = lv_obj_create(receiver); applyPanel(receiver_summary, embedded_palette::kAccent, embedded_palette::kAccentDark); @@ -2079,7 +2174,7 @@ class UConsoleDesktopShell lv_obj_set_height(receiver_summary, 42); lv_obj_set_style_pad_all(receiver_summary, 8, 0); lv_obj_t* summary_label = - createLabel(receiver_summary, "USE 6/8 HDOP 0.8 FIX 3D", + createLabel(receiver_summary, summary, &lv_font_montserrat_12, embedded_palette::kHeaderText); lv_obj_center(summary_label); @@ -2104,7 +2199,7 @@ class UConsoleDesktopShell lv_obj_set_pos(label, column_x[column], 7); } - for (std::size_t row_index = 0; row_index < satellites.size(); + for (std::size_t row_index = 0; row_index < satellite_count; ++row_index) { const auto& sat = satellites[row_index]; @@ -2148,9 +2243,33 @@ class UConsoleDesktopShell } } + if (satellite_count == 0) + { + const char* waiting_text = have_gnss_snapshot + ? "Waiting for satellites / AIO2 GPS" + : "GPS runtime unavailable"; + lv_obj_t* waiting = + createLabel(receiver, waiting_text, &lv_font_montserrat_12, + embedded_palette::kTextWarm); + lv_obj_set_width(waiting, LV_PCT(100)); + lv_obj_set_style_text_align(waiting, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_y(waiting, 42); + } + + char receiver_meta_text[128]; + if (gps_state.valid) + { + std::snprintf(receiver_meta_text, sizeof(receiver_meta_text), + "Live / %.5f, %.5f / GPS UTC", gps_state.lat, + gps_state.lng); + } + else + { + std::snprintf(receiver_meta_text, sizeof(receiver_meta_text), + "Live / no fix / AIO2 GPS"); + } lv_obj_t* receiver_meta = - createLabel(receiver, "Live / 31.2304, 121.4737 / GPS UTC", - &lv_font_montserrat_10, + createLabel(receiver, receiver_meta_text, &lv_font_montserrat_10, embedded_palette::kTextMuted); lv_obj_set_width(receiver_meta, LV_PCT(100)); addActionPill(receiver, "Open map"); @@ -2925,6 +3044,7 @@ class UConsoleDesktopShell std::vector> map_download_jobs_{}; clock::time_point last_map_preview_refresh_{}; + clock::time_point last_gps_preview_refresh_{}; std::array nav_bindings_{}; std::array nav_buttons_{};