diff --git a/console_backend/src/advanced_ins_tab.rs b/console_backend/src/advanced_ins_tab.rs new file mode 100644 index 000000000..2b99f7502 --- /dev/null +++ b/console_backend/src/advanced_ins_tab.rs @@ -0,0 +1,375 @@ +use log::error; +use sbp::messages::imu::{MsgImuAux, MsgImuRaw}; + +use capnp::message::Builder; +use capnp::serialize; + +use crate::console_backend_capnp as m; +use crate::constants::*; +use crate::errors::{CAP_N_PROTO_SERIALIZATION_FAILURE, GET_MUT_OBJECT_FAILURE}; +use crate::types::{Deque, MessageSender, SharedState}; + +/// AdvancedInsTab struct. +/// +/// # Fields: +/// +/// - `client_send`: Client Sender channel for communication from backend to frontend. +/// - `imu_conf`: Storage for the Imu configuration. +/// - `imu_temp`: Storage for the raw Imu temperature converted to proper units. +/// - `rms_acc_x`: The calculated root mean squared imu acceleration for x axis. +/// - `rms_acc_y`: The calculated root mean squared imu acceleration for y axis. +/// - `rms_acc_z`: The calculated root mean squared imu acceleration for z axis. +/// - `acc_x`: The stored historic Imu acceleration values along x axis. +/// - `acc_y`: The stored historic Imu acceleration values along y axis. +/// - `acc_z`: The stored historic Imu acceleration values along z axis. +/// - `gyro_x`: The stored historic Imu angular rate values along x axis. +/// - `gyro_y`: The stored historic Imu angular rate values along y axis. +/// - `gyro_z`: The stored historic Imu angular rate values along z axis. +/// - `shared_state`: The shared state for communicating between frontend/backend/other backend tabs. +#[derive(Debug)] +pub struct AdvancedInsTab { + client_sender: S, + imu_conf: u8, + imu_temp: f64, + rms_acc_x: f64, + rms_acc_y: f64, + rms_acc_z: f64, + acc_x: Deque, + acc_y: Deque, + acc_z: Deque, + gyro_x: Deque, + gyro_y: Deque, + gyro_z: Deque, + shared_state: SharedState, +} + +impl AdvancedInsTab { + pub fn new(shared_state: SharedState, client_sender: S) -> AdvancedInsTab { + let acc_fill_val = Some(0_f64); + let gyro_fill_val = Some(0_f64); + AdvancedInsTab { + client_sender, + imu_conf: 0_u8, + imu_temp: 0_f64, + rms_acc_x: 0_f64, + rms_acc_y: 0_f64, + rms_acc_z: 0_f64, + acc_x: Deque::with_size_limit(NUM_POINTS, acc_fill_val), + acc_y: Deque::with_size_limit(NUM_POINTS, acc_fill_val), + acc_z: Deque::with_size_limit(NUM_POINTS, acc_fill_val), + gyro_x: Deque::with_size_limit(NUM_POINTS, gyro_fill_val), + gyro_y: Deque::with_size_limit(NUM_POINTS, gyro_fill_val), + gyro_z: Deque::with_size_limit(NUM_POINTS, gyro_fill_val), + shared_state, + } + } + + /// Method for preparing some rms_acc data and initiating sending of data to frontend. + fn imu_set_data(&mut self) { + let acc_x = &mut self.acc_x.get(); + let acc_y = &mut self.acc_y.get(); + let acc_z = &mut self.acc_z.get(); + let acc_range = self.imu_conf & 0xF; + let sig_figs = f64::powi(2_f64, acc_range as i32 + 1_i32) / f64::powi(2_f64, 15); + let (rms_x, rms_y, rms_z) = { + let mut squared_sum_x: f64 = 0_f64; + let mut squared_sum_y: f64 = 0_f64; + let mut squared_sum_z: f64 = 0_f64; + for idx in 0..NUM_POINTS { + squared_sum_x += f64::powi(acc_x[idx], 2); + squared_sum_y += f64::powi(acc_y[idx], 2); + squared_sum_z += f64::powi(acc_z[idx], 2); + } + ( + f64::sqrt(squared_sum_x / acc_x.len() as f64), + f64::sqrt(squared_sum_y / acc_y.len() as f64), + f64::sqrt(squared_sum_z / acc_z.len() as f64), + ) + }; + self.rms_acc_x = sig_figs * rms_x; + self.rms_acc_y = sig_figs * rms_y; + self.rms_acc_z = sig_figs * rms_z; + self.send_data(); + } + + /// Handler for Imu Aux messages. + /// + /// # Parameters + /// - `msg`: MsgImuAux to extract data from. + pub fn handle_imu_aux(&mut self, msg: MsgImuAux) { + match msg.imu_type { + 0 => { + self.imu_temp = 23_f64 + msg.temp as f64 / f64::powi(2_f64, 9); + self.imu_conf = msg.imu_conf; + } + 1 => { + self.imu_temp = 25_f64 + msg.temp as f64 / 256_f64; + self.imu_conf = msg.imu_conf; + } + _ => { + error!("IMU type {} not known.", msg.imu_type); + } + } + } + + /// Handler for Imu Raw messages. + /// + /// # Parameters + /// - `msg`: MsgImuRaw to extract data from. + pub fn handle_imu_raw(&mut self, msg: MsgImuRaw) { + self.acc_x.add(msg.acc_x as f64); + self.acc_y.add(msg.acc_y as f64); + self.acc_z.add(msg.acc_z as f64); + self.gyro_x.add(msg.gyr_x as f64); + self.gyro_y.add(msg.gyr_y as f64); + self.gyro_z.add(msg.gyr_z as f64); + self.imu_set_data(); + } + + /// Package data into a message buffer and send to frontend. + fn send_data(&mut self) { + let mut builder = Builder::new_default(); + let msg = builder.init_root::(); + + let mut tab_status = msg.init_advanced_ins_status(); + + let mut tab_points = tab_status.reborrow().init_data(NUM_INS_PLOT_ROWS as u32); + + let mut points_vec = vec![ + self.acc_x.get(), + self.acc_y.get(), + self.acc_z.get(), + self.gyro_x.get(), + self.gyro_y.get(), + self.gyro_z.get(), + ]; + for idx in 0..NUM_INS_PLOT_ROWS { + let points = points_vec.get_mut(idx).expect(GET_MUT_OBJECT_FAILURE); + let mut point_val_idx = tab_points.reborrow().init(idx as u32, points.len() as u32); + for idx in 0..NUM_POINTS { + let mut point_val = point_val_idx.reborrow().get(idx as u32); + point_val.set_x(idx as f64); + point_val.set_y(points[idx]); + } + } + let fields_data = { + vec![ + self.imu_temp, + self.imu_conf as f64, + self.rms_acc_x, + self.rms_acc_y, + self.rms_acc_z, + ] + }; + let mut fields_data_status = tab_status + .reborrow() + .init_fields_data(NUM_INS_FIELDS as u32); + + for (i, datur) in fields_data.iter().enumerate() { + fields_data_status.set(i as u32, *datur); + } + + let mut msg_bytes: Vec = vec![]; + serialize::write_message(&mut msg_bytes, &builder) + .expect(CAP_N_PROTO_SERIALIZATION_FAILURE); + self.client_sender.send_data(msg_bytes); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::types::TestSender; + use sbp::messages::imu::{MsgImuAux, MsgImuRaw}; + + #[test] + fn handle_imu_raw_test() { + let shared_state = SharedState::new(); + let client_send = TestSender { inner: Vec::new() }; + let mut ins_tab = AdvancedInsTab::new(shared_state, client_send); + let tow = 1_u32; + let tow_f = 1_u8; + let acc_x = 2_i16; + let acc_y = 3_i16; + let acc_z = 4_i16; + let gyr_x = 5_i16; + let gyr_y = 6_i16; + let gyr_z = 7_i16; + let msg = MsgImuRaw { + sender_id: Some(1337), + tow, + tow_f, + acc_x, + acc_y, + acc_z, + gyr_x, + gyr_y, + gyr_z, + }; + let acc_xs = ins_tab.acc_x.get(); + let acc_ys = ins_tab.acc_y.get(); + let acc_zs = ins_tab.acc_z.get(); + let gyro_xs = ins_tab.gyro_x.get(); + let gyro_ys = ins_tab.gyro_y.get(); + let gyro_zs = ins_tab.gyro_z.get(); + for idx in 0..NUM_POINTS { + assert!(f64::abs(acc_xs[idx] - 0_f64) <= f64::EPSILON); + assert!(f64::abs(acc_ys[idx] - 0_f64) <= f64::EPSILON); + assert!(f64::abs(acc_zs[idx] - 0_f64) <= f64::EPSILON); + assert!(f64::abs(gyro_xs[idx] - 0_f64) <= f64::EPSILON); + assert!(f64::abs(gyro_ys[idx] - 0_f64) <= f64::EPSILON); + assert!(f64::abs(gyro_zs[idx] - 0_f64) <= f64::EPSILON); + } + assert!(f64::abs(ins_tab.rms_acc_x - 0_f64) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_y - 0_f64) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_z - 0_f64) <= f64::EPSILON); + ins_tab.handle_imu_raw(msg); + let acc_xs = ins_tab.acc_x.get(); + let acc_ys = ins_tab.acc_y.get(); + let acc_zs = ins_tab.acc_z.get(); + let gyro_xs = ins_tab.gyro_x.get(); + let gyro_ys = ins_tab.gyro_y.get(); + let gyro_zs = ins_tab.gyro_z.get(); + assert!(f64::abs(acc_xs[NUM_POINTS - 1] - acc_x as f64) <= f64::EPSILON); + assert!(f64::abs(acc_ys[NUM_POINTS - 1] - acc_y as f64) <= f64::EPSILON); + assert!(f64::abs(acc_zs[NUM_POINTS - 1] - acc_z as f64) <= f64::EPSILON); + assert!(f64::abs(gyro_xs[NUM_POINTS - 1] - gyr_x as f64) <= f64::EPSILON); + assert!(f64::abs(gyro_ys[NUM_POINTS - 1] - gyr_y as f64) <= f64::EPSILON); + assert!(f64::abs(gyro_zs[NUM_POINTS - 1] - gyr_z as f64) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_x - 0_f64) > f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_y - 0_f64) > f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_z - 0_f64) > f64::EPSILON); + } + + #[test] + fn handle_imu_aux_test() { + let shared_state = SharedState::new(); + let client_send = TestSender { inner: Vec::new() }; + let mut ins_tab = AdvancedInsTab::new(shared_state.clone(), client_send.clone()); + let imu_type_a = 0_u8; + let imu_type_b = 1_u8; + let imu_type_unknown = 2_u8; + let imu_conf = 1_u8; + let temp = 200; + let msg = MsgImuAux { + sender_id: Some(1337), + imu_type: imu_type_unknown, + imu_conf, + temp, + }; + assert!(f64::abs(ins_tab.imu_temp - 0_f64) <= f64::EPSILON); + assert_eq!(ins_tab.imu_conf, 0_u8); + ins_tab.handle_imu_aux(msg); + assert!(f64::abs(ins_tab.imu_temp - 0_f64) <= f64::EPSILON); + assert_ne!(ins_tab.imu_conf, imu_conf); + + let mut ins_tab = AdvancedInsTab::new(shared_state.clone(), client_send.clone()); + let msg = MsgImuAux { + sender_id: Some(1337), + imu_type: imu_type_a, + imu_conf, + temp, + }; + assert!(f64::abs(ins_tab.imu_temp - 0_f64) <= f64::EPSILON); + assert_eq!(ins_tab.imu_conf, 0_u8); + ins_tab.handle_imu_aux(msg); + assert!(f64::abs(ins_tab.imu_temp - 23.390625_f64) <= f64::EPSILON); + assert_eq!(ins_tab.imu_conf, imu_conf); + + let mut ins_tab = AdvancedInsTab::new(shared_state, client_send); + let msg = MsgImuAux { + sender_id: Some(1337), + imu_type: imu_type_b, + imu_conf, + temp, + }; + assert!(f64::abs(ins_tab.imu_temp - 0_f64) <= f64::EPSILON); + assert_eq!(ins_tab.imu_conf, 0_u8); + ins_tab.handle_imu_aux(msg); + assert!(f64::abs(ins_tab.imu_temp - 25.78125_f64) <= f64::EPSILON); + assert_eq!(ins_tab.imu_conf, imu_conf); + } + + #[test] + fn handle_imu_send_data_test() { + let shared_state = SharedState::new(); + let client_send = TestSender { inner: Vec::new() }; + let mut ins_tab = AdvancedInsTab::new(shared_state, client_send); + + assert!(f64::abs(ins_tab.rms_acc_x - 0_f64) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_y - 0_f64) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_z - 0_f64) <= f64::EPSILON); + + let imu_type = 0_u8; + let imu_conf = 1_u8; + let temp = 200; + let msg = MsgImuAux { + sender_id: Some(1337), + imu_type, + imu_conf, + temp, + }; + ins_tab.handle_imu_aux(msg); + + let tow = 1_u32; + let tow_f = 1_u8; + let acc_x = 2_i16; + let acc_y = 3_i16; + let acc_z = 4_i16; + let gyr_x = 5_i16; + let gyr_y = 6_i16; + let gyr_z = 7_i16; + let msg = MsgImuRaw { + sender_id: Some(1337), + tow, + tow_f, + acc_x, + acc_y, + acc_z, + gyr_x, + gyr_y, + gyr_z, + }; + ins_tab.handle_imu_raw(msg); + let sig_figs = 0.0001220703125_f64; + let acc_x = acc_x as f64; + let acc_y = acc_y as f64; + let acc_z = acc_z as f64; + + let rms_acc_x = f64::sqrt((acc_x * acc_x) / NUM_POINTS as f64); + let rms_acc_y = f64::sqrt((acc_y * acc_y) / NUM_POINTS as f64); + let rms_acc_z = f64::sqrt((acc_z * acc_z) / NUM_POINTS as f64); + assert!(f64::abs(ins_tab.rms_acc_x - rms_acc_x * sig_figs) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_y - rms_acc_y * sig_figs) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_z - rms_acc_z * sig_figs) <= f64::EPSILON); + + let acc_x = 4_i16; + let acc_y = 6_i16; + let acc_z = 8_i16; + let msg = MsgImuRaw { + sender_id: Some(1337), + tow, + tow_f, + acc_x, + acc_y, + acc_z, + gyr_x, + gyr_y, + gyr_z, + }; + ins_tab.handle_imu_raw(msg); + let sig_figs = 0.0001220703125_f64; + let acc_x = acc_x as f64; + let acc_y = acc_y as f64; + let acc_z = acc_z as f64; + let rms_acc_x = + f64::sqrt((acc_x * acc_x + (acc_x / 2_f64) * (acc_x / 2_f64)) / NUM_POINTS as f64); + let rms_acc_y = + f64::sqrt((acc_y * acc_y + (acc_y / 2_f64) * (acc_y / 2_f64)) / NUM_POINTS as f64); + let rms_acc_z = + f64::sqrt((acc_z * acc_z + (acc_z / 2_f64) * (acc_z / 2_f64)) / NUM_POINTS as f64); + assert!(f64::abs(ins_tab.rms_acc_x - rms_acc_x * sig_figs) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_y - rms_acc_y * sig_figs) <= f64::EPSILON); + assert!(f64::abs(ins_tab.rms_acc_z - rms_acc_z * sig_figs) <= f64::EPSILON); + } +} diff --git a/console_backend/src/common_constants.rs b/console_backend/src/common_constants.rs index c36f0d33a..c01c16249 100644 --- a/console_backend/src/common_constants.rs +++ b/console_backend/src/common_constants.rs @@ -30,8 +30,16 @@ pub enum Tabs { SETTINGS, #[strum(serialize = "UPDATE")] UPDATE, - #[strum(serialize = "ADVANCED")] - ADVANCED, + #[strum(serialize = "ADVANCED_SYSTEM_MONITOR")] + ADVANCED_SYSTEM_MONITOR, + #[strum(serialize = "ADVANCED_INS")] + ADVANCED_INS, + #[strum(serialize = "ADVANCED_MAGNETOMETER")] + ADVANCED_MAGNETOMETER, + #[strum(serialize = "ADVANCED_NETWORKING")] + ADVANCED_NETWORKING, + #[strum(serialize = "ADVANCED_SPECTRUM_ANALYZER")] + ADVANCED_SPECTRUM_ANALYZER, } #[derive(Clone, Debug, Display, EnumString, EnumVariantNames, Eq, Hash, PartialEq)] @@ -144,6 +152,10 @@ pub enum Keys { SBP_LOGGING_LABELS, #[strum(serialize = "LOG_LEVEL_LABELS")] LOG_LEVEL_LABELS, + #[strum(serialize = "FIELDS_DATA")] + FIELDS_DATA, + #[strum(serialize = "XMIN")] + XMIN_OFFSET, } #[derive(Clone, Debug, Display, EnumString, EnumVariantNames, Eq, Hash, PartialEq)] diff --git a/console_backend/src/constants.rs b/console_backend/src/constants.rs index 8ebf1e610..14273395f 100644 --- a/console_backend/src/constants.rs +++ b/console_backend/src/constants.rs @@ -1,4 +1,3 @@ -use crate::common_constants::Tabs; // 'Universal' constants pub const NANOSECONDS_PER_SECOND: f64 = 1.0e+9; @@ -8,17 +7,6 @@ pub const APPLICATION_QUALIFIER: &str = "com.swift-nav"; pub const APPLICATION_ORGANIZATION: &str = "swift-nav"; pub const APPLICATION_NAME: &str = "swift_navigation_console"; // CLI constants. -pub const TAB_LIST: &[Tabs] = &[ - Tabs::TRACKING_SIGNALS, - Tabs::TRACKING_SKYPLOT, - Tabs::SOLUTION_POSITION, - Tabs::SOLUTION_VELOCITY, - Tabs::BASELINE, - Tabs::OBSERVATIONS, - Tabs::SETTINGS, - Tabs::UPDATE, - Tabs::ADVANCED, -]; // Server constants. pub const CLOSE: &str = "CLOSE"; @@ -39,6 +27,10 @@ pub const DEFAULT_LOG_DIRECTORY: &str = "SwiftNav"; // Common constants. pub const NUM_POINTS: usize = 200; +// Advanced Ins Tab constants. +pub const NUM_INS_PLOT_ROWS: usize = 6; +pub const NUM_INS_FIELDS: usize = 5; + // Navbar constants. pub const AVAILABLE_REFRESH_RATES: [u8; 4] = [1, 5, 10, 25]; pub const AVAILABLE_BAUDRATES: [u32; 6] = [57600, 115200, 230400, 460800, 921600, 1000000]; @@ -62,9 +54,9 @@ pub const GLO_SLOT_SAT_MAX: u8 = 90; pub const GLO_FCN_OFFSET: i16 = 8; pub const SBAS_NEG_OFFSET: i16 = 120; pub const QZSS_NEG_OFFSET: i16 = 193; -pub const SNR_THRESHOLD: f64 = 15.0; -pub const TRACKING_SIGNALS_PLOT_MAX: f64 = 60.0; pub const GUI_UPDATE_PERIOD: f64 = 0.2; +pub const CHART_XMIN_OFFSET_NO_TRACKING: f64 = -45.0; +pub const CHART_XMIN_OFFSET_TRACKING: f64 = -95.0; pub const SHOW_LEGEND: &str = "Show Legend"; diff --git a/console_backend/src/errors.rs b/console_backend/src/errors.rs index 68d38b9d7..b95f71c80 100644 --- a/console_backend/src/errors.rs +++ b/console_backend/src/errors.rs @@ -3,3 +3,4 @@ pub const SHARED_STATE_LOCK_MUTEX_FAILURE: &str = "unable to lock shared_state m pub const CAP_N_PROTO_SERIALIZATION_FAILURE: &str = "unable to serialize capnproto message"; pub const CAP_N_PROTO_DESERIALIZATION_FAILURE: &str = "unable to deserialize capnproto message"; pub const CONVERT_TO_STR_FAILURE: &str = "error converting to str"; +pub const GET_MUT_OBJECT_FAILURE: &str = "error trying to get mut object"; diff --git a/console_backend/src/lib.rs b/console_backend/src/lib.rs index a86063388..fd24047a7 100644 --- a/console_backend/src/lib.rs +++ b/console_backend/src/lib.rs @@ -1,3 +1,4 @@ +pub mod advanced_ins_tab; pub mod cli_options; pub mod console_backend_capnp { include!(concat!(env!("OUT_DIR"), "/console_backend_capnp.rs")); diff --git a/console_backend/src/main_tab.rs b/console_backend/src/main_tab.rs index 301d3b9fe..162d07669 100644 --- a/console_backend/src/main_tab.rs +++ b/console_backend/src/main_tab.rs @@ -3,6 +3,7 @@ use log::{debug, error}; use sbp::{messages::SBP, time::GpsTime}; use std::{path::PathBuf, result::Result, thread::sleep, time::Instant}; +use crate::advanced_ins_tab::AdvancedInsTab; use crate::common_constants::SbpLogging; use crate::constants::*; use crate::observation_tab::ObservationTab; @@ -23,6 +24,7 @@ pub struct MainTab<'a, S: MessageSender> { last_gps_time: Option, client_sender: S, shared_state: SharedState, + pub advanced_ins_tab: AdvancedInsTab, pub tracking_signals_tab: TrackingSignalsTab, pub solution_tab: SolutionTab, pub observation_tab: ObservationTab, @@ -41,6 +43,7 @@ impl<'a, S: MessageSender> MainTab<'a, S> { last_gps_update: Instant::now(), client_sender: client_sender.clone(), shared_state: shared_state.clone(), + advanced_ins_tab: AdvancedInsTab::new(shared_state.clone(), client_sender.clone()), tracking_signals_tab: TrackingSignalsTab::new( shared_state.clone(), client_sender.clone(), diff --git a/console_backend/src/process_messages.rs b/console_backend/src/process_messages.rs index 7093f783f..77032d3fe 100644 --- a/console_backend/src/process_messages.rs +++ b/console_backend/src/process_messages.rs @@ -67,6 +67,12 @@ pub fn process_messages( SBP::MsgHeartbeat(_) => { main.status_bar.handle_heartbeat(); } + SBP::MsgImuAux(msg) => { + main.advanced_ins_tab.handle_imu_aux(msg); + } + SBP::MsgImuRaw(msg) => { + main.advanced_ins_tab.handle_imu_raw(msg); + } SBP::MsgInsStatus(msg) => { main.solution_tab.handle_ins_status(msg.clone()); main.status_bar.handle_ins_status(msg); diff --git a/console_backend/src/solution_tab.rs b/console_backend/src/solution_tab.rs index 5a01244bd..ae681d378 100644 --- a/console_backend/src/solution_tab.rs +++ b/console_backend/src/solution_tab.rs @@ -115,8 +115,8 @@ impl SolutionTab { GnssModes::Sbas.label(), ] }, - lats: Deque::with_size_limit(PLOT_HISTORY_MAX), - lngs: Deque::with_size_limit(PLOT_HISTORY_MAX), + lats: Deque::with_size_limit(PLOT_HISTORY_MAX, /*fill_value=*/ None), + lngs: Deque::with_size_limit(PLOT_HISTORY_MAX, /*fill_value=*/ None), last_ins_status_receipt_time: Instant::now(), last_pos_mode: 0, last_odo_update_time: Instant::now(), @@ -125,7 +125,7 @@ impl SolutionTab { lat_min: LAT_MIN, lon_max: LON_MAX, lon_min: LON_MIN, - modes: Deque::with_size_limit(PLOT_HISTORY_MAX), + modes: Deque::with_size_limit(PLOT_HISTORY_MAX, /*fill_value=*/ None), mode_strings: vec![ GnssModes::Spp.to_string(), GnssModes::Dgnss.to_string(), @@ -143,7 +143,12 @@ impl SolutionTab { slns: { SOLUTION_DATA_KEYS .iter() - .map(|key| (*key, Deque::with_size_limit(PLOT_HISTORY_MAX))) + .map(|key| { + ( + *key, + Deque::with_size_limit(PLOT_HISTORY_MAX, /*fill_value=*/ None), + ) + }) .collect() }, table: { diff --git a/console_backend/src/solution_velocity_tab.rs b/console_backend/src/solution_velocity_tab.rs index 35757d718..011260b78 100644 --- a/console_backend/src/solution_velocity_tab.rs +++ b/console_backend/src/solution_velocity_tab.rs @@ -51,8 +51,8 @@ impl<'a, S: MessageSender> SolutionVelocityTab<'a, S> { min: 0_f64, multiplier: VelocityUnits::Mps.get_multiplier(), points: vec![ - Deque::with_size_limit(NUM_POINTS), - Deque::with_size_limit(NUM_POINTS), + Deque::with_size_limit(NUM_POINTS, /*fill_value=*/ None), + Deque::with_size_limit(NUM_POINTS, /*fill_value=*/ None), ], shared_state, tow: 0_f64, @@ -63,8 +63,8 @@ impl<'a, S: MessageSender> SolutionVelocityTab<'a, S> { fn convert_points(&mut self, new_unit: VelocityUnits) { let new_mult = new_unit.get_multiplier(); let mut points = vec![ - Deque::with_size_limit(NUM_POINTS), - Deque::with_size_limit(NUM_POINTS), + Deque::with_size_limit(NUM_POINTS, /*fill_value=*/ None), + Deque::with_size_limit(NUM_POINTS, /*fill_value=*/ None), ]; let hpoints = &mut self.points[0].get(); let vpoints = &mut self.points[1].get(); diff --git a/console_backend/src/tracking_signals_tab.rs b/console_backend/src/tracking_signals_tab.rs index f109bc0a6..045bc056e 100644 --- a/console_backend/src/tracking_signals_tab.rs +++ b/console_backend/src/tracking_signals_tab.rs @@ -1,5 +1,8 @@ use ordered_float::OrderedFloat; -use std::{collections::HashMap, time::Instant}; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; use capnp::message::Builder; use capnp::serialize; @@ -48,9 +51,8 @@ pub struct TrackingSignalsTab { pub gps_tow: f64, pub gps_week: u16, pub incoming_obs_cn0: HashMap<(SignalCodes, i16), f64>, + pub last_obs_update_time: Instant, pub last_update_time: Instant, - pub max: f64, - pub min: f64, pub prev_obs_count: u8, pub prev_obs_total: u8, pub received_codes: Vec, @@ -88,9 +90,8 @@ impl TrackingSignalsTab { gps_tow: 0.0, gps_week: 0, incoming_obs_cn0: HashMap::new(), + last_obs_update_time: Instant::now(), last_update_time: Instant::now(), - max: TRACKING_SIGNALS_PLOT_MAX, - min: SNR_THRESHOLD, prev_obs_count: 0, prev_obs_total: 0, received_codes: Vec::new(), @@ -99,7 +100,7 @@ impl TrackingSignalsTab { sv_labels: Vec::new(), t_init: Instant::now(), time: { - let mut time = Deque::with_size_limit(NUM_POINTS); + let mut time = Deque::with_size_limit(NUM_POINTS, /*fill_value=*/ None); for x in (0..(NUM_POINTS as i32)).rev() { time.add((-x as f64) * (1.0 / TRK_RATE)); } @@ -117,7 +118,7 @@ impl TrackingSignalsTab { let cn0_deque = self .cn0_dict .entry(key) - .or_insert_with(|| Deque::with_size_limit(NUM_POINTS)); + .or_insert_with(|| Deque::with_size_limit(NUM_POINTS, /*fill_value=*/ None)); cn0_deque.add((OrderedFloat(t), cn0)); } /// Push carrier-to-noise density age to cn0_age with key. @@ -319,6 +320,11 @@ impl TrackingSignalsTab { if self.at_least_one_track_received { return; } + if Instant::now() - self.last_obs_update_time <= Duration::from_secs_f64(GUI_UPDATE_PERIOD) + { + return; + } + self.last_obs_update_time = Instant::now(); let mut codes_that_came: Vec<(SignalCodes, i16)> = Vec::new(); let t = (Instant::now()).duration_since(self.t_init).as_secs_f64(); self.time.add(t); @@ -358,14 +364,20 @@ impl TrackingSignalsTab { self.incoming_obs_cn0.clear() } + fn chart_xmin_offset(&self) -> f64 { + if self.at_least_one_track_received { + CHART_XMIN_OFFSET_TRACKING + } else { + CHART_XMIN_OFFSET_NO_TRACKING + } + } + /// Package data into a message buffer and send to frontend. fn send_data(&mut self) { let mut builder = Builder::new_default(); let msg = builder.init_root::(); let mut tracking_signals_status = msg.init_tracking_signals_status(); - tracking_signals_status.set_min(self.min); - tracking_signals_status.set_max(self.max); let mut labels = tracking_signals_status .reborrow() .init_labels(self.sv_labels.len() as u32); @@ -398,6 +410,7 @@ impl TrackingSignalsTab { } } } + tracking_signals_status.set_xmin_offset(self.chart_xmin_offset()); let mut tracking_checkbox_labels = tracking_signals_status .reborrow() .init_check_labels(self.check_labels.len() as u32); @@ -413,6 +426,8 @@ impl TrackingSignalsTab { #[cfg(test)] mod tests { + use std::thread::sleep; + use super::*; use sbp::messages::{ gnss::{CarrierPhase, GPSTime, GnssSignal}, @@ -579,7 +594,7 @@ mod tests { sat, }, }); - + sleep(Duration::from_secs_f64(GUI_UPDATE_PERIOD)); assert_eq!(tracking_signals_tab.cn0_dict.len(), 0); tracking_signals_tab.handle_obs(ObservationMsg::MsgObs(obs_msg)); assert_eq!(tracking_signals_tab.cn0_dict.len(), 1); diff --git a/console_backend/src/types.rs b/console_backend/src/types.rs index b8ae0aa4e..dc52a3b69 100644 --- a/console_backend/src/types.rs +++ b/console_backend/src/types.rs @@ -51,12 +51,14 @@ pub struct Deque { d: Vec, capacity: usize, } -impl Deque { - pub fn with_size_limit(capacity: usize) -> Deque { - Deque { - d: Vec::new(), - capacity, - } +impl Deque { + pub fn with_size_limit(capacity: usize, fill_value: Option) -> Deque { + let d = if let Some(val) = fill_value { + vec![val; capacity] + } else { + vec![] + }; + Deque { d, capacity } } pub fn add(&mut self, ele: T) { if self.d.len() == self.capacity { diff --git a/console_backend/tests/data/ins_updates.sbp b/console_backend/tests/data/ins_updates.sbp new file mode 100644 index 000000000..870c1556e --- /dev/null +++ b/console_backend/tests/data/ins_updates.sbp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4da3a40d88bdd6d4c3e322b5c61ef9cf461492145a1e60aae0447e6aafc9c650 +size 1475786 diff --git a/resources/AdvancedTab.qml b/resources/AdvancedTab.qml new file mode 100644 index 000000000..850059466 --- /dev/null +++ b/resources/AdvancedTab.qml @@ -0,0 +1,73 @@ +import "AdvancedTabComponents" as AdvancedTabComponents +import "Constants" +import QtCharts 2.2 +import QtQuick 2.5 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 + +Item { + id: advancedTab + + width: parent.width + height: parent.height + + TabBar { + id: advancedBar + + z: Constants.commonChart.zAboveCharts + currentIndex: Globals.initialMainTabIndex == 6 ? Globals.initialSubTabIndex : 0 + contentHeight: Constants.tabBarHeight + + Repeater { + model: ["System Monitor", "INS", "Magnetometer", "Networking", "Spectrum Analyzer"] + + TabButton { + text: modelData + width: implicitWidth + } + + } + + } + + Rectangle { + id: advancedTabBackground + + width: parent.width + height: parent.height + anchors.top: advancedBar.bottom + anchors.bottom: advancedTab.bottom + Component.onCompleted: { + } + + StackLayout { + id: advancedBarLayout + + width: parent.width + height: parent.height + currentIndex: advancedBar.currentIndex + + Item { + id: advancedSystemMonitorTab + } + + AdvancedTabComponents.AdvancedInsTab { + } + + Item { + id: advancedMagnetometerTab + } + + Item { + id: advancedNetworkingTab + } + + Item { + id: advancedSpectrumAnalyzerTab + } + + } + + } + +} diff --git a/resources/AdvancedTabComponents/AdvancedInsTab.qml b/resources/AdvancedTabComponents/AdvancedInsTab.qml new file mode 100644 index 000000000..4409057ee --- /dev/null +++ b/resources/AdvancedTabComponents/AdvancedInsTab.qml @@ -0,0 +1,314 @@ +import "../Constants" +import QtCharts 2.3 +import QtQuick 2.6 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import SwiftConsole 1.0 + +Item { + id: advancedInsTab + + property variant lines: [] + + width: parent.width + height: parent.height + Component.onCompleted: { + } + + AdvancedInsPoints { + id: advancedInsPoints + } + + ColumnLayout { + id: advancedInsArea + + width: parent.width + height: parent.height + + ChartView { + id: advancedInsChart + + visible: false + title: Constants.advancedIns.title + titleColor: Constants.advancedIns.titleColor + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignTop + backgroundColor: Constants.commonChart.backgroundColor + plotAreaColor: Constants.commonChart.areaColor + legend.visible: false + antialiasing: true + Component.onCompleted: { + } + + titleFont { + pointSize: Constants.advancedIns.titlePointSize + bold: true + } + + Rectangle { + id: lineLegend + + implicitHeight: lineLegendRepeater.height + width: lineLegendRepeater.width + border.color: Constants.commonLegend.borderColor + border.width: Constants.commonLegend.borderWidth + anchors.bottom: advancedInsChart.bottom + anchors.left: advancedInsChart.left + anchors.bottomMargin: Constants.advancedIns.legendBottomMargin + anchors.leftMargin: Constants.advancedIns.legendLeftMargin + + Column { + id: lineLegendRepeater + + padding: Constants.commonLegend.padding + anchors.bottom: lineLegend.bottom + + Repeater { + id: lineLegendRepeaterRows + + model: Constants.advancedIns.legendLabels + + Row { + Component.onCompleted: { + for (var idx in Constants.advancedIns.lineColors) { + if (lineLegendRepeaterRows.itemAt(idx)) + lineLegendRepeaterRows.itemAt(idx).children[0].color = Constants.advancedIns.lineColors[idx]; + + } + } + + Rectangle { + id: marker + + width: Constants.commonLegend.markerWidth + height: Constants.commonLegend.markerHeight + anchors.verticalCenter: parent.verticalCenter + } + + Text { + id: label + + text: modelData + font.pointSize: Constants.smallPointSize + font.bold: true + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: Constants.commonLegend.verticalCenterOffset + } + + } + + } + + } + + } + + ValueAxis { + id: advancedInsXAxis + + gridVisible: true + lineVisible: true + minorGridVisible: true + minorGridLineColor: Constants.commonChart.minorGridLineColor + gridLineColor: Constants.commonChart.gridLineColor + labelsColor: Constants.commonChart.labelsColor + tickInterval: Constants.advancedIns.xAxisTickCount + tickType: ValueAxis.TicksDynamic + min: Constants.advancedIns.xAxisMin + max: Constants.advancedIns.xAxisMax + + labelsFont { + pointSize: Constants.mediumPointSize + bold: true + } + + } + + ValueAxis { + id: advancedInsYAxis + + gridVisible: true + lineVisible: true + minorGridVisible: true + minorGridLineColor: Constants.commonChart.minorGridLineColor + gridLineColor: Constants.commonChart.gridLineColor + labelsColor: Constants.commonChart.labelsColor + tickInterval: Constants.advancedIns.yAxisTickCount + tickType: ValueAxis.TicksDynamic + min: Constants.advancedIns.yAxisMin + max: Constants.advancedIns.yAxisMax + + labelsFont { + pointSize: Constants.mediumPointSize + bold: true + } + + } + + Timer { + id: advancedInsTimer + + interval: Utils.hzToMilliseconds(Globals.currentRefreshRate) + running: true + repeat: true + onTriggered: { + if (!advancedTab.visible) + return ; + + advancedInsChart.visible = true; + advanced_ins_model.fill_console_points(advancedInsPoints); + if (!advancedInsPoints.points.length) + return ; + + var points = advancedInsPoints.points; + textDataRow.visible = true; + if (!lines.length) { + for (var idx in advancedInsPoints.points) { + var line = advancedInsChart.createSeries(ChartView.SeriesTypeLine, idx, advancedInsXAxis); + line.color = Constants.advancedIns.lineColors[idx]; + line.width = Constants.commonChart.lineWidth; + line.axisYRight = advancedInsYAxis; + line.useOpenGL = Globals.useOpenGL; + lines.push(line); + } + } + imuTempText.text = `${advancedInsPoints.fields_data[0].toFixed(2)} C`; + imuConfText.text = `0x${advancedInsPoints.fields_data[1].toString(16).padStart(2, "0")}`; + rmsAccXText.text = `${advancedInsPoints.fields_data[2].toFixed(2)} g`; + rmsAccYText.text = `${advancedInsPoints.fields_data[3].toFixed(2)} g`; + rmsAccZText.text = `${advancedInsPoints.fields_data[4].toFixed(2)} g`; + advancedInsPoints.fill_series(lines); + } + } + + } + + RowLayout { + id: textDataRow + + visible: false + Layout.fillWidth: true + Layout.preferredHeight: Constants.navBar.urlBarHeight + Layout.alignment: Qt.AlignBottom + + Text { + text: Constants.advancedIns.textDataLabels[0] + Layout.preferredWidth: Constants.advancedIns.textDataLabelWidth + font.pointSize: Constants.mediumPointSize + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Constants.advancedIns.textDataBarHeight + Layout.alignment: Qt.AlignVCenter + border.width: Constants.advancedIns.textDataBarBorderWidth + + Text { + id: imuTempText + + clip: true + anchors.fill: parent + anchors.margins: Constants.advancedIns.textDataBarMargin + font.pointSize: Constants.mediumPointSize + } + + } + + Text { + text: Constants.advancedIns.textDataLabels[1] + Layout.preferredWidth: Constants.advancedIns.textDataLabelWidth + font.pointSize: Constants.mediumPointSize + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Constants.advancedIns.textDataBarHeight + Layout.alignment: Qt.AlignVCenter + border.width: Constants.advancedIns.textDataBarBorderWidth + + Text { + id: imuConfText + + clip: true + anchors.fill: parent + anchors.margins: Constants.advancedIns.textDataBarMargin + font.pointSize: Constants.mediumPointSize + } + + } + + Text { + text: Constants.advancedIns.textDataLabels[2] + Layout.preferredWidth: Constants.advancedIns.textDataLabelWidth + font.pointSize: Constants.mediumPointSize + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Constants.advancedIns.textDataBarHeight + Layout.alignment: Qt.AlignVCenter + border.width: Constants.advancedIns.textDataBarBorderWidth + + Text { + id: rmsAccXText + + clip: true + anchors.fill: parent + anchors.margins: Constants.advancedIns.textDataBarMargin + font.pointSize: Constants.mediumPointSize + } + + } + + Text { + text: Constants.advancedIns.textDataLabels[3] + Layout.preferredWidth: Constants.advancedIns.textDataLabelWidth + font.pointSize: Constants.mediumPointSize + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Constants.advancedIns.textDataBarHeight + Layout.alignment: Qt.AlignVCenter + border.width: Constants.advancedIns.textDataBarBorderWidth + + Text { + id: rmsAccYText + + clip: true + anchors.fill: parent + anchors.margins: Constants.advancedIns.textDataBarMargin + font.pointSize: Constants.mediumPointSize + } + + } + + Text { + text: Constants.advancedIns.textDataLabels[4] + Layout.preferredWidth: Constants.advancedIns.textDataLabelWidth + font.pointSize: Constants.mediumPointSize + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Constants.advancedIns.textDataBarHeight + Layout.alignment: Qt.AlignVCenter + border.width: Constants.advancedIns.textDataBarBorderWidth + + Text { + id: rmsAccZText + + clip: true + anchors.fill: parent + anchors.margins: Constants.advancedIns.textDataBarMargin + font.pointSize: Constants.mediumPointSize + } + + } + + } + + } + +} diff --git a/resources/Constants/Constants.qml b/resources/Constants/Constants.qml index a744ca48a..73c0d4db0 100644 --- a/resources/Constants/Constants.qml +++ b/resources/Constants/Constants.qml @@ -16,6 +16,7 @@ QtObject { property QtObject loggingBar property QtObject commonChart property QtObject commonLegend + property QtObject advancedIns property QtObject solutionPosition property QtObject solutionTable property QtObject solutionVelocity @@ -88,6 +89,28 @@ QtObject { readonly property string folderButtonPath: "images/fontawesome/folder-solid.svg" } + advancedIns: QtObject { + readonly property string title: "Raw IMU Data" + readonly property color titleColor: "#00006E" + readonly property int titlePointSize: 14 + readonly property var textDataLabels: ["Imu temp:", "Imu conf:", "Rms acc x:", "Rms acc y:", "Rms acc z:"] + readonly property var legendLabels: ["Accn. X", "Accn. Y", "Accn. Z", "Gyro X", "Gyro Y", "Gyro Z"] + readonly property var lineColors: ["#8c510a", "#d8b365", "#f6e8c3", "#c7eae5", "#5ab4ac", "#01665e"] + readonly property int legendBottomMargin: 120 + readonly property int legendLeftMargin: 80 + readonly property int yAxisTickCount: 10000 + readonly property int xAxisTickCount: 25 + readonly property int xAxisMax: 200 + readonly property int xAxisMin: 0 + readonly property int yAxisMax: 32768 + readonly property int yAxisMin: -32768 + readonly property int textDataLabelWidth: 50 + readonly property int textDataRowHeight: 25 + readonly property int textDataBarHeight: 20 + readonly property int textDataBarMargin: 2 + readonly property int textDataBarBorderWidth: 1 + } + solutionPosition: QtObject { readonly property int navBarMargin: 10 readonly property int navBarSpacing: 0 @@ -172,6 +195,8 @@ QtObject { readonly property int xAxisMinOffsetFromMaxSeconds: 100 readonly property int checkBoxVerticalPadding: 0 readonly property int checkBoxPreferredWidth: 100 + readonly property int snrThreshold: 15 + readonly property int yAxisMax: 60 } observationTab: QtObject { diff --git a/resources/ObservationTab.qml b/resources/ObservationTab.qml index 4f7e779dd..7b39a914c 100644 --- a/resources/ObservationTab.qml +++ b/resources/ObservationTab.qml @@ -80,6 +80,7 @@ Item { localTable.tow = observationData.tow; localTable.week = observationData.week; localTable.model.rows = observationData.rows; + observationView.visible = true; } } } diff --git a/resources/SolutionTab.qml b/resources/SolutionTab.qml index 6e804efad..629ffad43 100644 --- a/resources/SolutionTab.qml +++ b/resources/SolutionTab.qml @@ -39,7 +39,7 @@ Item { TabBar { id: solutionBar - currentIndex: Globals.initialSubTabIndex + currentIndex: Globals.initialMainTabIndex == 1 ? Globals.initialSubTabIndex : 0 z: Constants.commonChart.zAboveCharts contentHeight: Constants.tabBarHeight diff --git a/resources/TrackingTab.qml b/resources/TrackingTab.qml index 108f2e1fb..6dec7ab92 100644 --- a/resources/TrackingTab.qml +++ b/resources/TrackingTab.qml @@ -15,7 +15,7 @@ Item { id: trackingBar z: Constants.commonChart.zAboveCharts - currentIndex: Globals.initialSubTabIndex + currentIndex: Globals.initialMainTabIndex == 0 ? Globals.initialSubTabIndex : 0 contentHeight: Constants.tabBarHeight Repeater { diff --git a/resources/TrackingTabComponents/TrackingSignalsTab.qml b/resources/TrackingTabComponents/TrackingSignalsTab.qml index ce1c5feda..39dc452f5 100644 --- a/resources/TrackingTabComponents/TrackingSignalsTab.qml +++ b/resources/TrackingTabComponents/TrackingSignalsTab.qml @@ -134,6 +134,8 @@ Item { minorGridLineColor: Constants.commonChart.minorGridLineColor gridLineColor: Constants.commonChart.gridLineColor labelsColor: Constants.commonChart.labelsColor + max: Constants.trackingSignals.yAxisMax + min: Constants.trackingSignals.snrThreshold labelsFont { pointSize: Constants.mediumPointSize @@ -183,12 +185,8 @@ Item { } trackingSignalsPoints.fill_series(lines); var last = points[0][points[0].length - 1]; - trackingSignalsXAxis.min = last.x - Constants.trackingSignals.xAxisMinOffsetFromMaxSeconds; + trackingSignalsXAxis.min = last.x + trackingSignalsPoints.xmin_offset; trackingSignalsXAxis.max = last.x; - if (trackingSignalsYAxis.min != trackingSignalsPoints.min_) { - trackingSignalsYAxis.min = trackingSignalsPoints.min_; - trackingSignalsYAxis.max = trackingSignalsPoints.max_; - } } } diff --git a/resources/console_resources.qrc b/resources/console_resources.qrc index 6e3c86b20..2dfeb7e7d 100644 --- a/resources/console_resources.qrc +++ b/resources/console_resources.qrc @@ -10,6 +10,8 @@ Constants/Globals.qml Constants/qmldir Constants/utils.js + AdvancedTab.qml + AdvancedTabComponents/AdvancedInsTab.qml SolutionTab.qml SolutionTabComponents/SolutionPositionTab.qml SolutionTabComponents/SolutionTable.qml diff --git a/resources/view.qml b/resources/view.qml index 5b51aa116..87ae754bb 100644 --- a/resources/view.qml +++ b/resources/view.qml @@ -73,11 +73,14 @@ ApplicationWindow { } Item { - id: updateTab + id: settingsTab } Item { - id: advancedTab + id: updateTab + } + + AdvancedTab { } } diff --git a/src/main/python/advanced_ins_tab.py b/src/main/python/advanced_ins_tab.py new file mode 100644 index 000000000..c50d8ce71 --- /dev/null +++ b/src/main/python/advanced_ins_tab.py @@ -0,0 +1,50 @@ +"""Advanced Ins Tab QObjects. +""" + +from typing import Dict, List, Any + +from PySide2.QtCore import Property, QObject, QPointF, Slot + +from constants import Keys, QTKeys + +ADVANCED_INS_TAB: Dict[str, Any] = { + Keys.FIELDS_DATA: [], + Keys.POINTS: [], +} + + +class AdvancedInsPoints(QObject): + + _points: List[List[QPointF]] = [[]] + _fields_data: List[float] = [] + + def get_fields_data(self) -> List[float]: + """Getter for _fields_data.""" + return self._fields_data + + def set_fields_data(self, fields_data: List[float]) -> None: + """Setter for _fields_data.""" + self._fields_data = fields_data + + fields_data = Property(QTKeys.QVARIANTLIST, get_fields_data, set_fields_data) # type: ignore + + def get_points(self) -> List[List[QPointF]]: + return self._points + + def set_points(self, points) -> None: + self._points = points + + points = Property(QTKeys.QVARIANTLIST, get_points, set_points) # type: ignore + + @Slot(list) # type: ignore + def fill_series(self, series_list): + for idx, series in enumerate(series_list): + series.replace(self._points[idx]) + + +class AdvancedInsModel(QObject): # pylint: disable=too-few-public-methods + @Slot(AdvancedInsPoints) # type: ignore + def fill_console_points(self, cp: AdvancedInsPoints) -> AdvancedInsPoints: # pylint:disable=no-self-use + cp.set_points(ADVANCED_INS_TAB[Keys.POINTS]) + cp.set_fields_data(ADVANCED_INS_TAB[Keys.FIELDS_DATA]) + return cp diff --git a/src/main/python/constants.py b/src/main/python/constants.py index 6073d1cd1..077f2f920 100644 --- a/src/main/python/constants.py +++ b/src/main/python/constants.py @@ -10,7 +10,11 @@ class Tabs(str, Enum): OBSERVATIONS = "OBSERVATIONS" SETTINGS = "SETTINGS" UPDATE = "UPDATE" - ADVANCED = "ADVANCED" + ADVANCED_SYSTEM_MONITOR = "ADVANCED_SYSTEM_MONITOR" + ADVANCED_INS = "ADVANCED_INS" + ADVANCED_MAGNETOMETER = "ADVANCED_MAGNETOMETER" + ADVANCED_NETWORKING = "ADVANCED_NETWORKING" + ADVANCED_SPECTRUM_ANALYZER = "ADVANCED_SPECTRUM_ANALYZER" class SbpLogging(str, Enum): @@ -71,6 +75,8 @@ class Keys(str, Enum): CSV_LOGGING = "CSV_LOGGING" SBP_LOGGING_LABELS = "SBP_LOGGING_LABELS" LOG_LEVEL_LABELS = "LOG_LEVEL_LABELS" + FIELDS_DATA = "FIELDS_DATA" + XMIN_OFFSET = "XMIN" class ApplicationStates(str, Enum): diff --git a/src/main/python/main.py b/src/main/python/main.py index 09219f60b..69a6cee53 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -41,6 +41,11 @@ LoggingBarModel, ) +from advanced_ins_tab import ( + AdvancedInsModel, + AdvancedInsPoints, + ADVANCED_INS_TAB, +) from observation_tab import ( ObservationData, @@ -122,10 +127,26 @@ MAIN_INDEX: 5, SUB_INDEX: 0, }, - Tabs.ADVANCED: { + Tabs.ADVANCED_SYSTEM_MONITOR: { MAIN_INDEX: 6, SUB_INDEX: 0, }, + Tabs.ADVANCED_INS: { + MAIN_INDEX: 6, + SUB_INDEX: 1, + }, + Tabs.ADVANCED_MAGNETOMETER: { + MAIN_INDEX: 6, + SUB_INDEX: 2, + }, + Tabs.ADVANCED_NETWORKING: { + MAIN_INDEX: 6, + SUB_INDEX: 3, + }, + Tabs.ADVANCED_SPECTRUM_ANALYZER: { + MAIN_INDEX: 6, + SUB_INDEX: 4, + }, } @@ -172,6 +193,12 @@ def receive_messages(app_, backend, messages): SOLUTION_VELOCITY_TAB[Keys.MAX] = m.solutionVelocityStatus.max SOLUTION_VELOCITY_TAB[Keys.MIN] = m.solutionVelocityStatus.min SOLUTION_VELOCITY_TAB[Keys.AVAILABLE_UNITS][:] = m.solutionVelocityStatus.availableUnits + elif m.which == Message.Union.AdvancedInsStatus: + ADVANCED_INS_TAB[Keys.FIELDS_DATA][:] = m.advancedInsStatus.fieldsData + ADVANCED_INS_TAB[Keys.POINTS][:] = [ + [QPointF(point.x, point.y) for point in m.advancedInsStatus.data[idx]] + for idx in range(len(m.advancedInsStatus.data)) + ] elif m.which == Message.Union.TrackingSignalsStatus: TRACKING_SIGNALS_TAB[Keys.CHECK_LABELS][:] = m.trackingSignalsStatus.checkLabels TRACKING_SIGNALS_TAB[Keys.LABELS][:] = m.trackingSignalsStatus.labels @@ -180,8 +207,7 @@ def receive_messages(app_, backend, messages): [QPointF(point.x, point.y) for point in m.trackingSignalsStatus.data[idx]] for idx in range(len(m.trackingSignalsStatus.data)) ] - TRACKING_SIGNALS_TAB[Keys.MAX] = m.trackingSignalsStatus.max - TRACKING_SIGNALS_TAB[Keys.MIN] = m.trackingSignalsStatus.min + TRACKING_SIGNALS_TAB[Keys.XMIN_OFFSET] = m.trackingSignalsStatus.xminOffset elif m.which == Message.Union.ObservationStatus: if m.observationStatus.isRemote: REMOTE_OBSERVATION_TAB[Keys.TOW] = m.observationStatus.tow @@ -424,6 +450,7 @@ def handle_cli_arguments(args: argparse.Namespace, globals_: QObject): qmlRegisterType(LogPanelData, "SwiftConsole", 1, 0, "LogPanelData") # type: ignore qmlRegisterType(NavBarData, "SwiftConsole", 1, 0, "NavBarData") # type: ignore qmlRegisterType(LoggingBarData, "SwiftConsole", 1, 0, "LoggingBarData") # type: ignore + qmlRegisterType(AdvancedInsPoints, "SwiftConsole", 1, 0, "AdvancedInsPoints") # type: ignore qmlRegisterType(SolutionPositionPoints, "SwiftConsole", 1, 0, "SolutionPositionPoints") # type: ignore qmlRegisterType(SolutionTableEntries, "SwiftConsole", 1, 0, "SolutionTableEntries") # type: ignore qmlRegisterType(SolutionVelocityPoints, "SwiftConsole", 1, 0, "SolutionVelocityPoints") # type: ignore @@ -447,6 +474,7 @@ def handle_cli_arguments(args: argparse.Namespace, globals_: QObject): data_model = DataModel(endpoint_main, messages_main) log_panel_model = LogPanelModel() nav_bar_model = NavBarModel() + advanced_ins_model = AdvancedInsModel() solution_position_model = SolutionPositionModel() solution_table_model = SolutionTableModel() solution_velocity_model = SolutionVelocityModel() @@ -458,6 +486,7 @@ def handle_cli_arguments(args: argparse.Namespace, globals_: QObject): root_context = engine.rootContext() root_context.setContextProperty("log_panel_model", log_panel_model) root_context.setContextProperty("nav_bar_model", nav_bar_model) + root_context.setContextProperty("advanced_ins_model", advanced_ins_model) root_context.setContextProperty("solution_position_model", solution_position_model) root_context.setContextProperty("solution_table_model", solution_table_model) root_context.setContextProperty("solution_velocity_model", solution_velocity_model) diff --git a/src/main/python/tracking_signals_tab.py b/src/main/python/tracking_signals_tab.py index 2f935b217..7f8db6e86 100644 --- a/src/main/python/tracking_signals_tab.py +++ b/src/main/python/tracking_signals_tab.py @@ -13,8 +13,7 @@ Keys.CHECK_LABELS: [], Keys.LABELS: [], Keys.COLORS: [], - Keys.MAX: 0, - Keys.MIN: 0, + Keys.XMIN_OFFSET: 0, } @@ -24,43 +23,17 @@ class TrackingSignalsPoints(QObject): _check_labels: List[str] = [] _labels: List[str] = [] _points: List[List[QPointF]] = [[]] - _valid: bool = False - _min: float = 0.0 - _max: float = 0.0 + _xmin_offset: float = 0.0 - def get_valid(self) -> bool: - """Getter for _valid. + def get_xmin_offset(self) -> float: + """Getter for _xmin_offset.""" + return self._xmin_offset - Returns: - bool: Whether it is valid or not. - """ - return self._valid + def set_xmin_offset(self, xmin_offset_: float) -> None: + """Setter for _xmin_offset.""" + self._xmin_offset = xmin_offset_ - def set_valid(self, valid: bool) -> None: - """Setter for _valid.""" - self._valid = valid - - valid = Property(bool, get_valid, set_valid) - - def get_min(self) -> float: - """Getter for _min.""" - return self._min - - def set_min(self, min_: float) -> None: - """Setter for _min.""" - self._min = min_ - - min_ = Property(float, get_min, set_min) - - def get_max(self) -> float: - """Getter for _max.""" - return self._max - - def set_max(self, max_: float) -> None: - """Setter for _max.""" - self._max = max_ - - max_ = Property(float, get_max, set_max) + xmin_offset = Property(float, get_xmin_offset, set_xmin_offset) def get_check_labels(self) -> List[str]: return self._check_labels @@ -109,6 +82,5 @@ def fill_console_points(self, cp: TrackingSignalsPoints) -> TrackingSignalsPoint cp.set_labels(TRACKING_SIGNALS_TAB[Keys.LABELS]) cp.set_check_labels(TRACKING_SIGNALS_TAB[Keys.CHECK_LABELS]) cp.set_colors(TRACKING_SIGNALS_TAB[Keys.COLORS]) - cp.set_max(TRACKING_SIGNALS_TAB[Keys.MAX]) - cp.set_min(TRACKING_SIGNALS_TAB[Keys.MIN]) + cp.set_xmin_offset(TRACKING_SIGNALS_TAB[Keys.XMIN_OFFSET]) return cp diff --git a/src/main/resources/base/console_backend.capnp b/src/main/resources/base/console_backend.capnp index b1798eaa7..08f951579 100644 --- a/src/main/resources/base/console_backend.capnp +++ b/src/main/resources/base/console_backend.capnp @@ -127,12 +127,16 @@ struct SolutionVelocityStatus { } struct TrackingSignalsStatus { - min @0 :Float64; - max @1 :Float64; - labels @2 :List(Text); - data @3 :List(List(Point)); - colors @4 :List(Text); - checkLabels @5 :List(Text); + xminOffset @0 :Float64; + labels @1 :List(Text); + data @2 :List(List(Point)); + colors @3 :List(Text); + checkLabels @4 :List(Text); +} + +struct AdvancedInsStatus { + data @0 :List(List(Point)); + fieldsData @1 :List(Float64); } struct LoggingBarFront { @@ -194,5 +198,6 @@ struct Message { loggingBarFront @20 :LoggingBarFront; loggingBarStatus @21 :LoggingBarStatus; logLevelFront @22 :LogLevelFront; + advancedInsStatus @23 :AdvancedInsStatus; } }