Tutorial: Plot Rover PositionsΒΆ

This notebook focuses on the rover positions stored in the RF xarray.

The extractor already removed:

  • invalid or missing rover samples
  • duplicate consecutive rover positions within the configured tolerance

This version of the tutorial can merge multiple experiment IDs into one combined set of valid rover positions.

# Optional: uncomment when this Jupyter kernel misses the plotting dependencies.
# import sys
# !{sys.executable} -m pip install matplotlib numpy requests xarray pyyaml
from pathlib import Path
import importlib.util
import sys

import matplotlib.pyplot as plt

NOTEBOOK_DIR = Path.cwd().resolve()
for candidate_dir in (
    NOTEBOOK_DIR,
    NOTEBOOK_DIR / "tutorials",
    NOTEBOOK_DIR / "processing" / "tutorials",
):
    if (candidate_dir / "csi_plot_utils.py").exists():
        NOTEBOOK_DIR = candidate_dir.resolve()
        break
else:
    raise ImportError(f"Could not locate csi_plot_utils.py from {Path.cwd().resolve()}")

UTILS_PATH = NOTEBOOK_DIR / "csi_plot_utils.py"
PROCESSING_DIR = NOTEBOOK_DIR.parent
PROJECT_ROOT = PROCESSING_DIR.parent
spec = importlib.util.spec_from_file_location("csi_plot_utils", UTILS_PATH)
if spec is None or spec.loader is None:
    raise ImportError(f"Could not load utility module from {UTILS_PATH}")
csi = importlib.util.module_from_spec(spec)
sys.modules["csi_plot_utils"] = csi
spec.loader.exec_module(csi)
REQUESTED_EXPERIMENT_IDS = None  # Example: ["EXP003", "EXP005"]. None means: use all experiment IDs in the opened dataset.
DATASET_PATH = None  # Set this to a specific .nc file when needed.
if REQUESTED_EXPERIMENT_IDS is None:
    ds, dataset_path = csi.open_dataset(dataset_path=DATASET_PATH)
    EXPERIMENT_IDS = csi.available_experiment_ids(ds)
else:
    EXPERIMENT_IDS = csi.normalize_experiment_ids(REQUESTED_EXPERIMENT_IDS)
    ds, dataset_path = csi.open_dataset(experiment_id=EXPERIMENT_IDS, dataset_path=DATASET_PATH)

print(f"Loaded dataset: {dataset_path}")
print(f"Selected experiment IDs: {EXPERIMENT_IDS}")
csi.print_experiment_overview(ds, EXPERIMENT_IDS)
Loaded dataset: C:\Users\Calle\OneDrive\Documenten\GitHub\ELLIIIT-dataset-26\results\csi_EXP003__EXP005__EXP006__EXP007__EXP008__EXP009__EXP010__EXP011__EXP012.nc
Selected experiment IDs: ['EXP003', 'EXP005', 'EXP006', 'EXP007', 'EXP008', 'EXP009', 'EXP010', 'EXP011', 'EXP012']
EXP003: cycles_with_csi=529, valid_positions=529, first_csi_cycle=1, last_csi_cycle=529
EXP005: cycles_with_csi=777, valid_positions=777, first_csi_cycle=1, last_csi_cycle=777
EXP006: cycles_with_csi=238, valid_positions=238, first_csi_cycle=1, last_csi_cycle=238
EXP007: cycles_with_csi=378, valid_positions=378, first_csi_cycle=1, last_csi_cycle=378
EXP008: cycles_with_csi=201, valid_positions=201, first_csi_cycle=1, last_csi_cycle=201
EXP009: cycles_with_csi=1355, valid_positions=1355, first_csi_cycle=1, last_csi_cycle=1356
EXP010: cycles_with_csi=458, valid_positions=458, first_csi_cycle=1, last_csi_cycle=458
EXP011: cycles_with_csi=291, valid_positions=291, first_csi_cycle=1, last_csi_cycle=291
EXP012: cycles_with_csi=784, valid_positions=784, first_csi_cycle=1, last_csi_cycle=784

Inspect The Merged Valid Position RowsΒΆ

The helper below keeps only rows with valid rover coordinates and merges the selected experiment IDs into one measurement table.

positions = csi.positions_for_experiments(ds, EXPERIMENT_IDS)
print(f"Valid rover positions across the selected experiments: {positions.sizes['measurement_index']}")
positions.isel(measurement_index=slice(0, min(10, positions.sizes['measurement_index'])))
Valid rover positions across the selected experiments: 5011
<xarray.Dataset> Size: 720B
Dimensions:            (measurement_index: 10)
Coordinates:
  * measurement_index  (measurement_index) int64 80B 0 1 2 3 4 5 6 7 8 9
Data variables:
    experiment_id      (measurement_index) <U6 240B 'EXP003' ... 'EXP003'
    cycle_id           (measurement_index) int64 80B 1 2 3 4 5 6 7 8 9 10
    rover_x            (measurement_index) float64 80B 1.918 2.541 ... 2.534
    rover_y            (measurement_index) float64 80B 2.865 2.254 ... 3.213
    rover_z            (measurement_index) float64 80B 0.7394 0.7401 ... 0.7428
    csi_host_count     (measurement_index) int64 80B 42 42 42 42 ... 42 42 42 42
Attributes:
    experiment_ids:  ['EXP003', 'EXP005', 'EXP006', 'EXP007', 'EXP008', 'EXP0...
xarray.Dataset
    • measurement_index: 10
    • measurement_index
      (measurement_index)
      int64
      0 1 2 3 4 5 6 7 8 9
      array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    • experiment_id
      (measurement_index)
      <U6
      'EXP003' 'EXP003' ... 'EXP003'
      array(['EXP003', 'EXP003', 'EXP003', 'EXP003', 'EXP003', 'EXP003',
             'EXP003', 'EXP003', 'EXP003', 'EXP003'], dtype='<U6')
    • cycle_id
      (measurement_index)
      int64
      1 2 3 4 5 6 7 8 9 10
      array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
    • rover_x
      (measurement_index)
      float64
      1.918 2.541 2.536 ... 2.534 2.534
      array([1.91830371, 2.54144556, 2.53586108, 2.53549023, 2.53543335,
             2.53498926, 2.53407666, 2.53364722, 2.53377734, 2.53357544])
    • rover_y
      (measurement_index)
      float64
      2.865 2.254 2.375 ... 3.094 3.213
      array([2.86501685, 2.25370142, 2.37524902, 2.49515308, 2.61471655,
             2.73427002, 2.85399072, 2.97374976, 3.09379517, 3.21349072])
    • rover_z
      (measurement_index)
      float64
      0.7394 0.7401 ... 0.7422 0.7428
      array([0.73942676, 0.74005927, 0.74008026, 0.7404585 , 0.74080383,
             0.74087518, 0.74136768, 0.74172736, 0.74216925, 0.7428092 ])
    • csi_host_count
      (measurement_index)
      int64
      42 42 42 42 42 42 42 42 42 42
      array([42, 42, 42, 42, 42, 42, 42, 42, 42, 42])
    • measurement_index
      PandasIndex
      PandasIndex(Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64', name='measurement_index'))
  • experiment_ids :
    ['EXP003', 'EXP005', 'EXP006', 'EXP007', 'EXP008', 'EXP009', 'EXP010', 'EXP011', 'EXP012']

Plot The Full Trajectory As Merged Scatter PointsΒΆ

This plot ignores the experiment boundaries and shows all valid rover positions as one combined point cloud. The antenna positions are fetched in csi_plot_utils.py and overlaid as square markers.

csi.plot_position_cloud(ds, EXPERIMENT_IDS, show_antennas=True)
plt.show()
No description has been provided for this image

Plot The Per-Experiment TrajectoriesΒΆ

Use this second plot when you still want to see the trajectory order for each experiment separately.

csi.plot_trajectory(ds, EXPERIMENT_IDS)
plt.show()
No description has been provided for this image

Resolve A Physical Point To An Experiment And Cycle IDΒΆ

Replace the example coordinates below with any target position in meters. The helper searches across the merged experiment IDs and returns the nearest recorded measurement.

target_x = float(positions['rover_x'].values[-1])
target_y = float(positions['rover_y'].values[-1])

nearest = csi.find_nearest_position_cycle(
    ds,
    EXPERIMENT_IDS,
    x=target_x,
    y=target_y,
)

print("Nearest recorded measurement for the chosen point:")
nearest
Nearest recorded measurement for the chosen point:
{'experiment_id': 'EXP012',
 'target_x': 5.2856826171875,
 'target_y': 2.95383740234375,
 'target_z': None,
 'cycle_id': 784,
 'rover_x': 5.2856826171875,
 'rover_y': 2.95383740234375,
 'rover_z': 0.75779052734375,
 'distance_m': 0.0,
 'csi_host_count': 41}