Skip to content

Commit 56d70cb

Browse files
authored
Merge pull request #132 from luigigisolfi/fix/mpc_examples
Updated mpc examples due to Tudatpy's PR #604
2 parents 1eaba98 + f2ba1a1 commit 56d70cb

File tree

2 files changed

+107
-99
lines changed

2 files changed

+107
-99
lines changed

estimation/estimation_with_mpc.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
# 1 month time buffer used to avoid interpolation errors:
8080
time_buffer = 1 * 31 * 86400.0
8181

82+
8283
# define the frame origin and orientation.
8384
global_frame_origin = "SSB"
8485
global_frame_orientation = "J2000"
@@ -111,9 +112,6 @@
111112
epoch_end=observations_end,
112113
)
113114

114-
batch.summary()
115-
116-
117115
"""
118116
Other than **Earth-based telescopes**, our batch also includes observations from **space telescopes**.
119117
Let's check that out.
@@ -138,7 +136,6 @@
138136
print("\nInitial and Final Observations by WISE:")
139137
print(obs_by_WISE)
140138

141-
142139
"""
143140
While the observations from space telescopes appear to be useful, including them requires setting up the dynamics for the spacecraft, which is too advanced for this tutorial. Space-based observations will therefore be excluded later on in this example.
144141
@@ -286,7 +283,6 @@
286283

287284
# Create numerical integrator settings
288285
integrator_settings = propagation_setup.integrator.runge_kutta_variable_step_size(
289-
epoch_start_buffer,
290286
timestep_global,
291287
propagation_setup.integrator.CoefficientSets.rkf_78,
292288
timestep_global,
@@ -861,4 +857,4 @@
861857
If you wanna get more hands-on experience, consider rerunning the script for some other object by changing the `target_mpc_code` variable and seeing how the results change.
862858
"""
863859

864-
plt.show()
860+
plt.show()

estimation/improved_estimation_with_mpc.py

Lines changed: 105 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@
1515

1616
# Tudat imports for propagation and estimation
1717
from tudatpy.interface import spice
18-
from tudatpy.dynamics import environment_setup, parameters_setup, parameters, propagation, propagation_setup
18+
from tudatpy.dynamics import environment_setup, parameters_setup, propagation_setup
1919
from tudatpy import estimation
20-
from tudatpy.estimation import observable_models_setup,observable_models, observations_setup, observations, estimation_analysis
20+
from tudatpy.estimation import observable_models_setup, estimation_analysis
2121
from tudatpy.constants import GRAVITATIONAL_CONSTANT
2222
from tudatpy.astro.frame_conversion import inertial_to_rsw_rotation_matrix
23-
from tudatpy.astro.time_representation import DateTime
24-
from tudatpy.astro import element_conversion
25-
26-
# import MPC, SBDB and Horizons interface
23+
import matplotlib.gridspec as gridspec
2724
from tudatpy.data.mpc import BatchMPC
2825
from tudatpy.data.horizons import HorizonsQuery
2926
from tudatpy.data.sbdb import SBDBquery
@@ -501,7 +498,6 @@
501498

502499
# Create numerical integrator settings
503500
integrator_settings = propagation_setup.integrator.runge_kutta_variable_step_size(
504-
time_representation.Time(epoch_start_buffer),
505501
time_representation.Time(timestep_global),
506502
propagation_setup.integrator.CoefficientSets.rkf_78,
507503
time_representation.Time(timestep_global),
@@ -1022,58 +1018,66 @@ def plot_cartesian_single(
10221018
return fig, ax
10231019

10241020

1025-
1026-
import matplotlib.gridspec as gridspec
1027-
from matplotlib.lines import Line2D
1028-
1029-
10301021
def plot_star_catalog_corrections(
10311022
mpc_batch: BatchMPC, include_satellites: bool = True, figsize=(9, 6)
10321023
):
1033-
1024+
"""
1025+
Plot star catalog RA/DEC corrections per observation with optional satellite observations.
1026+
1027+
Parameters
1028+
----------
1029+
mpc_batch : BatchMPC
1030+
Batch containing table with 'epochJ2000secondsTDB', 'corr_RA_EFCC18', 'corr_DEC_EFCC18', 'note2'.
1031+
include_satellites : bool, optional
1032+
Whether to include satellite observations, by default True
1033+
figsize : tuple, optional
1034+
Figure size, by default (9, 6)
1035+
1036+
Returns
1037+
-------
1038+
fig, ax1, ax2, hist1, hist2
1039+
Figure, RA axis, Dec axis, RA histogram, Dec histogram
1040+
"""
1041+
1042+
# Extract data
10341043
if include_satellites:
1035-
epochs = mpc_batch.table["epochUTC"].values
1044+
epochsUTC = mpc_batch.table["epochUTC"].values
10361045
ra_corrections = mpc_batch.table["corr_RA_EFCC18"].values
10371046
dec_corrections = mpc_batch.table["corr_DEC_EFCC18"].values
10381047
else:
1039-
epochs = mpc_batch.table[mpc_batch.table["note2"] != "S"]["epochUTC"].values
1040-
ra_corrections = mpc_batch.table[mpc_batch.table["note2"] != "S"][
1041-
"corr_RA_EFCC18"
1042-
].values
1043-
dec_corrections = mpc_batch.table[mpc_batch.table["note2"] != "S"][
1044-
"corr_DEC_EFCC18"
1045-
].values
1048+
mask = mpc_batch.table["note2"] != "S"
1049+
epochsUTC = mpc_batch.table["epochUTC"][mask].values
1050+
ra_corrections = mpc_batch.table["corr_RA_EFCC18"][mask].values
1051+
dec_corrections = mpc_batch.table["corr_DEC_EFCC18"][mask].values
1052+
1053+
# Convert epochs to ISO strings (YYYY-MM-DD)
1054+
iso_labels = [DateTime.from_epoch(t).to_iso_string()[:10] for t in epochsUTC]
10461055

1056+
# Numeric x-axis positions
1057+
x = np.arange(len(epochsUTC))
1058+
1059+
# Set up figure and GridSpec
10471060
fig = plt.figure(figsize=figsize, constrained_layout=True)
10481061
gs = gridspec.GridSpec(
10491062
2, 2, width_ratios=[4, 1], height_ratios=[1, 1], hspace=0.1, wspace=0.05
10501063
)
10511064

1052-
# Scatter plots
10531065
ax1 = fig.add_subplot(gs[0, 0])
10541066
ax2 = fig.add_subplot(gs[1, 0], sharex=ax1)
10551067

1056-
# Histograms on the right
10571068
hist1 = fig.add_subplot(gs[0, 1], sharey=ax1)
10581069
hist2 = fig.add_subplot(gs[1, 1], sharey=ax2)
10591070

10601071
# Scatter plots
1061-
ax1.scatter(epochs, ra_corrections, color="tab:blue", marker="+")
1062-
ax2.scatter(epochs, dec_corrections, color="tab:orange", marker="+")
1072+
ax1.scatter(x, ra_corrections, color="tab:blue", marker="+")
1073+
ax2.scatter(x, dec_corrections, color="tab:orange", marker="+")
10631074

10641075
# Histograms
1065-
hist1.hist(
1066-
ra_corrections, bins=50, orientation="horizontal", color="tab:blue", alpha=0.6
1067-
)
1068-
hist2.hist(
1069-
dec_corrections,
1070-
bins=50,
1071-
orientation="horizontal",
1072-
color="tab:orange",
1073-
alpha=0.6,
1074-
)
1076+
hist1.hist(ra_corrections, bins=50, orientation="horizontal", color="tab:blue", alpha=0.6)
1077+
hist2.hist(dec_corrections, bins=50, orientation="horizontal", color="tab:orange", alpha=0.6)
10751078
hist2.set_xlabel("Occurrences")
10761079

1080+
# Axis labels
10771081
ax1.set_ylabel(r"Right Ascension $[rad]$")
10781082
ax2.set_ylabel(r"Declination $[rad]$")
10791083
ax2.set_xlabel("Epoch [UTC]")
@@ -1082,83 +1086,93 @@ def plot_star_catalog_corrections(
10821086
ax1.tick_params(labelbottom=False)
10831087
hist1.tick_params(labelleft=False, labelbottom=False)
10841088
hist2.tick_params(labelleft=False)
1089+
1090+
# X-axis ISO tick labels
1091+
tick_spacing = max(1, len(x)//10) # show ~10 ticks max
1092+
ax2.set_xticks(x[::tick_spacing])
1093+
ax2.set_xticklabels([iso_labels[i] for i in range(0, len(x), tick_spacing)], rotation=45, ha='right')
1094+
10851095
fig.suptitle("Star Catalog Corrections (per observation)")
10861096

10871097
return fig, ax1, ax2, hist1, hist2
10881098

1089-
10901099
def plot_observation_weights(
1091-
mpc_batch: BatchMPC, include_satellites: bool = True, figsize=(9, 4)
1100+
mpc_batch, include_satellites: bool = True, figsize=(9, 4)
10921101
):
1102+
"""
1103+
Plot observation weights per RA/DEC pair with optional satellite observations.
1104+
1105+
Parameters
1106+
----------
1107+
mpc_batch : BatchMPC
1108+
Batch containing table with 'epochUTC', 'weight', and 'note2'.
1109+
include_satellites : bool, optional
1110+
Whether to include satellite observations, by default True
1111+
figsize : tuple, optional
1112+
Figure size, by default (9, 4)
1113+
1114+
Returns
1115+
-------
1116+
fig, ax, hist_ax
1117+
Figure, main scatter axis, histogram axis
1118+
"""
1119+
1120+
# Extract regular and satellite observations
1121+
reg_mask = mpc_batch.table["note2"] != "S"
1122+
sat_mask = mpc_batch.table["note2"] == "S"
1123+
1124+
reg_epochsUTC = mpc_batch.table["epochUTC"][reg_mask].values
1125+
reg_weights = mpc_batch.table["weight"][reg_mask].values
1126+
reg_iso = [DateTime.from_epoch(t).to_iso_string()[:10] for t in reg_epochsUTC] # YYYY-MM-DD
10931127

1094-
sat_epochs = mpc_batch.table[mpc_batch.table["note2"] == "S"]["epochUTC"].values
1095-
sat_weights = mpc_batch.table[mpc_batch.table["note2"] == "S"]["weight"].values
1128+
if include_satellites:
1129+
sat_epochsUTC = mpc_batch.table["epochUTC"][sat_mask].values
1130+
sat_weights = mpc_batch.table["weight"][sat_mask].values
1131+
sat_iso = [DateTime.from_epoch(t).to_iso_string()[:10] for t in sat_epochsUTC]
10961132

1097-
reg_epochs = mpc_batch.table[mpc_batch.table["note2"] != "S"]["epochUTC"].values
1098-
reg_weights = mpc_batch.table[mpc_batch.table["note2"] != "S"]["weight"].values
1133+
# Numeric x-axis for plotting
1134+
reg_x = np.arange(len(reg_epochsUTC))
1135+
if include_satellites:
1136+
sat_x = np.arange(len(sat_epochsUTC)) + len(reg_x) # offset to avoid overlap
10991137

1100-
# Set up figure and GridSpec for scatter + histogram
1138+
# Set up figure and GridSpec
11011139
fig = plt.figure(figsize=figsize)
11021140
gs = gridspec.GridSpec(1, 2, width_ratios=[4, 1], wspace=0.05)
1103-
11041141
ax = fig.add_subplot(gs[0])
1105-
ax.scatter(reg_epochs, reg_weights, marker="+")
1106-
1107-
if include_satellites:
1108-
ax.scatter(
1109-
sat_epochs, sat_weights, marker="+", color="tab:red", label="Satellite"
1110-
)
1111-
11121142
hist_ax = fig.add_subplot(gs[1])
11131143

1114-
hist_ranges = (
1115-
min(reg_weights.min(), sat_weights.min()),
1116-
max(reg_weights.max(), sat_weights.max()),
1117-
)
1118-
1119-
hist_ax.hist(
1120-
reg_weights,
1121-
range=hist_ranges if include_satellites else None,
1122-
bins=30,
1123-
orientation="horizontal",
1124-
color="tab:blue",
1125-
alpha=0.6,
1126-
log=False,
1127-
)
1128-
1144+
# Scatter plots
1145+
ax.scatter(reg_x, reg_weights, marker="+", color="tab:blue", label="Regular")
11291146
if include_satellites:
1130-
hist_ax.hist(
1131-
sat_weights,
1132-
range=hist_ranges,
1133-
bins=30,
1134-
orientation="horizontal",
1135-
color="tab:red",
1136-
alpha=0.6,
1137-
log=False,
1138-
)
1147+
ax.scatter(sat_x, sat_weights, marker="+", color="tab:red", label="Satellite")
1148+
1149+
# X-axis ticks and labels
1150+
all_x = np.concatenate([reg_x, sat_x]) if include_satellites else reg_x
1151+
all_iso = np.concatenate([reg_iso, sat_iso]) if include_satellites else reg_iso
1152+
1153+
tick_spacing = max(1, len(all_x) // 10) # show ~10 ticks max
1154+
ax.set_xticks(all_x[::tick_spacing])
1155+
ax.set_xticklabels(all_iso[::tick_spacing], rotation=45, ha='right')
1156+
1157+
# Histogram
1158+
hist_ranges = (min(reg_weights.min(), sat_weights.min() if include_satellites else reg_weights.min()),
1159+
max(reg_weights.max(), sat_weights.max() if include_satellites else reg_weights.max()))
1160+
hist_ax.hist(reg_weights, bins=30,
1161+
range=hist_ranges if include_satellites else None,
1162+
orientation="horizontal", color="tab:blue", alpha=0.6, log=False)
1163+
if include_satellites:
1164+
hist_ax.hist(sat_weights, bins=30, range=hist_ranges,
1165+
orientation="horizontal", color="tab:red", alpha=0.6, log=False)
11391166

11401167
hist_ax.tick_params(labelleft=False)
11411168
hist_ax.grid(False)
11421169
hist_ax.set_xlabel("Occurrences")
11431170

1171+
# Legends
11441172
if include_satellites:
11451173
legend_elements = [
1146-
Line2D(
1147-
[0],
1148-
[0],
1149-
marker="+",
1150-
color="tab:blue",
1151-
linestyle="None",
1152-
label="Regular",
1153-
),
1154-
Line2D(
1155-
[0],
1156-
[0],
1157-
marker="+",
1158-
color="tab:red",
1159-
linestyle="None",
1160-
label="Satellite",
1161-
),
1174+
Line2D([0], [0], marker="+", color="tab:blue", linestyle="None", label="Regular"),
1175+
Line2D([0], [0], marker="+", color="tab:red", linestyle="None", label="Satellite"),
11621176
]
11631177
ax.legend(handles=legend_elements, title="Observatory Type")
11641178

@@ -1169,7 +1183,6 @@ def plot_observation_weights(
11691183

11701184
return fig, ax, hist_ax
11711185

1172-
11731186
"""
11741187
## Comparison Round 1: Acceleration models
11751188
With our core estimation and plotting functions ready, we can now perform a comparison of the three acceleration models. For this first comparison, we turn the remaining options: including satelite data, star catalog corrections and observations weights off. The setups can be described as follows:
@@ -1751,7 +1764,6 @@ def plot_observation_weights(
17511764
fig = batch.plot_observations_sky(figsize=(6, 4))
17521765
fig.suptitle(f"{batch.size} observations for {target_name} in the sky")
17531766
fig.axes[0].get_legend().remove()
1754-
17551767
# fig.savefig("Eros_observations_sky.pdf")
17561768

17571769

@@ -1902,4 +1914,4 @@ def plot_observation_weights(
19021914
# fig.savefig("Eros_estimation_error_overview.pdf", dpi=400)
19031915

19041916

1905-
plt.show()
1917+
plt.show()

0 commit comments

Comments
 (0)