Add titles in some Python plots

This commit is contained in:
Carles Fernandez
2026-06-26 08:59:15 +02:00
parent 95ad7d19ec
commit 60a1c43f2b
5 changed files with 375 additions and 153 deletions
+104 -99
View File
@@ -45,11 +45,11 @@ repeat them with the default value that applies to each tool.
Any directory in `--file-prefix` is resolved relative to `--input-path` (an
absolute `--file-prefix` path overrides it).
When `--conf` is provided to the generic tracking and acquisition plotters,
a multi-signal configuration expands to every enabled signal. Pass
When `--conf` is provided to the generic tracking and acquisition plotters, a
multi-signal configuration expands to every enabled signal. Pass
`--signal-type` to restrict plots to one signal. Single-file modes and
signal-specific scripts still require or prefer one signal. Channel ranges
are computed using GNSS-SDR's receiver order:
signal-specific scripts still require or prefer one signal. Channel ranges are
computed using GNSS-SDR's receiver order:
`1C, 2S, L5, 1B, 5X, E6, 1G, 2G, B1, B3, 7X, J1, J5`.
- `-o`, `--fig-path`: directory where plots are saved. Defaults to a per-script
@@ -92,25 +92,30 @@ Output PNGs (`_2D` and `_3D`):
<fig-path>/<output-basename>_<system>_<signal>_ch_<channel>_<execution>_sat_<PRN>_3D.png
```
| Option | Default | Description |
| ---------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the acquisition `.mat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer acquisition dump prefixes and signal selectors. |
Each figure title uses a readable signal name and PRN, for example
`GPS L1 C/A PRN 3 (channel 0, execution 1)`.
| Option | Default | Description |
| ---------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the acquisition `.mat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer acquisition dump prefixes and signal selectors. |
| `--file-prefix` | `--conf` or `acquisition` | `Acquisition.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/acquisition` | Output directory for PNGs. |
| `--output-basename`, `--output-prefix` | `--file-prefix` basename | Base name for the saved PNGs (directory and extension stripped). |
| `--all-files` / `--single-file` | `--all-files` | Plot every matching dump, or a single dump chosen by the selectors below. |
| `--positive-only` / `--include-negative` | `--positive-only` | Plot only positive acquisitions, or positive and negative. |
| `--lite-view` / `--full-view` | `--lite-view` | Interpolated light grid, or the raw acquisition grid. |
| `--sat` | `1` in single-file mode | Satellite PRN. Filters in all-files mode; selects the dump in single-file mode. |
| `--channel` | `0` in single-file mode | Acquisition channel number. Filter / selector. |
| `--execution` | `1` in single-file mode | Acquisition dump execution index. Filter / selector. |
| `--signal-type CODE` | `--conf` or `1C` | GNSS-SDR signal code (see table below); case-insensitive. Filters in all-files mode; selects in single-file mode. |
| `--samples-per-chip` | `3` | Samples per chip used by the light-grid interpolation. |
| `--samples-per-code` | `25000` | Samples per code used for the 2D normalization. |
| `--input-power` | `100.0` | Input power used for the 2D normalization. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| `-o`, `--fig-path` | `plots/acquisition` | Output directory for PNGs. |
| `--output-basename`, `--output-prefix` | `--file-prefix` basename | Base name for the saved PNGs (directory and extension stripped). |
| `--all-files` / `--single-file` | `--all-files` | Plot every matching dump, or a single dump chosen by the selectors below. |
| `--positive-only` / `--include-negative` | `--positive-only` | Plot only positive acquisitions, or positive and negative. |
| `--lite-view` / `--full-view` | `--lite-view` | Interpolated light grid, or the raw acquisition grid. |
| `--sat` | `1` in single-file mode | Satellite PRN. Filters in all-files mode; selects the dump in single-file mode. |
| `--channel` | `0` in single-file mode | Acquisition channel number. Filter / selector. |
| `--execution` | `1` in single-file mode | Acquisition dump execution index. Filter / selector. |
| `--signal-type CODE` | `--conf` or `1C` | GNSS-SDR signal code (see table below); case-insensitive. Filters in all-files mode; selects in single-file mode. |
| `--samples-per-chip` | `3` | Samples per chip used by the light-grid interpolation. |
| `--samples-per-doppler-step` | `5` | Interpolated samples per Doppler step used by the light-grid interpolation. |
| `--surface-smoothing` | `2.0` | Gaussian smoothing sigma for the lite 3D surface; use `0` to disable. |
| `--samples-per-code` | `25000` | Samples per code used for the 2D normalization. |
| `--input-power` | `100.0` | Input power used for the 2D normalization. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
Signal codes accepted by `--signal-type`, using the same nomenclature as the
GNSS-SDR configuration and dump filenames (e.g. `--signal-type=E6`):
@@ -145,7 +150,7 @@ python3 plot_acq_grid.py --input-path ./out --fig-path ./plots/acquisition
python3 plot_acq_grid.py --file-prefix hackrf_l1_acq_dump --channel 0
python3 plot_acq_grid.py --file-prefix hackrf_l1_acq_dump --sat 26
python3 plot_acq_grid.py --signal-type E6
python3 plot_acq_grid.py --conf conf/File_input/Beidou/gnss-sdr_BDS_B3I_byte.conf --signal-type B3 --input-path ./out
python3 plot_acq_grid.py --conf gnss-sdr_BDS_B3I_byte.conf
python3 plot_acq_grid.py --single-file --signal-type J1 --sat 193 --channel 0 --execution 1
python3 plot_acq_grid.py --single-file --sat 3 --channel 0 --execution 1 --full-view
python3 plot_acq_grid.py --include-negative --output-basename my_run
@@ -158,20 +163,20 @@ Reads per-channel tracking `.dat` dumps and plots VEML tracking diagnostics
Input pattern: `<input-path>/<file-prefix><channel>.dat`.
| Option | Default | Description |
| ---------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the tracking `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer tracking dump prefix, sampling frequency, and channel range. |
| `--file-prefix` | `--conf` or `track_ch` | `Tracking.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/dll-pll-veml-tracking` | Output directory for PNGs. |
| `--sampling-frequency` | `--conf` or `3000000.0` | Signal sampling frequency in Hz (sets the RX-time axis). |
| `--channels` | `--conf` or `5` | Number of channels to read. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| Option | Default | Description |
| ---------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the tracking `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer tracking dump prefix, sampling frequency, and channel range. |
| `--file-prefix` | `--conf` or `track_ch` | `Tracking.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/dll-pll-veml-tracking` | Output directory for PNGs. |
| `--sampling-frequency` | `--conf` or `3000000.0` | Signal sampling frequency in Hz (sets the RX-time axis). |
| `--channels` | `--conf` or `5` | Number of channels to read. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type CODE` | inferred if needed | GNSS-SDR signal code used to restrict channel ranges and dump filenames from `--conf`; omitted means all configured signals. |
| `--plot-last-outputs` | `0` | Only plot the last N outputs; `0` plots all of them. |
| `--no-doppler` | Doppler plots on | Skip the extra Doppler-only plots. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| `--plot-last-outputs` | `0` | Only plot the last N outputs; `0` plots all of them. |
| `--no-doppler` | Doppler plots on | Skip the extra Doppler-only plots. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
Examples:
@@ -189,19 +194,19 @@ Reads per-channel Kalman-filter tracking `.dat` dumps produced by the
Input pattern: `<input-path>/<file-prefix><channel>.dat`.
| Option | Default | Description |
| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the tracking `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer tracking dump prefix, sampling frequency, and channel range. |
| `--file-prefix` | `--conf` or `track_ch` | `Tracking.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/kf-tracking` | Output directory for PNGs. |
| `--sampling-frequency` | `--conf` or `4000000.0` | Signal sampling frequency in Hz. |
| `--channels` | `--conf` or `5` | Number of channels to read. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type CODE` | inferred if needed | GNSS-SDR signal code used to select channel ranges and dump filenames from `--conf`. |
| `--code-period` | `0.001` | Code period in seconds. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| Option | Default | Description |
| ---------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the tracking `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer tracking dump prefix, sampling frequency, and channel range. |
| `--file-prefix` | `--conf` or `track_ch` | `Tracking.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/kf-tracking` | Output directory for PNGs. |
| `--sampling-frequency` | `--conf` or `4000000.0` | Signal sampling frequency in Hz. |
| `--channels` | `--conf` or `5` | Number of channels to read. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type CODE` | inferred if needed | GNSS-SDR signal code used to select channel ranges and dump filenames from `--conf`. |
| `--code-period` | `0.001` | Code period in seconds. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
> The dumps must come from the `GPS_L1_CA_KF_Tracking` block; standard DLL/PLL
> tracking dumps have a different record layout and will be misread.
@@ -222,22 +227,22 @@ figures.
Input pattern: `<input-path>/<file-prefix><channel>.dat`.
| Option | Default | Description |
| -------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the tracking `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer tracking dump prefix, sampling frequency, signal type, and channel range. |
| `--file-prefix` | `--conf` or `track_ch` | `Tracking.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/tracking-quality` | Output directory for PNGs. |
| `--sampling-frequency` | `--conf` or `4000000.0` | Signal sampling frequency in Hz. |
| `--channels` | `--conf` or `5` | Number of channels to read. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type` | `--conf` or `1C` | GNSS-SDR signal code used to restrict channel ranges and label traces; omitted with multi-signal `--conf` means all configured signals. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| Option | Default | Description |
| ---------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the tracking `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer tracking dump prefix, sampling frequency, signal type, and channel range. |
| `--file-prefix` | `--conf` or `track_ch` | `Tracking.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/tracking-quality` | Output directory for PNGs. |
| `--sampling-frequency` | `--conf` or `4000000.0` | Signal sampling frequency in Hz. |
| `--channels` | `--conf` or `5` | Number of channels to read. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type` | `--conf` or `1C` | GNSS-SDR signal code used to restrict channel ranges and label traces; omitted with multi-signal `--conf` means all configured signals. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
The C/N0 legend labels each trace as `<signal-type> PRN <PRN>` (e.g.
`1C PRN 9`); the carrier-lock legend labels traces as `SV <signal-type> PRN
<PRN>`.
`1C PRN 9`); the carrier-lock legend labels traces as
`SV <signal-type> PRN <PRN>`.
Examples:
@@ -255,19 +260,19 @@ channels.
Input pattern: `<input-path>/<file-prefix><channel>.dat`.
| Option | Default | Description |
| -------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the telemetry `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer telemetry dump prefix and channel range. |
| `--file-prefix` | `--conf` or `telemetry` | `TelemetryDecoder.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/telemetry` | Output directory for PNGs. |
| `--channels` | `--conf` or `18` | Number of channels to try reading; missing channels are skipped. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type CODE` | inferred if needed | GNSS-SDR signal code used to select channel ranges and dump filenames from `--conf`. |
| `--channel-a` | `--conf` or `0` | First channel number to plot. |
| `--channel-b` | `--conf` or `5` | Second channel number to plot. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| Option | Default | Description |
| -------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the telemetry `.dat` dumps. |
| `--conf` | unset | GNSS-SDR configuration used to infer telemetry dump prefix and channel range. |
| `--file-prefix` | `--conf` or `telemetry` | `TelemetryDecoder.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/telemetry` | Output directory for PNGs. |
| `--channels` | `--conf` or `18` | Number of channels to try reading; missing channels are skipped. |
| `--first-channel` | `--conf` or `0` | First channel number in the dump filenames. |
| `--signal-type CODE` | inferred if needed | GNSS-SDR signal code used to select channel ranges and dump filenames from `--conf`. |
| `--channel-a` | `--conf` or `0` | First channel number to plot. |
| `--channel-b` | `--conf` or `5` | Second channel number to plot. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
Examples:
@@ -284,15 +289,15 @@ phase, Doppler frequency, and captured PRNs across channels.
Input pattern: `<input-path>/<file-prefix>.dat` (single file).
| Option | Default | Description |
| -------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the observables dump. |
| `--conf` | unset | GNSS-SDR configuration used to infer observables dump filename and total channel count. |
| `--file-prefix` | `--conf` or `observables.dat` | `Observables.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/hybrid-observables` | Output directory for PNGs. |
| `--channels` | `--conf` or `5` | Number of observable channels in the dump. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| Option | Default | Description |
| -------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the observables dump. |
| `--conf` | unset | GNSS-SDR configuration used to infer observables dump filename and total channel count. |
| `--file-prefix` | `--conf` or `observables.dat` | `Observables.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/hybrid-observables` | Output directory for PNGs. |
| `--channels` | `--conf` or `5` | Number of observable channels in the dump. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
Examples:
@@ -311,20 +316,20 @@ given.
Input pattern: `<input-path>/<file-prefix>.dat` (single file).
| Option | Default | Description |
| ----------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the PVT dump. |
| `--conf` | unset | GNSS-SDR configuration used to infer PVT dump filename and navigation solution period. |
| `--file-prefix` | `--conf` or `PVT.dat` | `PVT.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/pvt` | Output directory for PNGs and maps. |
| `--nav-sol-period` | `--conf` or `10.0` | Navigation solution period in milliseconds. |
| `--true-position E_UTM N_UTM U_UTM` | unset (NaN) | Reference receiver position in UTM, used for error plots. |
| `--plot-skyplot` | off | Try to generate the sky-plot panel. |
| `--no-position` | position plot on | Skip the position/map diagnostic (avoids the `folium` dependency). |
| `--one-vs-time NAME` | `X_vel`, `Tot_Vel` | `navSolutions` variable to plot versus time. Repeatable. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| `--open-maps` | off | Open the generated folium map HTML files in a browser. |
| Option | Default | Description |
| ----------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `-i`, `--input-path` | `.` | Directory containing the PVT dump. |
| `--conf` | unset | GNSS-SDR configuration used to infer PVT dump filename and navigation solution period. |
| `--file-prefix` | `--conf` or `PVT.dat` | `PVT.dump_filename` value (may include a directory/extension). |
| `-o`, `--fig-path` | `plots/pvt` | Output directory for PNGs and maps. |
| `--nav-sol-period` | `--conf` or `10.0` | Navigation solution period in milliseconds. |
| `--true-position E_UTM N_UTM U_UTM` | unset (NaN) | Reference receiver position in UTM, used for error plots. |
| `--plot-skyplot` | off | Try to generate the sky-plot panel. |
| `--no-position` | position plot on | Skip the position/map diagnostic (avoids the `folium` dependency). |
| `--one-vs-time NAME` | `X_vel`, `Tot_Vel` | `navSolutions` variable to plot versus time. Repeatable. |
| `--show` | off | Display figures after saving. |
| `--format` | `png` | Saved figure format: `png` (default), `pdf`, `eps`, `svg`, `jpg`. Vector formats embed fonts (publication-ready). |
| `--open-maps` | off | Open the generated folium map HTML files in a browser. |
Examples:
+20 -3
View File
@@ -33,6 +33,7 @@ from lib.gnss_sdr_conf import (
SIGNAL_TYPES,
add_conf_argument,
load_gnss_sdr_conf,
signal_pretty_name,
)
from lib.plot_format import add_output_format_argument, apply_publication_style
from lib.plotVEMLTracking import plotVEMLTracking
@@ -216,6 +217,11 @@ def read_tracking_dumps(args):
return dumps
def prn_label(prns):
prns = dict.fromkeys(int(prn) for prn in prns if int(prn))
return "/".join(str(prn) for prn in prns) or "unknown"
def main():
args = parse_args()
args.fig_path.mkdir(parents=True, exist_ok=True)
@@ -270,6 +276,14 @@ def main():
/ args.sampling_frequency
),
}
signal_name = signal_pretty_name(
tracking.get("_signal_type") or DEFAULT_SIGNAL_TYPE
)
track_result["signalName"] = signal_name
track_result["prnLabel"] = prn_label(track_result["PRN"])
track_result["plotTitle"] = (
f"{signal_name} PRN {track_result['prnLabel']}"
)
track_results.append(track_result)
plotVEMLTracking(index, track_results, settings)
@@ -281,9 +295,12 @@ def main():
track_result["prn_start_time_s"],
[x / 1000 for x in tracking["carrier_doppler_hz"][start_sample:]],
)
plt.xlabel("Time(s)")
plt.ylabel("Doppler(KHz)")
plt.title(f"Doppler frequency channel {channel}")
plt.xlabel("Time (s)")
plt.ylabel("Doppler (kHz)")
plt.title(
f"Doppler frequency - {track_result['plotTitle']} "
f"(channel {channel})"
)
plt.savefig(
args.fig_path
/ f"Doppler_freq_ch_{channel}.{args.output_format}"
+41 -15
View File
@@ -39,25 +39,51 @@ SIGNAL_ORDER = (
"J5",
)
@dataclass(frozen=True)
class SignalType:
system: str
n_chips: int
pretty_name: str
def __iter__(self):
# Keep compatibility with callers that unpack SIGNAL_TYPES entries as
# (system, n_chips).
yield self.system
yield self.n_chips
# GNSS-SDR signal nomenclature: each signal code maps to the system character
# used in acquisition dump filenames and to its primary code length in chips.
# used in acquisition dump filenames, its primary code length in chips, and a
# human-friendly signal name for plot labels.
SIGNAL_TYPES = {
"1C": ("G", 1023), # GPS L1 C/A
"2S": ("G", 10230), # GPS L2C
"L5": ("G", 10230), # GPS L5
"1B": ("E", 4092), # Galileo E1B
"5X": ("E", 10230), # Galileo E5a
"7X": ("E", 10230), # Galileo E5b
"E6": ("E", 5115), # Galileo E6
"1G": ("R", 511), # GLONASS L1
"2G": ("R", 511), # GLONASS L2
"B1": ("C", 2046), # BeiDou B1I
"B3": ("C", 10230), # BeiDou B3I
"J1": ("J", 1023), # QZSS L1 C/A
"J5": ("J", 10230), # QZSS L5
"1C": SignalType("G", 1023, "GPS L1 C/A"),
"2S": SignalType("G", 10230, "GPS L2C"),
"L5": SignalType("G", 10230, "GPS L5"),
"1B": SignalType("E", 4092, "Galileo E1B"),
"5X": SignalType("E", 10230, "Galileo E5a"),
"7X": SignalType("E", 10230, "Galileo E5b"),
"E6": SignalType("E", 5115, "Galileo E6"),
"1G": SignalType("R", 511, "GLONASS L1 C/A"),
"2G": SignalType("R", 511, "GLONASS L2 C/A"),
"B1": SignalType("C", 2046, "BeiDou B1I"),
"B3": SignalType("C", 10230, "BeiDou B3I"),
"J1": SignalType("J", 1023, "QZSS L1 C/A"),
"J5": SignalType("J", 10230, "QZSS L5"),
}
N_CHIPS = {(system, code): nc for code, (system, nc) in SIGNAL_TYPES.items()}
N_CHIPS = {
(signal_type.system, code): signal_type.n_chips
for code, signal_type in SIGNAL_TYPES.items()
}
def signal_pretty_name(signal_type):
if signal_type is None:
return ""
metadata = SIGNAL_TYPES.get(signal_type.upper())
if metadata is None:
return signal_type
return metadata.pretty_name
class ConfigError(ValueError):
+8 -3
View File
@@ -46,12 +46,17 @@ def plotVEMLTracking(channelNr, trackResults, settings):
# Protection - if the list contains incorrect channel numbers
if channelNr in list(range(1,settings["numberOfChannels"]+1)):
track_result = trackResults[channelNr-1]
prn_label = track_result.get('prnLabel', str(track_result["PRN"][0]))
plot_title = track_result.get('plotTitle', f'PRN {prn_label}')
plt.figure(figsize=(1920 / 120, 1080 / 120))
plt.clf()
plt.gcf().canvas.manager.set_window_title(
f'Channel {display_channel} (PRN '
f'{trackResults[channelNr-1]["PRN"][0]}) results')
plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1,
f'Channel {display_channel} - {plot_title} results')
plt.suptitle(f'{plot_title} (channel {display_channel})',
fontweight='bold')
plt.subplots_adjust(left=0.1, right=0.9, top=0.86, bottom=0.1,
hspace=0.4, wspace=0.4)
# Extract timeAxis and time_label
+202 -33
View File
@@ -33,6 +33,7 @@ from lib.gnss_sdr_conf import (
SIGNAL_TYPES,
add_conf_argument,
load_gnss_sdr_conf,
signal_pretty_name,
)
from lib.plot_format import add_output_format_argument, apply_publication_style
@@ -46,6 +47,8 @@ DEFAULT_SAT = 1
DEFAULT_CHANNEL = 0
DEFAULT_EXECUTION = 1
DEFAULT_SIGNAL_TYPE = "1C"
MAX_LITE_SURFACE_CODE_POINTS = 3200
MAX_LITE_SURFACE_DOPPLER_POINTS = 250
@dataclass(frozen=True)
@@ -176,6 +179,20 @@ def parse_args():
default=3,
help="Samples per chip used by the light grid interpolation.",
)
parser.add_argument(
"--samples-per-doppler-step",
type=int,
default=5,
help="Interpolated samples per Doppler step used by the light grid "
"interpolation.",
)
parser.add_argument(
"--surface-smoothing",
type=float,
default=2.0,
help="Gaussian smoothing sigma, in interpolated surface samples, "
"applied only to the lite 3D view. Use 0 to disable.",
)
parser.add_argument(
"--samples-per-code",
type=int,
@@ -310,6 +327,52 @@ def matching_dump_prefixes(input_path):
return sorted(prefixes)
def configured_doppler_axis(doppler_max, doppler_step, n_dop_bins, file_path):
import numpy as np
doppler_max = abs(float(doppler_max))
doppler_step = abs(float(doppler_step))
if doppler_step == 0:
raise ValueError(f"{file_path}: doppler_step must be non-zero")
freq = np.arange(-doppler_max, doppler_max + doppler_step / 2, doppler_step)
if len(freq) == n_dop_bins:
return freq
legacy_freq = np.arange(n_dop_bins) * doppler_step - doppler_max
if len(freq) - 1 == n_dop_bins:
print(
f"Warning: {file_path.name}: acq_grid has {n_dop_bins} Doppler "
f"bins, one fewer than the inclusive configured axis from "
f"{-doppler_max:g} Hz to {doppler_max:g} Hz in "
f"{doppler_step:g} Hz steps. Plotting the available bins from "
f"{legacy_freq[0]:g} Hz to {legacy_freq[-1]:g} Hz."
)
return legacy_freq
print(
f"Warning: {file_path.name}: configured Doppler axis has {len(freq)} "
f"bins from {-doppler_max:g} Hz to {doppler_max:g} Hz in "
f"{doppler_step:g} Hz steps, but acq_grid has {n_dop_bins} columns. "
"Plotting the available columns over the configured Doppler range."
)
return np.linspace(-doppler_max, doppler_max, n_dop_bins)
def expected_doppler_bin_counts(doppler_max, doppler_step):
import numpy as np
doppler_max = abs(float(doppler_max))
doppler_step = abs(float(doppler_step))
if doppler_step == 0:
return set()
inclusive_count = len(
np.arange(-doppler_max, doppler_max + doppler_step / 2, doppler_step)
)
return {inclusive_count, max(1, inclusive_count - 1)}
def read_acquisition_dump(file_path, n_chips, positive_only):
import h5py
import numpy as np
@@ -323,21 +386,77 @@ def read_acquisition_dump(file_path, n_chips, positive_only):
if positive_acq != 1:
return None
acq_grid = data["acq_grid"][:]
n_fft, n_dop_bins = acq_grid.shape
d_max, f_max = np.unravel_index(np.argmax(acq_grid), acq_grid.shape)
doppler_step = data["doppler_step"][0]
doppler_max = data["doppler_max"][0]
freq = np.arange(n_dop_bins) * doppler_step - doppler_max
acq_grid = data["acq_grid"][:]
expected_doppler_bins = expected_doppler_bin_counts(
doppler_max,
doppler_step,
)
if (
acq_grid.shape[1] not in expected_doppler_bins
and acq_grid.shape[0] in expected_doppler_bins
):
acq_grid = acq_grid.T
n_fft, n_dop_bins = acq_grid.shape
freq = configured_doppler_axis(
doppler_max,
doppler_step,
n_dop_bins,
file_path,
)
d_max, f_max = np.unravel_index(np.argmax(acq_grid), acq_grid.shape)
delay = np.arange(n_fft) / n_fft * n_chips
return acq_grid, n_fft, d_max, f_max, doppler_step, doppler_max, freq, delay
def plot_dump(file_path, fig_path, image_name_root, n_chips, args):
def acquisition_plot_title(metadata):
signal_name = signal_pretty_name(metadata["signal"])
return (
f"{signal_name} PRN {metadata['sat']} "
f"(channel {metadata['channel']}, execution {metadata['execution']})"
)
def interpolated_acquisition_grid(delay, freq, acq_grid, args, n_chips):
import numpy as np
from scipy.ndimage import gaussian_filter
from scipy.interpolate import RectBivariateSpline
if len(delay) < 2 or len(freq) < 2:
return delay, freq, acq_grid
samples_per_chip = max(1, args.samples_per_chip)
samples_per_doppler_step = max(1, args.samples_per_doppler_step)
delay_points = min(
samples_per_chip * n_chips,
MAX_LITE_SURFACE_CODE_POINTS,
)
freq_points = min(
(len(freq) - 1) * samples_per_doppler_step + 1,
MAX_LITE_SURFACE_DOPPLER_POINTS,
)
delay_interp = np.linspace(delay[0], delay[-1], max(2, delay_points))
freq_interp = np.linspace(freq[0], freq[-1], max(2, freq_points))
kx = min(3, len(delay) - 1)
ky = min(3, len(freq) - 1)
spline = RectBivariateSpline(delay, freq, acq_grid, kx=kx, ky=ky)
grid_interp = spline(delay_interp, freq_interp)
if args.surface_smoothing > 0:
grid_interp = gaussian_filter(
grid_interp,
sigma=args.surface_smoothing,
mode="nearest",
)
return delay_interp, freq_interp, grid_interp
def plot_dump(file_path, fig_path, image_name_root, n_chips, args, metadata=None):
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import CubicSpline
dump = read_acquisition_dump(file_path, n_chips, args.plot_positive_acqs)
if dump is None:
@@ -354,28 +473,65 @@ def plot_dump(file_path, fig_path, image_name_root, n_chips, args):
delay,
) = dump
if metadata is None:
metadata = parse_dump_name(file_path)
plot_title = acquisition_plot_title(metadata) if metadata else str(file_path)
fig = plt.figure()
plt.gcf().canvas.manager.set_window_title(str(file_path))
if not args.lite_view:
ax = fig.add_subplot(111, projection="3d")
x_axis, y_axis = np.meshgrid(freq, delay)
ax.plot_surface(x_axis, y_axis, acq_grid, cmap="viridis")
x_axis, y_axis = np.meshgrid(freq, delay, indexing="xy")
ax.plot_surface(
x_axis,
y_axis,
acq_grid,
cmap="viridis",
linewidth=0,
edgecolor="none",
antialiased=False,
)
ax.set_ylim([min(delay), max(delay)])
else:
delay_interp = (
np.arange(args.samples_per_chip * n_chips) / args.samples_per_chip
delay_interp, freq_interp, grid_interp = interpolated_acquisition_grid(
delay,
freq,
acq_grid,
args,
n_chips,
)
spline = CubicSpline(delay, acq_grid)
grid_interp = spline(delay_interp)
ax = fig.add_subplot(111, projection="3d")
x_axis, y_axis = np.meshgrid(freq, delay_interp)
ax.plot_surface(x_axis, y_axis, grid_interp, cmap="inferno")
x_axis, y_axis = np.meshgrid(freq_interp, delay_interp, indexing="xy")
ax.plot_surface(
x_axis,
y_axis,
grid_interp,
cmap="inferno",
rstride=1,
cstride=1,
linewidth=0,
edgecolor="none",
antialiased=False,
)
ax.set_ylim([min(delay_interp), max(delay_interp)])
peak_doppler = freq[f_max]
peak_delay = delay[d_max]
peak_value = acq_grid[d_max, f_max]
ax.scatter(
[peak_doppler],
[peak_delay],
[peak_value],
color="red",
s=30,
label="Peak",
)
ax.set_xlabel("Doppler shift (Hz)")
ax.set_xlim([min(freq), max(freq)])
ax.set_ylabel("Code delay (chips)")
ax.set_zlabel("Test Statistics")
ax.set_title(plot_title)
ax.legend(loc="upper right")
plt.tight_layout()
plt.savefig(fig_path / f"{image_name_root}_3D.{args.output_format}")
if not args.show:
@@ -383,24 +539,25 @@ def plot_dump(file_path, fig_path, image_name_root, n_chips, args):
fig2, axes = plt.subplots(2, 1, figsize=(8, 6))
plt.gcf().canvas.manager.set_window_title(str(file_path))
fig2.suptitle(plot_title)
axes[0].plot(freq, acq_grid[d_max, :])
axes[0].axvline(peak_doppler, color="red", linestyle="--", linewidth=0.8)
axes[0].set_xlim([min(freq), max(freq)])
axes[0].set_xlabel("Doppler shift (Hz)")
axes[0].set_ylabel("Test statistics")
axes[0].set_title(
f"Fixed code delay to {(d_max - 1) / n_fft * n_chips} chips"
)
axes[0].set_title(f"Doppler cut at code delay = {peak_delay:g} chips")
normalization = (args.samples_per_code ** 4) * args.input_power
axes[1].plot(delay, acq_grid[:, f_max] / normalization)
axes[1].axvline(peak_delay, color="red", linestyle="--", linewidth=0.8)
axes[1].set_xlim([min(delay), max(delay)])
axes[1].set_xlabel("Code delay (chips)")
axes[1].set_ylabel("Test statistics")
axes[1].set_title(
f"Doppler wipe-off = {(f_max - 1) * doppler_step - doppler_max} Hz"
f"Code-delay cut at Doppler wipe-off = {peak_doppler:g} Hz"
)
plt.tight_layout()
fig2.tight_layout(rect=(0, 0, 1, 0.95))
plt.savefig(fig_path / f"{image_name_root}_2D.{args.output_format}")
if not args.show:
plt.close(fig2)
@@ -425,17 +582,15 @@ def single_file_path(args):
f"{base}_{system}_{signal}_ch_{channel}_"
f"{execution}_sat_{sat}.mat"
)
image_name_root = image_name_from_metadata(
args.output_basename,
{
"system": system,
"signal": signal,
"channel": channel,
"execution": execution,
"sat": sat,
},
)
return directory / filename, image_name_root, n_chips
metadata = {
"system": system,
"signal": signal,
"channel": channel,
"execution": execution,
"sat": sat,
}
image_name_root = image_name_from_metadata(args.output_basename, metadata)
return directory / filename, image_name_root, n_chips, metadata
def image_name_from_metadata(output_basename, metadata):
@@ -486,10 +641,17 @@ def main():
apply_publication_style()
if not args.plot_all_files:
file_path, image_name_root, n_chips = single_file_path(args)
file_path, image_name_root, n_chips, metadata = single_file_path(args)
if not file_path.exists():
raise FileNotFoundError(f"dump file not found: {file_path}")
plot_dump(file_path, args.fig_path, image_name_root, n_chips, args)
plot_dump(
file_path,
args.fig_path,
image_name_root,
n_chips,
args,
metadata,
)
show_figures(args)
return
@@ -530,7 +692,14 @@ def main():
args.output_basename,
metadata,
)
if plot_dump(file_path, args.fig_path, image_name_root, n_chips, args):
if plot_dump(
file_path,
args.fig_path,
image_name_root,
n_chips,
args,
metadata,
):
plotted += 1
if not found_paths: