-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add a simple example TUI based on ratatui
- show NAV-PVT msg as in u-center - show ESF-STATUS and ESF-ALG statuses similar to u-center - show MON-VER - a few other things are work in progress, such as: show a graph of the sensor values, properly capture the log using tracing and show it in the UI Signed-off-by: Andrei Gherghescu <[email protected]>
- Loading branch information
Showing
11 changed files
with
1,812 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[package] | ||
authors = ["Andrei Gherghescu <[email protected]>"] | ||
edition = "2021" | ||
name = "ublox-tui" | ||
publish = false | ||
rust-version = "1.70" | ||
version = "0.0.1" | ||
|
||
[dependencies] | ||
anyhow = "1.0" | ||
chrono = "0.4" | ||
clap = { version = "4.5.23", features = ["derive", "cargo"] } | ||
crossterm = { version = "0.28", features = ["event-stream"] } | ||
env_logger = "0.11" | ||
indoc = "2" | ||
log = "0.4" | ||
ratatui = "0.29" | ||
serialport = "4.2" | ||
strum = { version = "0.26", features = ["derive"] } | ||
tracing = "0.1.40" | ||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } | ||
unicode-width = "0.2" | ||
lazy_static = "1.5.0" | ||
tracing-error = "0.2.1" | ||
tui-logger = {version ="0.14.1", features = ["crossterm", "tracing-support"]} | ||
directories = "0.10" | ||
|
||
ublox = { path = "../../ublox" } | ||
|
||
[features] | ||
alloc = ["ublox/alloc"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# TUI example | ||
|
||
This TUI is based on the [Ratatui demo app](https://github.com/ratatui/ratatui/tree/main/examples/apps/demo) | ||
|
||
It is implemented ony for the `crossterm` backend. | ||
|
||
It will show the NAV-PVT and ESF-ALG, ESF-STATUS messages similar to the u-center UI from uBlox. | ||
|
||
```shell | ||
cargo run -p /dev/ttyACM0 | ||
``` | ||
|
||
You should see a TUI like this | ||
|
||
![images/ublox-tui.png](images/ublox-tui.png) | ||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
use core::f64; | ||
use std::{path::PathBuf, vec}; | ||
|
||
use ublox::{ | ||
EsfAlgStatus, EsfSensorFaults, EsfSensorStatusCalibration, EsfSensorStatusTime, EsfSensorType, | ||
EsfStatusFusionMode, EsfStatusImuInit, EsfStatusInsInit, EsfStatusMountAngle, | ||
EsfStatusWheelTickInit, GpsFix, NavPvtFlags, NavPvtFlags2, | ||
}; | ||
|
||
use crate::ui::LogWidget; | ||
|
||
pub struct TabsState<'a> { | ||
pub titles: Vec<&'a str>, | ||
pub index: usize, | ||
} | ||
|
||
impl<'a> TabsState<'a> { | ||
pub const fn new(titles: Vec<&'a str>) -> Self { | ||
Self { titles, index: 0 } | ||
} | ||
pub fn next(&mut self) { | ||
self.index = (self.index + 1) % self.titles.len(); | ||
} | ||
|
||
pub fn previous(&mut self) { | ||
if self.index > 0 { | ||
self.index -= 1; | ||
} else { | ||
self.index = self.titles.len() - 1; | ||
} | ||
} | ||
} | ||
|
||
pub enum UbxStatus { | ||
Pvt(Box<NavPvtWidgetState>), | ||
MonVer(Box<MonVersionWidgetState>), | ||
EsfAlgImu(EsfAlgImuAlignmentWidgetState), | ||
EsfAlgSensors(EsfSensorsWidgetState), | ||
EsfAlgStatus(EsfAlgStatusWidgetState), | ||
} | ||
|
||
#[derive(Debug)] | ||
#[allow(dead_code)] | ||
pub struct NavPvtWidgetState { | ||
pub time_tag: f64, | ||
pub year: u16, | ||
pub month: u8, | ||
pub day: u8, | ||
pub hour: u8, | ||
pub min: u8, | ||
pub sec: u8, | ||
pub valid: u8, | ||
pub time_accuracy: u32, | ||
pub nanosecond: i32, | ||
pub utc_time_accuracy: u32, | ||
pub lat: f64, | ||
pub lon: f64, | ||
pub height: f64, | ||
pub msl: f64, | ||
pub vel_ned: (f64, f64, f64), | ||
pub speed_over_ground: f64, | ||
pub heading_motion: f64, | ||
pub heading_vehicle: f64, | ||
pub magnetic_declination: f64, | ||
|
||
pub pdop: f64, | ||
pub satellites_used: u8, | ||
|
||
pub position_fix_type: GpsFix, | ||
pub fix_flags: NavPvtFlags, | ||
pub invalid_llh: bool, | ||
pub position_accuracy: (f64, f64), | ||
pub velocity_accuracy: f64, | ||
pub heading_accuracy: f64, | ||
pub magnetic_declination_accuracy: f64, | ||
pub flags2: NavPvtFlags2, | ||
} | ||
|
||
impl Default for NavPvtWidgetState { | ||
fn default() -> Self { | ||
Self { | ||
time_tag: f64::NAN, | ||
year: 0, | ||
month: 0, | ||
day: 0, | ||
hour: 0, | ||
min: 0, | ||
sec: 0, | ||
valid: 0, | ||
time_accuracy: 0, | ||
nanosecond: 0, | ||
lat: f64::NAN, | ||
lon: f64::NAN, | ||
height: f64::NAN, | ||
msl: f64::NAN, | ||
vel_ned: (f64::NAN, f64::NAN, f64::NAN), | ||
speed_over_ground: f64::NAN, | ||
heading_motion: f64::NAN, | ||
heading_vehicle: f64::NAN, | ||
magnetic_declination: f64::NAN, | ||
pdop: f64::NAN, | ||
satellites_used: 0, | ||
utc_time_accuracy: 0, | ||
invalid_llh: true, | ||
position_accuracy: (f64::NAN, f64::NAN), | ||
velocity_accuracy: f64::NAN, | ||
heading_accuracy: f64::NAN, | ||
magnetic_declination_accuracy: f64::NAN, | ||
position_fix_type: GpsFix::NoFix, | ||
fix_flags: NavPvtFlags::empty(), | ||
flags2: NavPvtFlags2::empty(), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Default)] | ||
pub struct EsfSensorsWidgetState { | ||
pub sensors: Vec<EsfSensorWidget>, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct EsfSensorWidget { | ||
pub sensor_type: EsfSensorType, | ||
pub calib_status: EsfSensorStatusCalibration, | ||
pub time_status: EsfSensorStatusTime, | ||
pub freq: u16, | ||
pub faults: EsfSensorFaults, | ||
} | ||
|
||
impl Default for EsfSensorWidget { | ||
fn default() -> Self { | ||
Self { | ||
sensor_type: EsfSensorType::Unknown, | ||
calib_status: EsfSensorStatusCalibration::NotCalibrated, | ||
time_status: EsfSensorStatusTime::NoData, | ||
freq: 0, | ||
faults: EsfSensorFaults::default(), | ||
} | ||
} | ||
} | ||
|
||
pub struct EsfAlgStatusWidgetState { | ||
pub time_tag: f64, | ||
pub fusion_mode: EsfStatusFusionMode, | ||
pub imu_status: EsfStatusImuInit, | ||
pub wheel_tick_sensor_status: EsfStatusWheelTickInit, | ||
pub ins_status: EsfStatusInsInit, | ||
pub imu_mount_alignment_status: EsfStatusMountAngle, | ||
} | ||
|
||
impl Default for EsfAlgStatusWidgetState { | ||
fn default() -> Self { | ||
Self { | ||
time_tag: f64::NAN, | ||
fusion_mode: EsfStatusFusionMode::Disabled, | ||
imu_status: EsfStatusImuInit::Off, | ||
wheel_tick_sensor_status: EsfStatusWheelTickInit::Off, | ||
ins_status: EsfStatusInsInit::Off, | ||
imu_mount_alignment_status: EsfStatusMountAngle::Off, | ||
} | ||
} | ||
} | ||
#[derive(Debug)] | ||
pub struct EsfAlgImuAlignmentWidgetState { | ||
pub time_tag: f64, | ||
pub auto_alignment: bool, | ||
pub alignment_status: EsfAlgStatus, | ||
pub angle_singularity: bool, | ||
pub roll: f64, | ||
pub pitch: f64, | ||
pub yaw: f64, | ||
} | ||
|
||
impl Default for EsfAlgImuAlignmentWidgetState { | ||
fn default() -> Self { | ||
Self { | ||
time_tag: f64::NAN, | ||
auto_alignment: false, | ||
alignment_status: EsfAlgStatus::UserDefinedAngles, | ||
angle_singularity: false, | ||
roll: f64::NAN, | ||
pitch: f64::NAN, | ||
yaw: f64::NAN, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Default)] | ||
pub struct MonVersionWidgetState { | ||
pub software_version: [u8; 30], | ||
pub hardware_version: [u8; 10], | ||
pub extensions: String, | ||
} | ||
|
||
#[allow(dead_code)] | ||
pub struct App<'a> { | ||
pub title: &'a str, | ||
pub log_file: PathBuf, | ||
pub pvt_state: NavPvtWidgetState, | ||
pub mon_ver_state: MonVersionWidgetState, | ||
pub esf_sensors_state: EsfSensorsWidgetState, | ||
pub esf_alg_state: EsfAlgStatusWidgetState, | ||
pub esf_alg_imu_alignment_state: EsfAlgImuAlignmentWidgetState, | ||
pub should_quit: bool, | ||
pub tabs: TabsState<'a>, | ||
pub log_widget: LogWidget, | ||
} | ||
|
||
impl<'a> App<'a> { | ||
pub fn new(title: &'a str, log_file: PathBuf) -> Self { | ||
App { | ||
title, | ||
log_file, | ||
pvt_state: NavPvtWidgetState::default(), | ||
mon_ver_state: MonVersionWidgetState::default(), | ||
esf_sensors_state: EsfSensorsWidgetState::default(), | ||
esf_alg_state: EsfAlgStatusWidgetState::default(), | ||
esf_alg_imu_alignment_state: EsfAlgImuAlignmentWidgetState::default(), | ||
should_quit: false, | ||
log_widget: LogWidget, | ||
tabs: TabsState::new(vec![ | ||
"PVT & ESF Status", | ||
"Version Info", | ||
"Sensor Graphs", | ||
"World Map", | ||
]), | ||
} | ||
} | ||
|
||
pub fn on_right(&mut self) { | ||
self.tabs.next(); | ||
} | ||
|
||
pub fn on_left(&mut self) { | ||
self.tabs.previous(); | ||
} | ||
|
||
pub fn on_key(&mut self, c: char) { | ||
match c { | ||
'q' => { | ||
self.should_quit = true; | ||
}, | ||
'Q' => { | ||
self.should_quit = true; | ||
}, | ||
_ => {}, | ||
} | ||
} | ||
} |
Oops, something went wrong.