Skip to content

Commit

Permalink
introduce DORIS rinex handling in cli
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume W. Bres <[email protected]>
  • Loading branch information
gwbres committed May 13, 2024
1 parent 1fd99aa commit 3e9ec83
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 61 deletions.
13 changes: 1 addition & 12 deletions rinex-cli/src/cli/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ Example (2): render meteo sensor observations similary.
Example (3): render DORIS observations similarly.
./target/release/rinex-cli \\
-f test_resources/MET/V3/POTS00DEU_R_20232540000_01D_05M_MM.rnx.gz \\
-g --obs --csv
-f test_resources/OR/V3/cs2rx18164.gz -g --obs --csv
Example (4): render OBS + Meteo combination at once.
RINEX-Cli allows loading OBS and Meteo in one session.
Expand Down Expand Up @@ -186,14 +185,4 @@ rinex-cli -f test_resources/IONEX/V1/CKMG0080.09I.gz -g --tec")
.action(ArgAction::SetTrue)
.help("Plot ionospheric delay per signal & SV, at latitude and longitude of signal sampling."),
)
.next_help_heading("DORIS (requires at least one DORIS file)").
arg(
Arg::new("acorr")
.short('a')
.long("acorr")
.action(ArgAction::SetTrue)
.help("Compute and render the autocorrelation of (precise) Pseudo Range and Dopplers from the DORIS measurement,
from all contained stations. See --help")
.long_help("TODO")
)
}
9 changes: 5 additions & 4 deletions rinex-cli/src/fops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ pub fn filegen(ctx: &Context, matches: &ArgMatches) -> Result<(), Error> {

for product in [
ProductType::Observation,
ProductType::DORIS,
ProductType::MeteoObservation,
ProductType::BroadcastNavigation,
ProductType::HighPrecisionClock,
ProductType::Ionex,
ProductType::Antex,
ProductType::IONEX,
ProductType::ANTEX,
] {
if let Some(rinex) = ctx_data.rinex(product) {
let prod = custom_prod_attributes(rinex, matches);
Expand Down Expand Up @@ -180,7 +181,7 @@ pub fn split(ctx: &Context, matches: &ArgMatches) -> Result<(), Error> {
ProductType::MeteoObservation,
ProductType::BroadcastNavigation,
ProductType::HighPrecisionClock,
ProductType::Ionex,
ProductType::IONEX,
] {
if let Some(rinex) = ctx_data.rinex(product) {
let (rinex_a, rinex_b) = rinex
Expand Down Expand Up @@ -295,7 +296,7 @@ pub fn time_binning(ctx: &Context, matches: &ArgMatches) -> Result<(), Error> {
ProductType::MeteoObservation,
ProductType::BroadcastNavigation,
ProductType::HighPrecisionClock,
ProductType::Ionex,
ProductType::IONEX,
] {
// input data determination
if let Some(rinex) = ctx_data.rinex(product) {
Expand Down
3 changes: 3 additions & 0 deletions rinex-cli/src/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,9 @@ pub fn graph_opmode(ctx: &Context, matches: &ArgMatches) -> Result<(), Error> {
if ctx.data.has_meteo() {
record::plot_meteo_observations(ctx, &mut plot_ctx, csv_export);
}
if ctx.data.has_doris() {
record::plot_doris_observations(ctx, &mut plot_ctx, csv_export);
}

/* save observations */
ctx.render_html("OBSERVATIONS.html", plot_ctx.to_html());
Expand Down
296 changes: 296 additions & 0 deletions rinex-cli/src/graph/record/doris.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
use crate::cli::Context;
use itertools::Itertools;
use plotly::common::{Marker, MarkerSymbol, Mode, Visible};
use rinex::{carrier::Carrier, prelude::*};

use crate::graph::{build_chart_epoch_axis, PlotContext};

fn plot_title(observable: Observable) -> (String, String) {
if observable.is_pseudorange_observable() {
("Pseudo Range".to_string(), "[m]".to_string())
} else if observable.is_phase_observable() {
("Phase".to_string(), "[m]".to_string())
} else if observable.is_power_observable() {
("Signal Power".to_string(), "Power [dBm]".to_string())
} else if observable == Observable::Temperature {
(
"Temperature (at base station)".to_string(),
"T [°C]".to_string(),
)
} else if observable == Observable::Pressure {
(
"Pressure (at base station)".to_string(),
"P [hPa]".to_string(),
)
} else if observable == Observable::HumidityRate {
(
"Humidity (at base station)".to_string(),
"Saturation Rate [%]".to_string(),
)
} else if observable == Observable::FrequencyRatio {
(
"RX Clock Offset (f(t)-f0)/f0".to_string(),
"n.a".to_string(),
)
} else {
unreachable!("unexpected DORIS observable {}", observable);
}
}

/*
* Plots given DORIS RINEX content
*/
pub fn plot_doris_observations(ctx: &Context, plot_ctx: &mut PlotContext, _csv_export: bool) {
let doris = ctx.data.doris().unwrap(); // infaillible

// Per observable
for observable in doris.observable().sorted() {
// Trick to present S1/U2 on the same plot
let marker_symbol = if observable.is_phase_observable()
|| observable.is_pseudorange_observable()
|| observable.is_power_observable()
{
let obs_str = observable.to_string();
if obs_str.contains("1") {
let (plot_title, y_title) = plot_title(observable.clone());
plot_ctx.add_timedomain_plot(&plot_title, &y_title);
MarkerSymbol::Circle
} else {
MarkerSymbol::Diamond
}
} else {
let (plot_title, y_title) = plot_title(observable.clone());
plot_ctx.add_timedomain_plot(&plot_title, &y_title);
MarkerSymbol::Circle
};

// Per station
for (station_index, station) in doris.stations().sorted().enumerate() {
if *observable == Observable::Temperature {
let x = doris
.doris_temperature()
.filter_map(|(t_i, station_i, _data)| {
if station_i == station {
Some(t_i)
} else {
None
}
})
.collect::<Vec<_>>();
let y = doris
.doris_temperature()
.filter_map(|(_t_i, station_i, value)| {
if station_i == station {
Some(value)
} else {
None
}
})
.collect::<Vec<_>>();
let trace = build_chart_epoch_axis(&station.label, Mode::Markers, x, y)
.marker(Marker::new().symbol(marker_symbol.clone()))
.visible({
if station_index < 3 {
Visible::True
} else {
Visible::LegendOnly
}
});
plot_ctx.add_trace(trace);
} else if *observable == Observable::Pressure {
let x = doris
.doris_pressure()
.filter_map(|(t_i, station_i, _data)| {
if station_i == station {
Some(t_i)
} else {
None
}
})
.collect::<Vec<_>>();
let y = doris
.doris_pressure()
.filter_map(|(_t_i, station_i, value)| {
if station_i == station {
Some(value)
} else {
None
}
})
.collect::<Vec<_>>();
let trace = build_chart_epoch_axis(&station.label, Mode::Markers, x, y)
.marker(Marker::new().symbol(marker_symbol.clone()))
.visible({
if station_index < 3 {
Visible::True
} else {
Visible::LegendOnly
}
});
plot_ctx.add_trace(trace);
} else if *observable == Observable::HumidityRate {
let x = doris
.doris_humidity()
.filter_map(|(t_i, station_i, _data)| {
if station_i == station {
Some(t_i)
} else {
None
}
})
.collect::<Vec<_>>();
let y = doris
.doris_humidity()
.filter_map(|(_t_i, station_i, value)| {
if station_i == station {
Some(value)
} else {
None
}
})
.collect::<Vec<_>>();
let trace = build_chart_epoch_axis(&station.label, Mode::Markers, x, y)
.marker(Marker::new().symbol(marker_symbol.clone()))
.visible({
if station_index < 3 {
Visible::True
} else {
Visible::LegendOnly
}
});
plot_ctx.add_trace(trace);
// TODO
// } else if *observable == Observable::FrequencyRatio {
} else if observable.is_power_observable() {
let x = doris
.doris_rx_power()
.filter_map(|(t_i, station_i, observable_i, _data)| {
if station_i == station && observable_i == observable {
Some(t_i)
} else {
None
}
})
.collect::<Vec<_>>();
let y = doris
.doris_rx_power()
.filter_map(|(_t_i, station_i, observable_i, data)| {
if station_i == station && observable_i == observable {
Some(data)
} else {
None
}
})
.collect::<Vec<_>>();

let freq_label = Carrier::from_doris_observable(&observable)
.unwrap_or_else(|_| {
panic!("failed to determine plot freq_label for {}", observable)
})
.to_string();

let trace = build_chart_epoch_axis(
&format!("{}({})", station.label, freq_label),
Mode::Markers,
x,
y,
)
.marker(Marker::new().symbol(marker_symbol.clone()))
.visible({
if station_index < 3 {
Visible::True
} else {
Visible::LegendOnly
}
});
plot_ctx.add_trace(trace);
} else if observable.is_pseudorange_observable() {
let x = doris
.doris_pseudo_range()
.filter_map(|(t_i, station_i, observable_i, _data)| {
if station_i == station && observable_i == observable {
Some(t_i)
} else {
None
}
})
.collect::<Vec<_>>();
let y = doris
.doris_pseudo_range()
.filter_map(|(_t_i, station_i, observable_i, data)| {
if station_i == station && observable_i == observable {
Some(data)
} else {
None
}
})
.collect::<Vec<_>>();

let freq_label = Carrier::from_doris_observable(&observable)
.unwrap_or_else(|_| {
panic!("failed to determine plot freq_label for {}", observable)
})
.to_string();

let trace = build_chart_epoch_axis(
&format!("{}({})", station.label, freq_label),
Mode::Markers,
x,
y,
)
.marker(Marker::new().symbol(marker_symbol.clone()))
.visible({
if station_index < 3 {
Visible::True
} else {
Visible::LegendOnly
}
});
plot_ctx.add_trace(trace);
} else if observable.is_phase_observable() {
let x = doris
.doris_phase()
.filter_map(|(t_i, station_i, observable_i, _data)| {
if station_i == station && observable_i == observable {
Some(t_i)
} else {
None
}
})
.collect::<Vec<_>>();
let y = doris
.doris_phase()
.filter_map(|(_t_i, station_i, observable_i, data)| {
if station_i == station && observable_i == observable {
Some(data)
} else {
None
}
})
.collect::<Vec<_>>();

let freq_label = Carrier::from_doris_observable(&observable)
.unwrap_or_else(|_| {
panic!("failed to determine plot freq_label for {}", observable)
})
.to_string();

let trace = build_chart_epoch_axis(
&format!("{}({})", station.label, freq_label),
Mode::Markers,
x,
y,
)
.marker(Marker::new().symbol(marker_symbol.clone()))
.visible({
if station_index < 3 {
Visible::True
} else {
Visible::LegendOnly
}
});
plot_ctx.add_trace(trace);
}
}
}
}
2 changes: 2 additions & 0 deletions rinex-cli/src/graph/record/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod doris;
mod ionex;
mod ionosphere;
mod meteo;
mod navigation;
mod observation;
mod sp3_plot;

pub use doris::plot_doris_observations;
pub use meteo::plot_meteo_observations;
pub use navigation::plot_sv_nav_clock;
pub use navigation::plot_sv_nav_orbits;
Expand Down
Loading

0 comments on commit 3e9ec83

Please sign in to comment.