Skip to content

Commit 714a659

Browse files
authored
Merge pull request #232 from jbrodovsky:jbrodovsky/issue230
Create geophysical RBPF implementation
2 parents 05c7163 + cc3ee62 commit 714a659

95 files changed

Lines changed: 190078 additions & 229110 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

analysis/src/analysis/preprocess.py

Lines changed: 25 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -36,59 +36,35 @@ def clean_phone_data(dataset_path: Path | str) -> pd.DataFrame:
3636
assert os.path.exists(dataset_path), f"File {dataset_path} does not exist."
3737
# Assert the needed .csv files exist
3838
# assert os.path.exists(os.path.join(dataset_path, "Accelerometer.csv")), "Accelerometer.csv does not exist."
39-
assert os.path.exists(os.path.join(dataset_path, "Gyroscope.csv")), (
40-
"Gyroscope.csv does not exist."
41-
)
39+
assert os.path.exists(os.path.join(dataset_path, "Gyroscope.csv")), "Gyroscope.csv does not exist."
4240
# Check to make sure that the trajectory is of sufficient length (>=300 seconds)
4341
gyro = pd.read_csv(os.path.join(dataset_path, "Gyroscope.csv"), index_col=0)
4442
assert gyro["seconds_elapsed"].max() >= 300, (
4543
f"Trajectory is too short. Minimum required is 300 seconds. Trajectory is {gyro['seconds_elapsed'].max()} seconds."
4644
)
47-
assert os.path.exists(os.path.join(dataset_path, "Magnetometer.csv")), (
48-
"Magnetometer.csv does not exist."
49-
)
50-
assert os.path.exists(os.path.join(dataset_path, "Barometer.csv")), (
51-
"Barometer.csv does not exist."
52-
)
53-
assert os.path.exists(os.path.join(dataset_path, "Gravity.csv")), (
54-
"Gravity.csv does not exist."
55-
)
45+
assert os.path.exists(os.path.join(dataset_path, "Magnetometer.csv")), "Magnetometer.csv does not exist."
46+
assert os.path.exists(os.path.join(dataset_path, "Barometer.csv")), "Barometer.csv does not exist."
47+
assert os.path.exists(os.path.join(dataset_path, "Gravity.csv")), "Gravity.csv does not exist."
5648
try:
57-
assert os.path.exists(os.path.join(dataset_path, "LocationGps.csv")), (
58-
"LocationGps.csv does not exist."
59-
)
49+
assert os.path.exists(os.path.join(dataset_path, "LocationGps.csv")), "LocationGps.csv does not exist."
6050
except AssertionError:
61-
assert os.path.exists(os.path.join(dataset_path, "Location.csv")), (
62-
"Location.csv does not exist."
63-
)
64-
assert os.path.exists(os.path.join(dataset_path, "Orientation.csv")), (
65-
"Orientation.csv does not exist."
66-
)
51+
assert os.path.exists(os.path.join(dataset_path, "Location.csv")), "Location.csv does not exist."
52+
assert os.path.exists(os.path.join(dataset_path, "Orientation.csv")), "Orientation.csv does not exist."
6753
# Read in raw data
6854
gyroscope = pd.read_csv(os.path.join(dataset_path, "Gyroscope.csv"), index_col=0)
69-
magnetometer = pd.read_csv(
70-
os.path.join(dataset_path, "Magnetometer.csv"), index_col=0
71-
)
55+
magnetometer = pd.read_csv(os.path.join(dataset_path, "Magnetometer.csv"), index_col=0)
7256
barometer = pd.read_csv(os.path.join(dataset_path, "Barometer.csv"), index_col=0)
7357
gravity = pd.read_csv(os.path.join(dataset_path, "Gravity.csv"), index_col=0)
74-
orientation = pd.read_csv(
75-
os.path.join(dataset_path, "Orientation.csv"), index_col=0
76-
)
58+
orientation = pd.read_csv(os.path.join(dataset_path, "Orientation.csv"), index_col=0)
7759
try:
78-
location = pd.read_csv(
79-
os.path.join(dataset_path, "LocationGps.csv"), index_col=0
80-
)
60+
location = pd.read_csv(os.path.join(dataset_path, "LocationGps.csv"), index_col=0)
8161
except FileNotFoundError:
8262
location = pd.read_csv(os.path.join(dataset_path, "Location.csv"), index_col=0)
8363
try:
84-
accelerometer = pd.read_csv(
85-
os.path.join(dataset_path, "TotalAcceleration.csv"), index_col=0
86-
)
64+
accelerometer = pd.read_csv(os.path.join(dataset_path, "TotalAcceleration.csv"), index_col=0)
8765
except FileNotFoundError as e:
8866
print(f"TotalAcceleration.csv not found, using Accelerometer.csv instead: {e}")
89-
accelerometer = pd.read_csv(
90-
os.path.join(dataset_path, "Accelerometer.csv"), index_col=0
91-
)
67+
accelerometer = pd.read_csv(os.path.join(dataset_path, "Accelerometer.csv"), index_col=0)
9268
accelerometer["x"] += gravity["x"]
9369
accelerometer["y"] += gravity["y"]
9470
accelerometer["z"] += gravity["z"]
@@ -109,12 +85,8 @@ def clean_phone_data(dataset_path: Path | str) -> pd.DataFrame:
10985
location.drop(columns=["seconds_elapsed"], inplace=True)
11086
orientation.drop(columns=["seconds_elapsed"], inplace=True)
11187
# Rename columns
112-
magnetometer = magnetometer.rename(
113-
columns={"x": "mag_x", "y": "mag_y", "z": "mag_z"}
114-
)
115-
accelerometer = accelerometer.rename(
116-
columns={"x": "acc_x", "y": "acc_y", "z": "acc_z"}
117-
)
88+
magnetometer = magnetometer.rename(columns={"x": "mag_x", "y": "mag_y", "z": "mag_z"})
89+
accelerometer = accelerometer.rename(columns={"x": "acc_x", "y": "acc_y", "z": "acc_z"})
11890
gyroscope = gyroscope.rename(columns={"x": "gyro_x", "y": "gyro_y", "z": "gyro_z"})
11991
gravity = gravity.rename(columns={"x": "grav_x", "y": "grav_y", "z": "grav_z"})
12092
# Merge dataframes
@@ -160,13 +132,12 @@ def convert_hz_to_time_str(frequency: int) -> str:
160132

161133
def preprocess_data(args):
162134
"""Preprocess the data based on the provided arguments."""
163-
# datasets = Path(args.input_dir).glob("**/*.csv")
164-
input_path = Path(args.input_dir)
165-
output_path = Path(args.output_dir)
135+
input_path = Path(args.input)
136+
output_path = Path(args.output)
166137
# Find all CSV files under the input directory
167138
all_csv = list(input_path.rglob("*.csv"))
168139

169-
# Check for folders directly under input_dir that contain CSV files.
140+
# Check for folders directly under input that contain CSV files.
170141
datasets = [d for d in input_path.iterdir() if d.is_dir() and any(d.glob("*.csv"))]
171142

172143
print("found the following folders with data: ")
@@ -175,9 +146,7 @@ def preprocess_data(args):
175146

176147
# Check to see if datasets in empty, if true ask if the user would lke to download the dataset
177148
if not datasets:
178-
download = input(
179-
"No datasets found. Would you like to download the dataset? (y/n): "
180-
)
149+
download = input("No datasets found. Would you like to download the dataset? (y/n): ")
181150
if download.lower() == "y":
182151
# Code to download the dataset goes here
183152
print("Fetch script is currently not implemented")
@@ -186,16 +155,11 @@ def preprocess_data(args):
186155
print("No datasets found. Exiting.")
187156
return
188157

189-
# os.makedirs(os.path.join("data", "cleaned"), exist_ok=True)
190-
# os.makedirs(args.output_dir, exist_ok=True)
191-
print(
192-
f"Preprocessing data from {args.input_dir}. Output will be saved to {args.output_dir}."
193-
)
158+
print(f"Preprocessing data from {args.input}. Output will be saved to {args.output}.")
194159
output_path.mkdir(parents=True, exist_ok=True)
195160

196161
# def process_dataset(dataset: Path):
197162
for dataset in tqdm(datasets):
198-
# dataset_path = os.path.join(args.input_dir, dataset)
199163
cleaned_data = pd.DataFrame()
200164
# print(f"Processing: {dataset}")
201165
try:
@@ -214,9 +178,6 @@ def preprocess_data(args):
214178
title=dataset.name,
215179
)
216180
street_map_path = output_path / f"{dataset.name}_street_map.png"
217-
# os.path.join(
218-
# args.output_dir, f"{dataset.name}_street_map.png"
219-
# )
220181
street_map.savefig(street_map_path, dpi=300)
221182
# Close the figure to avoid accumulating open figures and memory usage
222183
plt.close(street_map)
@@ -227,20 +188,14 @@ def preprocess_data(args):
227188
lon_max = cleaned_data["longitude"].max()
228189
lat_min = cleaned_data["latitude"].min()
229190
lat_max = cleaned_data["latitude"].max()
230-
lon_min, lon_max, lat_min, lat_max = inflate_bounds(
231-
lon_min, lon_max, lat_min, lat_max, args.buffer
232-
)
191+
lon_min, lon_max, lat_min, lat_max = inflate_bounds(lon_min, lon_max, lat_min, lat_max, args.buffer)
233192

234193
# Download the maps
235-
relief = load_earth_relief(
236-
resolution="15s", region=[lon_min, lon_max, lat_min, lat_max]
237-
)
194+
relief = load_earth_relief(resolution="15s", region=[lon_min, lon_max, lat_min, lat_max])
238195
relief.to_netcdf(output_path / f"{dataset.name}_relief.nc")
239196
# print(f" Downloaded relief map: {relief.data.shape}.")
240197

241-
gravity = load_earth_free_air_anomaly(
242-
resolution="01m", region=[lon_min, lon_max, lat_min, lat_max]
243-
)
198+
gravity = load_earth_free_air_anomaly(resolution="01m", region=[lon_min, lon_max, lat_min, lat_max])
244199
gravity.to_netcdf(output_path / f"{dataset.name}_gravity.nc")
245200
# print(f" Downloaded gravity map: {gravity.data.shape}.")
246201

@@ -258,13 +213,13 @@ def main() -> None:
258213
"""
259214
parser = ArgumentParser(description="Clean sensor logger app data or plot routes.")
260215
parser.add_argument(
261-
"--input_dir",
216+
"--input",
262217
type=str,
263218
default="data/raw",
264219
help="Base directory for the sensor logger app data.",
265220
)
266221
parser.add_argument(
267-
"--output_dir",
222+
"--output",
268223
type=str,
269224
default="data",
270225
help="Output directory for the cleaned data.",
@@ -281,9 +236,7 @@ def main() -> None:
281236
help="Whether to download geophysical maps for each trajectory.",
282237
)
283238
args = parser.parse_args()
284-
assert os.path.exists(args.input_dir), (
285-
f"Input directory {args.input_dir} does not exist."
286-
)
239+
assert os.path.exists(args.input), f"Input directory {args.input} does not exist."
287240
preprocess_data(args)
288241

289242

conf/rbpf_both.toml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# RBPF with both gravity and magnetic anomaly measurements
2+
input = "data/input"
3+
output = "data/output/rbpf/both"
4+
mode = "particle-filter"
5+
seed = 42
6+
generate_plot = true
7+
parallel = true
8+
9+
10+
[logging]
11+
level = "info"
12+
file = "log/rbpf_both.log"
13+
14+
[particle_filter]
15+
num_particles = 1000
16+
position_init_std_m = [10.0, 10.0, 5.0]
17+
velocity_init_std_mps = 1.0
18+
attitude_init_std_rad = 0.1
19+
position_process_noise_std_m = [1.0, 1.0, 1.0]
20+
velocity_process_noise_std_mps = 1e-3
21+
attitude_process_noise_std_rad = 0.01
22+
geo_bias_init_std = 1.0
23+
geo_bias_process_noise_std = 1e-3
24+
zero_vertical_velocity = true
25+
zero_vertical_velocity_std_mps = 0.1
26+
27+
[geophysical]
28+
# Gravity anomaly measurements
29+
gravity_resolution = "one_minute"
30+
gravity_bias = 0.0
31+
gravity_noise_std = 100.0
32+
33+
# Magnetic anomaly measurements
34+
magnetic_resolution = "two_minutes"
35+
magnetic_bias = 0.0
36+
magnetic_noise_std = 150.0
37+
38+
# Measurement frequency (seconds)
39+
geo_frequency_s = 1.0
40+
41+
[gnss_degradation]
42+
seed = 42
43+
44+
[gnss_degradation.scheduler]
45+
kind = "fixed_interval"
46+
interval_s = 60.0
47+
phase_s = 0.0
48+
49+
[gnss_degradation.fault]
50+
kind = "degraded"
51+
rho_pos = 0.99
52+
sigma_pos_m = 15.0
53+
rho_vel = 0.95
54+
sigma_vel_mps = 5.0
55+
r_scale = 15.0

conf/rbpf_degraded.toml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
input = "data/input"
2+
output = "data/output/rbpf/degraded"
3+
mode = "particle-filter"
4+
seed = 42
5+
generate_plot = true
6+
parallel = true
7+
8+
[logging]
9+
level = "info"
10+
file = "log/rbpf_degraded.log"
11+
12+
[particle_filter]
13+
num_particles = 1000
14+
position_init_std_m = [10.0, 10.0, 5.0]
15+
velocity_init_std_mps = 1.0
16+
attitude_init_std_rad = 0.1
17+
position_process_noise_std_m = [1.0, 1.0, 1.0]
18+
velocity_process_noise_std_mps = 1e-3
19+
attitude_process_noise_std_rad = 0.01
20+
geo_bias_init_std = 1.0
21+
geo_bias_process_noise_std = 1e-3
22+
zero_vertical_velocity = true
23+
zero_vertical_velocity_std_mps = 0.1
24+
25+
[gnss_degradation]
26+
seed = 42
27+
28+
[gnss_degradation.scheduler]
29+
kind = "fixed_interval"
30+
interval_s = 60.0
31+
phase_s = 0.0
32+
33+
[gnss_degradation.fault]
34+
kind = "degraded"
35+
rho_pos = 0.99
36+
sigma_pos_m = 15.0
37+
rho_vel = 0.95
38+
sigma_vel_mps = 5.0
39+
r_scale = 15.0

conf/rbpf_grav.toml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# RBPF with gravity anomaly measurements only
2+
input = "data/input"
3+
output = "data/output/rbpf/grav"
4+
mode = "particle-filter"
5+
seed = 42
6+
generate_plot = true
7+
parallel = true
8+
9+
[logging]
10+
level = "info"
11+
file = "log/rbpf_grav.log"
12+
13+
[particle_filter]
14+
num_particles = 1000
15+
position_init_std_m = [10.0, 10.0, 5.0]
16+
velocity_init_std_mps = 1.0
17+
attitude_init_std_rad = 0.1
18+
position_process_noise_std_m = [1.0, 1.0, 1.0]
19+
velocity_process_noise_std_mps = 1e-3
20+
attitude_process_noise_std_rad = 0.01
21+
geo_bias_init_std = 1.0
22+
geo_bias_process_noise_std = 1e-3
23+
zero_vertical_velocity = true
24+
zero_vertical_velocity_std_mps = 0.1
25+
26+
[geophysical]
27+
# Gravity anomaly measurements
28+
gravity_resolution = "one_minute"
29+
gravity_bias = 0.0
30+
gravity_noise_std = 100.0
31+
# gravity_map_file will be auto-detected if not specified
32+
33+
# Magnetic anomaly disabled for this config
34+
# magnetic_resolution = ""
35+
# magnetic_bias = 0.0
36+
# magnetic_noise_std = 150.0
37+
38+
# Measurement frequency (seconds)
39+
geo_frequency_s = 1.0
40+
41+
[gnss_degradation]
42+
seed = 42
43+
44+
[gnss_degradation.scheduler]
45+
kind = "fixed_interval"
46+
interval_s = 60.0
47+
phase_s = 0.0
48+
49+
[gnss_degradation.fault]
50+
kind = "degraded"
51+
rho_pos = 0.99
52+
sigma_pos_m = 15.0
53+
rho_vel = 0.95
54+
sigma_vel_mps = 5.0
55+
r_scale = 15.0

0 commit comments

Comments
 (0)