-
-
Notifications
You must be signed in to change notification settings - Fork 2
Initial scaffolding build for geophysical research #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -10,7 +10,7 @@ | |||||
| from pathlib import Path | ||||||
|
|
||||||
| from analysis.preprocess import preprocess_data | ||||||
| from analysis.plotting import plot_performance | ||||||
| from analysis.plotting import plot_performance, plot_relative_performance | ||||||
| from haversine import haversine_vector, Unit | ||||||
|
|
||||||
| __version__ = "0.1.0" | ||||||
|
|
@@ -85,12 +85,46 @@ def main() -> None: | |||||
| default="data/output" | ||||||
| ) | ||||||
|
|
||||||
| geoperformance = command.add_parser( | ||||||
| "geoperformance", help="Generate geophysical performance plots." | ||||||
| ) | ||||||
| geoperformance.add_argument( | ||||||
| "-p", | ||||||
| "--processed", | ||||||
| type=str, | ||||||
| help="Input directory containing the processed navigation result CSV files.", | ||||||
| ) | ||||||
| geoperformance.add_argument( | ||||||
| "-r", | ||||||
| "--reference", | ||||||
| type=str, | ||||||
| help="Input directory containing the reference GPS CSV files.", | ||||||
| default="data/input" | ||||||
| ) | ||||||
| geoperformance.add_argument( | ||||||
| "-d", | ||||||
| "--degraded", | ||||||
| type=str, | ||||||
| help="Input directory containing the degraded navigation result CSV files.", | ||||||
| ) | ||||||
| geoperformance.add_argument( | ||||||
| "-o", | ||||||
| "--output", | ||||||
| type=str, | ||||||
| help="Output directory for the geophysical performance plots.", | ||||||
| default="data/output" | ||||||
| ) | ||||||
|
|
||||||
| args = parser.parse_args() | ||||||
|
|
||||||
| if args.command == "preprocess": | ||||||
| preprocess_data(args) | ||||||
| elif args.command == "performance": | ||||||
| performance_analysis(args) | ||||||
| elif args.command == "geoperformance": | ||||||
| geophysical_performance_analysis(args) | ||||||
| else: | ||||||
| parser.print_help() | ||||||
|
|
||||||
|
|
||||||
| def performance_analysis(args): | ||||||
|
|
@@ -158,6 +192,52 @@ def performance_analysis(args): | |||||
| summary_df.to_csv(summary_file) | ||||||
| print("Performance analysis completed.") | ||||||
|
|
||||||
| def geophysical_performance_analysis(args): | ||||||
| """Generate geophysical performance plots.""" | ||||||
| input_dir = args.processed | ||||||
| print(f"Generating geophysical performance plots from data in: {input_dir}") | ||||||
|
|
||||||
| datasets = list(Path(input_dir).glob("*.csv")) | ||||||
| print(f"Found {len(datasets)} datasets to process.") | ||||||
|
|
||||||
| print(f"Comparing to reference data in: {args.reference}") | ||||||
| references = list(Path(args.reference).glob("*.csv")) | ||||||
| print(f"Found {len(references)} reference datasets.") | ||||||
|
|
||||||
| print(f"Comparing to degraded data in: {args.degraded}") | ||||||
| degradeds = list(Path(args.degraded).glob("*.csv")) | ||||||
| print(f"Found {len(degradeds)} degraded datasets.") | ||||||
|
|
||||||
| output_path = Path(args.output) | ||||||
| output_path.mkdir(parents=True, exist_ok=True) | ||||||
| print(f"Saving geophysical performance plots to: {args.output}") | ||||||
|
|
||||||
| reference_path = Path(args.reference) | ||||||
| degraded_path = Path(args.degraded) | ||||||
|
|
||||||
| for dataset in datasets: | ||||||
| geo = read_csv(dataset, parse_dates=True, index_col=0) | ||||||
| try: | ||||||
| reference_file = reference_path / dataset.name | ||||||
| nav = read_csv(reference_file, parse_dates=True, index_col=0) | ||||||
| except FileNotFoundError: | ||||||
| print(f"Reference file for {dataset.name} not found in {reference_path}. Skipping.") | ||||||
| continue | ||||||
| try: | ||||||
| degraded_file = degraded_path / dataset.name | ||||||
| degraded_nav = read_csv(degraded_file, parse_dates=True, index_col=0) | ||||||
| except FileNotFoundError: | ||||||
| print(f"Degraded file for {dataset.name} not found in {degraded_path}. Skipping.") | ||||||
| continue | ||||||
| output_plot = output_path / f"{dataset.stem}_geophysical_performance.png" | ||||||
| print(f"Processing dataset {dataset} ({len(nav)}) with reference {reference_file.name} ({len(nav)}) and degraded {degraded_file.name} ({len(degraded_nav)})") | ||||||
|
||||||
| print(f"Processing dataset {dataset} ({len(nav)}) with reference {reference_file.name} ({len(nav)}) and degraded {degraded_file.name} ({len(degraded_nav)})") | |
| print(f"Processing dataset {dataset} ({len(geo)}) with reference {reference_file.name} ({len(nav)}) and degraded {degraded_file.name} ({len(degraded_nav)})") |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -138,7 +138,7 @@ def plot_performance(nav: DataFrame, gps: DataFrame, output_path: Path | str): | |||||||||||||||
| linestyle="--", | ||||||||||||||||
| ) | ||||||||||||||||
| ax.set_xlim(left=0) | ||||||||||||||||
| ax.set_ylim((0, 50)) | ||||||||||||||||
| #ax.set_ylim((0, 50)) | ||||||||||||||||
| ax.set_xlabel("Time (s)") | ||||||||||||||||
| ax.set_ylabel("2D Haversine Error (m)") | ||||||||||||||||
| ax.set_title( | ||||||||||||||||
|
|
@@ -150,6 +150,82 @@ def plot_performance(nav: DataFrame, gps: DataFrame, output_path: Path | str): | |||||||||||||||
| plt.close(fig) | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| def plot_relative_performance( | ||||||||||||||||
| geo: DataFrame, | ||||||||||||||||
| deg: DataFrame, # degraded/baseline navigation | ||||||||||||||||
| nav: DataFrame, # GPS-aided truth | ||||||||||||||||
|
Comment on lines
+155
to
+156
|
||||||||||||||||
| output_path: Path | str, | ||||||||||||||||
| ): | ||||||||||||||||
| """Plot relative performance: geophysical-aided nav w.r.t. degraded nav. | ||||||||||||||||
|
|
||||||||||||||||
| This mirrors the geonav-sim notebook visualization by plotting the | ||||||||||||||||
| difference of the cumulative root-mean-squared error series: | ||||||||||||||||
|
|
||||||||||||||||
| root_mean_geo_cum_error - root_mean_deg_cum_error | ||||||||||||||||
|
|
||||||||||||||||
| Positive values are filled red, negative values green. | ||||||||||||||||
| """ | ||||||||||||||||
| output_path = Path(output_path) | ||||||||||||||||
|
|
||||||||||||||||
| distance_traveled = haversine_vector( | ||||||||||||||||
| nav[["latitude", "longitude"]].to_numpy()[:-1, :], | ||||||||||||||||
| nav[["latitude", "longitude"]].to_numpy()[1:, :], | ||||||||||||||||
| Unit.METERS, | ||||||||||||||||
| ) | ||||||||||||||||
| distance_traveled = np.hstack(([0], distance_traveled)) | ||||||||||||||||
| distance_traveled = np.nancumsum(distance_traveled) | ||||||||||||||||
|
|
||||||||||||||||
|
Comment on lines
+170
to
+177
|
||||||||||||||||
| distance_traveled = haversine_vector( | |
| nav[["latitude", "longitude"]].to_numpy()[:-1, :], | |
| nav[["latitude", "longitude"]].to_numpy()[1:, :], | |
| Unit.METERS, | |
| ) | |
| distance_traveled = np.hstack(([0], distance_traveled)) | |
| distance_traveled = np.nancumsum(distance_traveled) |
Copilot
AI
Jan 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace on this line. This should be removed to maintain code cleanliness and consistency.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # EKF with both gravity and magnetic anomaly measurements | ||
| input = "data/input" | ||
| output = "data/output/ekf/both" | ||
| mode = "closed-loop" | ||
| seed = 42 | ||
| parallel = true | ||
|
|
||
| [logging] | ||
| level = "info" | ||
| file = "log/ekf_both.log" | ||
|
|
||
| [closed_loop] | ||
| filter = "ekf" | ||
|
|
||
| [geophysical] | ||
| # Gravity anomaly measurements | ||
| gravity_resolution = "one_minute" | ||
| gravity_bias = 0.0 | ||
| gravity_noise_std = 100.0 | ||
|
|
||
| # Magnetic anomaly measurements | ||
| magnetic_resolution = "two_minutes" | ||
| magnetic_bias = 0.0 | ||
| magnetic_noise_std = 150.0 | ||
|
|
||
| # Measurement frequency (seconds) | ||
| geo_frequency_s = 1.0 | ||
|
|
||
| [gnss_degradation] | ||
| seed = 42 | ||
|
|
||
| [gnss_degradation.scheduler] | ||
| kind = "fixed_interval" | ||
| interval_s = 60.0 | ||
| phase_s = 0.0 | ||
|
|
||
| [gnss_degradation.fault] | ||
| kind = "degraded" | ||
| rho_pos = 0.99 | ||
| sigma_pos_m = 15.0 | ||
| rho_vel = 0.95 | ||
| sigma_vel_mps = 5.0 | ||
| r_scale = 15.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # EKF with gravity anomaly measurements only | ||
| input = "data/input" | ||
| output = "data/output/ekf/grav" | ||
| mode = "closed-loop" | ||
| seed = 42 | ||
| parallel = true | ||
|
|
||
| [logging] | ||
| level = "info" | ||
| file = "log/ekf_grav.log" | ||
|
|
||
| [closed_loop] | ||
| filter = "ekf" | ||
|
|
||
| [geophysical] | ||
| # Gravity anomaly measurements | ||
| gravity_resolution = "one_minute" | ||
| gravity_bias = 0.0 | ||
| gravity_noise_std = 100.0 | ||
| # gravity_map_file will be auto-detected if not specified | ||
|
|
||
| # Magnetic anomaly disabled for this config | ||
| # magnetic_resolution = "" | ||
| # magnetic_bias = 0.0 | ||
| # magnetic_noise_std = 150.0 | ||
|
|
||
| # Measurement frequency (seconds) | ||
| geo_frequency_s = 1.0 | ||
|
|
||
| [gnss_degradation] | ||
| seed = 42 | ||
|
|
||
| [gnss_degradation.scheduler] | ||
| kind = "pass_through" | ||
|
|
||
| [gnss_degradation.fault] | ||
| kind = "none" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # EKF with magnetic anomaly measurements only | ||
| input = "data/input" | ||
| output = "data/output/ekf/mag" | ||
| mode = "closed-loop" | ||
| seed = 42 | ||
| parallel = true | ||
|
|
||
| [logging] | ||
| level = "info" | ||
| file = "log/ekf_mag.log" | ||
|
|
||
| [closed_loop] | ||
| filter = "ekf" | ||
|
|
||
| [geophysical] | ||
| # Magnetic anomaly measurements | ||
| magnetic_resolution = "two_minutes" | ||
| magnetic_bias = 0.0 | ||
| magnetic_noise_std = 150.0 | ||
| # magnetic_map_file will be auto-detected if not specified | ||
|
|
||
| # Gravity anomaly disabled for this config | ||
| # gravity_resolution = "" | ||
| # gravity_bias = 0.0 | ||
| # gravity_noise_std = 100.0 | ||
|
|
||
| # Measurement frequency (seconds) | ||
| geo_frequency_s = 1.0 | ||
|
|
||
| [gnss_degradation] | ||
| seed = 42 | ||
|
|
||
| [gnss_degradation.scheduler] | ||
| kind = "pass_through" | ||
|
|
||
| [gnss_degradation.fault] | ||
| kind = "none" |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variables
referencesanddegradedsare computed but never used. These lines can be removed as the actual file paths are constructed later usingreference_path / dataset.nameanddegraded_path / dataset.name.