f2m package

Subpackages

Submodules

f2m.ampl_interface module

class f2m.ampl_interface.AMPLModel(ampl_mod_path: str, auto_reset: bool = True)

Bases: object

Code from https://gitlab.com/EuropeanSpaceAgency/trajectory-to-events

checkSolved()
coerceSpecialVariable(var_name)
display(*args)
getAMPL()
getConstraint(name)
getConstraints()
getObjective(name)
getObjectiveShape(obj_name)
getObjectiveValue(obj_name)
getObjectiveValues(name_list=None)
getObjectives()
getParamShape(param_name)
getParameter(name)
getParameterValue(param_name)
getParameterValues(name_list=None)
getParameters()
getSetSize(set_name)
getSolutionValues(name_list=None)
getSpecialVariable(name_list=None)
getVarShape(var_name)
getVariable(name)
getVariableValue(var_name)
getVariableValues(name_list=None)
getVariables()
items()
keys()
reset()
setParameterValue(param_name, param_value)
setParameterValues(param_dict={}, **kwargs)
showAMPLOutput()
solve(hide_solver_output=True)
solveAsync(callback, hide_solver_output=True)
f2m.ampl_interface.extract_ocp_params(p)

[DEPRECATED] Extract user-defined parameters for the OCP

f2m.ampl_interface.sample_ocp_params(traj_config, parameter)

[DEPRECATED] Sample specified trajectory parameter range to generate new OCP

Parameters:
  • traj_config (dict) – Trajectory configuration

  • parameter (str) – Parameter to sample

Returns:

f2m.camera_noise module

f2m.camera_noise.add_gaussian_noise(image: ndarray, std: float = 6, cosmic_rays_outliers: bool = True, framerate: int = 4) ndarray

f2m.motion_field module

f2m.motion_field.least_squares_estimation(vars: dict[str, float], pars: dict[str, float], points: ndarray, flow: ndarray, focal_length: float, framerate: int, image_width: int, target_radius: int | None = None, stable_depth: bool = False, least_squares_kwargs: dict = {}) OptimizeResult

Estimate the spacecraft state using least squares optimization over motion field equations. If the system is linear, linear least squares will be used, otheriwse a non-linear version will be used.

Parameters:
  • vars (dict[str, float]) – variables to optimize. The keys are the variable names and the values are the initial guesses.

  • pars (dict[str, float]) – parameters with known values

  • points (np.ndarray) – points in the image plane, shape (n, 2)

  • flow (np.ndarray) – optical flow vectors corresponding to points, shape (n, 2)

  • focal_length (float) – camera focal length related to the image width

  • framerate (int) – camera framerate

  • image_width (int) – image width, in pixels

  • target_radius (int, optional) – radius of the target in meters. Defaults to None (planar case)

  • stable_depth (bool, optional) – If True, replaces NaN depth values with the maximum depth

  • least_squares_kwargs (dict, optional) – additional arguments to pass to scipy.optimize.least_squares. Defaults to {}.

Returns:

optimization result. The x attribute contains the optimized variables

Return type:

OptimizeResult

f2m.motion_field.motion_field(sx: float | ndarray, sy: float | ndarray, state: ndarray, fx: float = 1, fy: float = 1, target_radius: float | None = None, alpha: float | None = None, beta: float | None = None, stable_depth: bool = False) tuple[ndarray, ndarray]

Computes the motion field assuming a planar or spherical target surface. If target_radius is None, a planar surface is assumed; otherwise, the surface is treated as spherical.

A necessary step to compute the depth map is estimating the surface inclination relative to the camera, which is represented by the components of the unit vector orthogonal to the surface at the center of the image: (alpha, beta, gamma). By default, these values are inferred from the spacecraft state. Specifically, the orientation angles (phi, theta, psi) are used in the planar case, and in the spherical case, the rangefinder measurement (rho) and target_radius are also required.

Alternatively, users can explicitly provide alpha and beta (gamma is inferred from them to complete the unit vector). This is useful when surface geometry is known or constant.

If stable_depth is True, any NaN values in the computed depth map will be replaced by the maximum depth value found in the map.

Parameters:
  • sx (float or np.ndarray) – Horizontal image coordinate(s) on the sensor.

  • sy (float or np.ndarray) – Vertical image coordinate(s) on the sensor.

  • state (np.ndarray) – Spacecraft state vector [x, y, z, vx, vy, vz, phi, theta, psi, p, q, r, rho]. - x and y are ignored and can be set to any value. - Planar case (target_radius is None): use either theta or z (rho overrides z if present). - Spherical case (target_radius is not None): requires rho; z is ignored.

  • fx (float, optional) – Horizontal focal length. Defaults to 1.

  • fy (float, optional) – Vertical focal length. Defaults to 1.

  • target_radius (float, optional) – Radius of the target in meters. If None, a planar target is assumed.

  • alpha (float, optional) – X-component of the surface normal vector.

  • beta (float, optional) – Y-component of the surface normal vector.

  • stable_depth (bool, optional) – If True, replaces NaN depth values with the maximum depth. Defaults to False.

Returns:

The horizontal (u) and vertical (v) components of the motion field.

Return type:

tuple

f2m.motion_field.ransac_estimation(vars: dict[str, float], pars: dict[str, float], points: ndarray, flow: ndarray, focal_length: float, framerate: int, image_width: int, target_radius: int | None = None, least_squares_kwargs: dict = {}, min_samples: int = 3, residual_threshold: float = 0.1, consensus_threshold: float = 0.75, max_trials: int = 100) dict[str, Any]

Estimate the spacecraft state using least squares optimization over motion field equations with RANSAC to handle outliers

Parameters:
  • vars (dict[str, float]) – variables to optimize. The keys are the variable names and the values are the initial guesses.

  • pars (dict[str, float]) – parameters with known values

  • points (np.ndarray) – points in the image plane, shape (n, 2)

  • flow (np.ndarray) – optical flow vectors corresponding to points, shape (n, 2)

  • focal_length (float) – camera focal length related to the image width

  • framerate (int) – camera framerate

  • image_width (int) – image width, in pixels

  • target_radius (int, optional) – radius of the target in meters. Defaults to None (planar case).

  • least_squares_kwargs (dict, optional) – additional arguments to pass to scipy.optimize.least_squares. Defaults to {}.

  • min_samples (int, optional) – number of samples to fit the model. Defaults to 3.

  • residual_threshold (float, optional) – maximum residual to consider an inlier. Defaults to 0.1.

  • consensus_threshold (float, optional) – minimum ratio of inliers to consider the model valid. Defaults to 0.75.

  • max_trials (int, optional) – maximum number of iterations. Defaults to 100.

Returns:

dictionary with the following keys:
  • result: optimized variables

  • mask: boolean mask indicating inliers

  • iterations: number of iterations

  • converged: whether the algorithm converged

  • consensus: ratio of inliers

Return type:

dict[str, Any]

f2m.optical_flow module

class f2m.optical_flow.LKOpticalFlowEstimator(feature_params: dict = None, lk_params: dict = None, do_histogram_equalization: bool = False, mask: ndarray = None)

Bases: object

forward(img0: ndarray, img1: ndarray, features: ndarray = None, verbose: bool = False) tuple[ndarray, ndarray]

Compute the optical flow between two frames

Parameters:
  • img0 (np.ndarray) – [3]xHxW cv2 image representing the first frame

  • img1 (np.ndarray) – [3]xHxW cv2 image representing the second frame

  • features (np.ndarray) – Nx2 array representing already tracked feature coordinates (Optional)

  • verbose (bool) – Debugging mode flag

Returns:

Nx2 array representing features coordinates and Nx2 array representing their flow

Return type:

tuple[np.ndarray, np.ndarray]

update_tracked_points(tracked_points: ndarray, image: ndarray, verbose: bool = False) ndarray

Detects, filters then adds additional points to the list of tracked points

Parameters:
  • tracked_points (np.ndarray) – Nx2 array of position of tracked points in next frame

  • image (np.ndarray) – [1]xHxW cv2 image representing the grayscale frame of the image

  • verbose (bool) – Debugging mode flag

Returns:

Nx2 array of position of tracked points for the next frame of OF

Return type:

np.ndarray

f2m.optical_flow.draw_optical_flow(img: ndarray, points: ndarray, flow: ndarray, arrow_scale: float = 3.0, arrow_thickness: int = 2, color: tuple[int, int, int] = (0, 255, 0)) ndarray

Draw representation of optical flow

Parameters:
  • img (np.ndarray) – cv2 image

  • points (np.ndarray) – Nx2 array representing features coordinates

  • flow (np.ndarray) – Nx2 array representing their flow

  • arrow_scale (float, optional) – scale optical flow arrows for better visualization. Defaults to 3.

  • arrow_thickness (int, optional) – thickness of the arrows. Defaults to 2.

  • (tuple[int (color) – color of arrows)

  • int – color of arrows)

  • int] – color of arrows)

Returns:

[2]xHxW representation of the flow

Return type:

np.ndarray

f2m.optical_flow.generate_optical_flow_video(frames: ndarray, of_estimator: LKOpticalFlowEstimator, output_path: Path, arrow_scale: float = 3.0, arrow_thickness: int = 2, frame_interval: int = 1, fps: int = 4, verbose: bool = False, save_frames: bool = False) None

Generate the optical flow video between all frames

Parameters:
  • frames (np.ndarray) – cv2 arrays representing video frames in BGR

  • of_estimator (LKOpticalFlowEstimator) – optical flow estimator

  • output_dir (Path) – where to save output frames

  • arrow_scale (float, optional) – scale optical flow arrows for better visualization. Defaults to 3.

  • arrow_thickness (int, optional) – thickness of the arrows. Defaults to 2.

  • frame_interval (int, optional) – Detect new points to track every frame_interval frames. Defaults to 1.

  • fps (int) – framerate in the output video. Defaults to 4

  • verbose (bool) – Debugging mode flag

  • save_frames (bool) – Save frames to disk

f2m.symbolic module

f2m.symbolic.H_numerical(phi, theta, rho, R)

Created with lambdify. Signature:

func(phi, theta, rho, R)

Expression:

-R + R*sin(acos(cos(phi)*cos(theta)) +…

Source code:

def _lambdifygenerated(phi, theta, rho, R):

return -R + R*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))/sqrt(-cos(phi)**2*cos(theta)**2 + 1)

Imported modules:

f2m.symbolic.alpha_numerical(phi, theta, rho, R)

Created with lambdify. Signature:

func(phi, theta, rho, R)

Expression:

-R*sin(theta)*sin(acos(cos(phi)*cos(theta)) +…

Source code:

def _lambdifygenerated(phi, theta, rho, R):

return -R*sin(theta)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))/(sqrt(-cos(phi)**2*cos(theta)**2 + 1)*sqrt(abs(R*sin(theta)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R)))**2/abs(sqrt(-cos(phi)**2*cos(theta)**2 + 1))**2 + abs(R*sin(phi)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(theta))**2/abs(sqrt(-cos(phi)**2*cos(theta)**2 + 1))**2 + abs(R*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(phi)*cos(theta)/sqrt(-cos(phi)**2*cos(theta)**2 + 1) - rho)**2))

Imported modules:

f2m.symbolic.beta_numerical(phi, theta, rho, R)

Created with lambdify. Signature:

func(phi, theta, rho, R)

Expression:

R*sin(phi)*sin(acos(cos(phi)*cos(theta)) +…

Source code:

def _lambdifygenerated(phi, theta, rho, R):

return R*sin(phi)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(theta)/(sqrt(-cos(phi)**2*cos(theta)**2 + 1)*sqrt(abs(R*sin(theta)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R)))**2/abs(sqrt(-cos(phi)**2*cos(theta)**2 + 1))**2 + abs(R*sin(phi)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(theta))**2/abs(sqrt(-cos(phi)**2*cos(theta)**2 + 1))**2 + abs(R*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(phi)*cos(theta)/sqrt(-cos(phi)**2*cos(theta)**2 + 1) - rho)**2))

Imported modules:

f2m.symbolic.gamma_numerical(phi, theta, rho, R)

Created with lambdify. Signature:

func(phi, theta, rho, R)

Expression:

(-R*sin(acos(cos(phi)*cos(theta)) +…

Source code:

def _lambdifygenerated(phi, theta, rho, R):

return (-R*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(phi)*cos(theta)/sqrt(-cos(phi)**2*cos(theta)**2 + 1) + rho)/sqrt(abs(R*sin(theta)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R)))**2/abs(sqrt(-cos(phi)**2*cos(theta)**2 + 1))**2 + abs(R*sin(phi)*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(theta))**2/abs(sqrt(-cos(phi)**2*cos(theta)**2 + 1))**2 + abs(R*sin(arccos(cos(phi)*cos(theta)) + arcsin(rho*sqrt(-cos(phi)**2*cos(theta)**2 + 1)/R))*cos(phi)*cos(theta)/sqrt(-cos(phi)**2*cos(theta)**2 + 1) - rho)**2)

Imported modules:

f2m.symbolic.get_alpha(phi_value: float, theta_value: float, rho_value: float, R_value: float) float

Calculate alpha based on the given parameters.

Parameters:
  • phi_value (float) – The value of phi.

  • theta_value (float) – The value of theta.

  • rho_value (float) – The value of rho.

  • R_value (float) – The value of R.

Returns:

The calculated alpha.

Return type:

float

f2m.symbolic.get_beta(phi_value: float, theta_value: float, rho_value: float, R_value: float) float

Calculate beta based on the given parameters.

Parameters:
  • phi_value (float) – The value of phi.

  • theta_value (float) – The value of theta.

  • rho_value (float) – The value of rho.

  • R_value (float) – The value of R.

Returns:

The calculated beta.

Return type:

float

f2m.symbolic.get_gamma(phi_value: float, theta_value: float, rho_value: float, R_value: float) float

Calculate gamma based on the given parameters.

Parameters:
  • phi_value (float) – The value of phi.

  • theta_value (float) – The value of theta.

  • rho_value (float) – The value of rho.

  • R_value (float) – The value of R.

Returns:

The calculated gamma.

Return type:

float

f2m.symbolic.get_h(phi_value: float, theta_value: float, rho_value: float, R_value: float) float

Calculate h based on the given parameters.

Parameters:
  • phi_value (float) – The value of phi.

  • theta_value (float) – The value of theta.

  • rho_value (float) – The value of rho.

  • R_value (float) – The value of R.

Returns:

The calculated h.

Return type:

float

f2m.symbolic.is_singularity(phi_value: float, theta_value: float, epsilon: float = 1e-07) bool

Return True if abs value of phi and theta are lower than epsilon.

Parameters:
  • phi_value (float) – the value of phi.

  • theta_value (float) – the value of theta.

  • epsilon (float, optional) – threshold value to decide if we are in a singluarity. Defaults to 1e-7.

Returns:

True if we are in a singularity, False otherwise.

Return type:

bool

f2m.trajectory module

f2m.trajectory.AMPL_to_PANGU_conversion(trajectory: ndarray) ndarray

Convert an AMPL trajectory to the PANGU coordinate convention.

The AMPL body and PANGU camera frames are assumed to be physically aligned. Angular velocity therefore remains expressed in this common body/camera frame. Zero AMPL attitude becomes PANGU’s nadir-pointing attitude.

Parameters:

trajectory – Trajectory with shape (N, 13), containing [x, y, z, vx, vy, vz, phi, theta, psi, p, q, r, m]. Angles and angular velocities are given in radians and radians per second, respectively.

Returns:

Converted trajectory with shape (N, 14), containing

[x, y, z, vx, vy, vz, qw, qx, qy, qz, p, q, r, m]. Position and velocity are expressed in the PANGU frame, attitude is a scalar-first camera-to-PANGU quaternion, and angular velocity is expressed in the aligned body/camera frame.

f2m.trajectory.PANGU_to_AMPL_conversion(trajectory: ndarray) ndarray

Convert a PANGU trajectory to the AMPL coordinate convention.

The PANGU camera and AMPL body frames are assumed to be physically aligned. Angular velocity therefore remains expressed in this common camera/body frame.

Parameters:

trajectory – Trajectory with shape (N, 14), containing [x, y, z, vx, vy, vz, qw, qx, qy, qz, p, q, r, m]. Attitude is a scalar-first camera-to-PANGU quaternion.

Returns:

Converted trajectory with shape (N, 13), containing

[x, y, z, vx, vy, vz, phi, theta, psi, p, q, r, m]. Position and velocity are expressed in the AMPL frame, attitude is represented by ZYX Euler angles in radians, and angular velocity is expressed in the aligned camera/body frame.

f2m.trajectory.apply_camera_tilt(trajectory: ndarray, camera_tilt: Rotation, convention: str) ndarray

Apply a fixed local camera tilt to a trajectory.

The camera tilt maps vectors from the tilted camera frame to the original body/camera frame. It is post-multiplied with each attitude. Angular velocity is re-expressed in the tilted camera frame, while position, linear velocity, and mass remain unchanged.

Parameters:
  • trajectory – Trajectory in AMPL shape (N, 13) or PANGU shape (N, 14).

  • camera_tilt – Rotation from the tilted camera frame to the original body/camera frame.

  • convention – Trajectory convention, either “AMPL” or “PANGU”.

Returns:

A copy of the trajectory with the camera tilt applied. AMPL attitudes remain ZYX Euler angles in [phi, theta, psi] order, while PANGU attitudes remain scalar-first quaternions in [qw, qx, qy, qz] order.

f2m.trajectory.convert_state_history_inertial_to_body_frame(traj_dir: str, return_ranges=False)

Converts an inertial state history to a body frame one. The body frame is defined in get_axes_from_state().

Parameters:

traj_dir (str) – The directory from which to load the trajectory.

Returns:

The time and state history in the body frame.

Return type:

tuple(np.ndarray, np.ndarray)

f2m.trajectory.generate_hohmann_trajectory(output_dir: Path, init_position, alt_p=None, alt_a=None, r_moon=1737400.0, mu_moon: float = 4902800118000.0, n_points=200, save_traj=True, as_array=False, fps: int = 4, field_of_view: int = 60, camera_tilt: tuple[float, float, float] | None = None, indices=None) dict | ndarray

Generates a Hohmann transfer trajectory.

Warning! User discretion is advised. This function isn’t validated and therefore is not guaranteed to work for all init_position values (and camera_tilts)

Parameters:
  • output_dir (Path) – Path to the directory where trajectory files should be saved.

  • init_position (array-like) – Initial position vector for the transfer.

  • alt_p (float, optional) – Pericenter altitude in meters.

  • alt_p – Apocenter altitude in meters.

  • period_frac (float, optional) – Fraction of the orbital period to simulate. Defaults to 0.5.

  • r_moon (float, optional) – Radius of the Moon in meters. Defaults to 1737.4e3 m.

  • mu_moon (float, optional) – The gravitational parameter. Defaults to the Moon 4.902800118e12 m**3/s**2.

  • save_traj (bool, optional) – If True, saves the trajectory to a file. Defaults to True.

  • as_array (bool, optional) – If True, returns the trajectory as a NumPy array. Defaults to False.

  • fps (int, optional) – Frames per second for trajectory interpolation. Defaults to 4.

  • field_of_view (int, optional) – Camera field of view in degrees. Defaults to 60.

  • camera_tilt (Optional[tuple[float, float, float]], optional) – Camera tilt angles (X, Y, Z) in radians. Defaults to None.

  • indices (list) – List of indices that represents the indices of the state history that you want to save. Defaults to None.

Returns:

If as_array is True, returns the trajectory as a NumPy array. Otherwise, returns a dictionary of state history.

Return type:

np.ndarray

f2m.trajectory.generate_multiple_trajectories(landing_model_path: Path, parameters_path: Path, output_dir: Path, number_of_trajectories: int, save_continuation: bool = False, save_traj: bool = True, verbose: bool = True, fps: int = 4, camera_tilt: tuple[float, float, float] | None = [0, 0, 0], offset: tuple[float, float, float] = [0, 0, 0], landing_site: tuple[float, float, float] = [-25010, -40149, -3530.85])

[DEPRECATED] Generate a trajectory with.

Parameters:
  • landing_model_path (Path) – Path to the landing model.

  • parameters_path (Path) – Path to the parameters file.

  • output_dir (Path) – Path to the output directory.

  • number_of_trajectories (int) – Number of trajectories to generate.

  • save_continuation (bool, optional) – Save trajectory for each updated parameters. Defaults to False.

  • save_traj (bool, optional) – Save the trajectory for each full set of updated parameters. Defaults to True.

  • verbose (bool, optional) – Verbose flag. Defaults to True.

  • fps (int, optional) – States per second. Defaults to 4.

  • (Optional (camera_tilt) – (float, float, float)): camera tilt angles on X, Y, and Z axes

  • offset (tuple[float, float, float]) – position offset applied to the trajectory

f2m.trajectory.generate_trajectory(landing_model_path: ~pathlib.Path, parameters_path: ~pathlib.Path, output_dir: ~pathlib.Path, verbose: bool = True, fps: int = 4, camera_tilt: ~scipy.spatial.transform._rotation.Rotation = <scipy.spatial.transform._rotation.Rotation object>, landing_site: tuple[float, float, float] = (-25010, -40149, -3530.85), save: bool = True) ndarray

Generate a trajectory with the given parameters, using AMPL and save it in the output directory.

Parameters:
  • landing_model_path (Path) – Path to the landing model.

  • parameters_path (Path) – Path to the parameters file.

  • output_dir (Path) – Path to the output directory.

  • verbose (bool, optional) – Verbose flag. Defaults to True.

  • fps (int, optional) – States per second. Defaults to 4.

  • camera_tilt (Rotation) – camera tilt rotation. Defaults to ventral camera.

  • landing_site (tuple[float, float, float]) – The coordinates to translate the origin to the landing site position. Defaults to (-25010, -40149, -3530.850).

Returns:

The optimized trajectory state history.

Return type:

np.ndarray

f2m.trajectory.get_angular_state_history(orbit: dict, as_array=False, sequence='XYZ')

Computes the angular state history of a spacecraft, assuming the camera points toward the Moon’s center.

Parameters:
  • orbit (dict) – Dictionary of time-mapped position and velocity states.

  • as_array (bool, optional) – If True, returns results as a NumPy array. Defaults to False.

  • sequence (str) – If XYZ (used for ispace traj), then XYZ rotation order is maintained, else if “ZYX” (for hohmann transfer) then ZYX rotation order

Returns:

A dictionary mapping time values to angular states [phi, theta, psi, phi_dot, theta_dot, psi_dot]. or np.ndarray: An array of the angular states [phi, theta, psi, phi_dot, theta_dot, psi_dot].

Return type:

dict

f2m.trajectory.get_hohmann_state_history(alt_p, alt_a, r_body, mu, n_points=100, as_array=False, direction='inbound')

Generate position and velocity history along a Hohmann transfer orbit.

Parameters:
  • alt_p – float Perigee altitude above the central body [m].

  • alt_a – float Apogee altitude above the central body [m].

  • r_body – float Radius of the central body [m].

  • mu – float Gravitational parameter of the central body [m^3/s^2].

  • n_points – int, optional Number of points along the transfer orbit (default is 100).

  • as_array – bool, optional If True, return a NumPy array of shape (n_points, 6) where each row is [x, y, z, vx, vy, vz]. If False, return a dict keyed by time with each value being a 6-element state vector.

Returns:

np.ndarray or dict

The trajectory states along the transfer orbit, either as an array or a dictionary indexed by time.

Notes

  • The orbit is assumed to be planar and elliptical (Hohmann transfer).

  • The z and vz components are always zero due to the planar assumption.

  • The trajectory is computed for half the orbital period, corresponding

to the Hohmann transfer time.

f2m.trajectory.get_landing_site(landing_site: str) tuple[float, float, float]

Get the landing site coordinates from the given string

Parameters:

landing_site (str) – The landing site name

Returns:

The landing site coordinates

Return type:

tuple[float, float, float]

f2m.trajectory.get_period(mu, radius)

Orbital period using Kepler’s third law

f2m.trajectory.get_state_history_from_csv(path)

Read a CSV file containing trajectory state history.

Parameters:

path (str) – Path to the CSV file.

Returns:

Time and state history arrays.

Return type:

tuple

f2m.trajectory.get_velocity_from_position_diff(t, x)

Interpolates velocity from position data using finite differences.

Parameters:
  • t (np.ndarray) – Time data.

  • x (np.ndarray) – Position data.

Returns:

Interpolated velocity data.

Return type:

np.ndarray

f2m.trajectory.interpolate_vector(t, x, dt=1)

Interpolates position data using cubic splines.

Parameters:
  • t (np.ndarray) – Time data.

  • x (np.ndarray) – Position data.

  • dt (int, float) – Time step for interpolation.

Returns:

Interpolated position data.

Return type:

np.ndarray

f2m.trajectory.position_trajectory_above_landing_site(trajectory: ndarray, landing_site: ndarray | tuple[float, float, float]) ndarray

Position a local PANGU trajectory above a selected landing site.

The local trajectory is rigidly rotated so that its +z axis aligns with radial up at the landing site. It is then translated so that its final position lies above the landing site, with the final local z-coordinate interpreted as altitude above the spherical lunar surface.

The reference Moon centre is assumed to be at [0, 0, -1_737_400] in the PANGU south-polar model frame. The camera and trajectory origins are assumed to be collocated.

Parameters:
  • trajectory – PANGU trajectory with shape (N, 14), containing [x, y, z, vx, vy, vz, qw, qx, qy, qz, p, q, r, m]. Position and velocity are expressed in the local PANGU frame, attitude is a scalar-first camera-to-local quaternion, and angular velocity is expressed in the camera frame.

  • landing_site – Landing-site surface coordinates [x, y, z] in the global PANGU south-polar model frame, in meters.

Returns:

A copy of the trajectory expressed in the global PANGU model frame. Positions, velocities, and attitudes are transformed. Camera-expressed angular velocities and mass remain unchanged.

f2m.trajectory.rotate_vector_rodrigues(v, k, theta)

Rotate vector v around axis k by angle theta (Rodrigues’ rotation formula).

f2m.trajectory.rotated_hohmann_state_history(r_init, alt_p=None, alt_a=None, r_body=1737400.0, mu=4902800118000.0, n_points=100, as_array=False)

Generate a rotated Hohmann transfer trajectory to align with an arbitrary initial position vector.

Parameters:
  • r_init – array-like Desired initial position vector in inertial frame [m].

  • alt_p – Passed through to get_hohmann_state_history.

  • alt_a – Passed through to get_hohmann_state_history.

  • r_body – Passed through to get_hohmann_state_history.

  • mu – Passed through to get_hohmann_state_history.

  • n_points – Passed through to get_hohmann_state_history.

  • as_array – Passed through to get_hohmann_state_history.

Returns:

State history as array or dict, rotated to start at r_init.

Note

If alt_p is None and alt_a is not None -> outbound traj where r_init is periapsis position. If alt_p is not None and alt_a is None -> inbound traj where r_init is apoapsis position. Make sure that r_init corresponds properly to apoapsis or periapsis, making sure it’s smaller than alt_a if alt_a is passed, and idem for alt_p.

f2m.trajectory.save_ampl_trajectory(ampl_model: ~f2m.ampl_interface.AMPLModel, output_dir: ~pathlib.Path, fps: int, camera_tilt: ~scipy.spatial.transform._rotation.Rotation = <scipy.spatial.transform._rotation.Rotation object>, landing_site: tuple[float, float, float] = [-25010, -40149, -3530.85]) None

Saves the computed trajectory and generates a flight file for visualization.

Parameters:
  • ampl_model (AMPLModel) – The AMPL optimization model containing trajectory results.

  • output_dir (Path) – Directory to save trajectory data.

  • fps (int) – Frames per second for trajectory interpolation.

  • camera_tilt (Rotation) – Camera tilt rotation.

f2m.trajectory.save_trajectory_hohmann(full_state_history: dict, output_dir: Path, fps: int, field_of_view=60, camera_tilt: tuple[float, float, float] | None = None, indices=None) None

Saves the computed Hohmann trajectory and generates a PANGU flight file.

Parameters:
  • full_state_history (dict) – Dictionary of trajectory state history.

  • output_dir (Path) – Directory to save trajectory data.

  • fps (int) – Frames per second for trajectory interpolation.

  • field_of_view (int, optional) – Camera field of view in degrees. Defaults to 60.

  • camera_tilt (Optional[tuple[float, float, float]]) – Camera tilt angles (X, Y, Z).

f2m.trajectory.solve_kepler(M, e, tol=1e-10, max_iter=100)

Solves Kepler’s equation E - e*sin(E) = M for E using Newton-Raphson.

Parameters:
  • M (float) – Mean anomaly in radians

  • e (float) – Eccentricity (0 <= e < 1)

  • tol (float) – Convergence tolerance

  • max_iter (int) – Maximum number of iterations

Returns:

Eccentric anomaly E in radians

Return type:

float

f2m.trajectory.trajectory_to_flight(trajectory: ndarray, field_of_view: int = 60, output_dir: Path | None = None) list[str]

Generate PANGU flight file starting from array representing trajectory’s states and saves it to output_dir/flight.fli.

Parameters:
  • trajectory (np.ndarray) – trajectory’s state with shape Nx13 Note: trajectory shape can vary so long as position is in the 0 - 2, and attitude is in the 6 - 9 column. The convention we used is [x, y, z, vx, vy, vz, qw, qx, qy, qz, p, q, r].

  • field_of_view (int) – the field of view of the camera in degrees. Defaults to 60

  • output_dir (Optional[Path]) – Directory to save the flight file. If None, the flight file will not be saved.

Returns:

Each element represents a line of Pangu’s flight file

Return type:

list[str]

f2m.trajectory.trajectory_to_flight_hohmann(trajectory: ndarray, field_of_view: int = 60, camera_tilt: tuple[float, float, float] | None = None) list[str]

Generate PANGU flight file with positions in quaternions, starting from array representing Hohmann trajectory’s states

Parameters:
  • trajectory (np.ndarray) – tracjectory’s state with shape Nx12. Each state is [x, y, z, vx, vy, vz, phi, theta, psi, p, q, r]

  • field_of_view (int) – the field of view of the camera in degrees. Defaults to 60

  • (Optional (camera_tilt) – (float, float, float)): camera tilt angles on X, Y, and Z axes. Defaults to None.

Returns:

Each element represents a line of Pangu’s flight file

Return type:

list[str]

f2m.utils module

f2m.utils.dict2array(d: dict)
f2m.utils.get_axes_from_state(state: ndarray)

Computes a right-handed coordinate frame from a state vector.

Parameters:

state (np.ndarray) – The state vector [x, y, z, vx, vy, vz].

Returns:

A tuple (e_x, e_y, e_z) representing the unit vectors of the frame.

Return type:

tuple

Raises:

RuntimeError – If e_z and e_v are not linearly independent.

f2m.utils.get_camera_tilt(roll: float = 0, pitch: float = 0, yaw: float = 0, degrees: bool = False) Rotation

Return the camera tilt expressed in the body frame.

The input uses the intrinsic camera-centric yaw-pitch-roll convention:

yaw: rotation around body +y pitch: rotation around the rotated +x roll: rotation around the resulting optical +z

The rotation is constructed using intrinsic YXZ Euler angles.

Parameters:
  • roll – Camera roll around the resulting optical z-axis.

  • pitch – Camera pitch around the rotated x-axis.

  • yaw – Camera yaw around the body y-axis.

  • degrees – Whether the input angles are in degrees.

Returns:

Camera tilt expressed in the body frame as a Rotation object.

f2m.utils.get_dcm_from_euler_angles(angles, sequence='XYZ')

Computes the direction cosine matrix (DCM) from given Euler angles.

Parameters:
  • angles (np.ndarray 3x1) – The Euler angles [phi, theta, psi] in radians.

  • sequence (str, optional) – The rotation sequence, either ‘XYZ’ or ‘ZYX’. Default is ‘XYZ’.

Returns:

The 3x3 DCM corresponding to the given Euler angles.

Return type:

np.ndarray

f2m.utils.get_euler_angles_from_dcm(e_x, e_y, e_z, sequence)

Computes the Euler angles from a given direction cosine matrix (DCM).

Parameters:
  • e_x (np.ndarray 3x1) – The first column of the DCM.

  • e_y (np.ndarray 3x1) – The second column of the DCM.

  • e_z (np.ndarray 3x1) – The third column of the DCM.

  • sequence (str, optional) – The rotation sequence, either ‘XYZ’ or ‘ZYX’. Default is ‘XYZ’.

Returns:

The computed Euler angles [phi, theta, psi] in radians.

Return type:

np.ndarray

f2m.utils.get_quaternion_from_dcm(e_x, e_y, e_z)

Computes a quaternion from a given direction cosine matrix (DCM).

Parameters:
  • e_x (np.ndarray 3x1) – The first column of the DCM.

  • e_y (np.ndarray 3x1) – The second column of the DCM.

  • e_z (np.ndarray 3x1) – The third column of the DCM.

Returns:

The computed quaternion [q0, q1, q2, q3].

Return type:

np.ndarray

f2m.utils.get_quaternion_from_euler_angles(angles, sequence='XYZ')

Computes a quaternion from given Euler angles.

Parameters:
  • angles (np.ndarray 3x1) – The Euler angles [phi, theta, psi] in radians.

  • sequence (str, optional) – The rotation sequence, either ‘XYZ’ or ‘ZYX’. Default is ‘XYZ’.

Returns:

The computed quaternion [q0, q1, q2, q3].

Return type:

np.ndarray

f2m.utils.get_quaternion_state_history(state_history: ndarray)

Computes the quaternion state history from a given state history.

This function assumes that the camera is oriented such that it points toward the position vector from the origin (e.g., the Moon).

Parameters:

state_history (np.ndarray) – An array of state vectors, where each row is [x, y, z, vx, vy, vz].

Returns:

An array of quaternions representing the orientation history.

Return type:

np.ndarray

f2m.utils.lat_lon_to_pangu_xyz(latitude_deg: float, longitude_deg: float, elevation: float = 0.0, moon_radius: float = 1737400.0) tuple[float, float, float]

Computes PANGU south-polar model coordinates from lunar latitude, longitude, and radial elevation.

Lunar latitude is measured from the lunar equator, with positive values toward the north pole and negative values toward the south pole. Lunar longitude is measured from the lunar prime meridian, with positive values eastward. The point (0 deg latitude, 0 deg longitude) lies on the lunar equator at the prime meridian, approximately at the center of the Moon’s near side.

Parameters:
  • latitude_deg (float) – The lunar latitude in degrees.

  • longitude_deg (float) – The lunar east longitude in degrees.

  • elevation (float) – The radial elevation above the reference spherical Moon in meters.

  • moon_radius (float) – The reference Moon radius in meters.

Returns:

A tuple (x, y, z) representing the PANGU model coordinates in meters.

Return type:

tuple

f2m.utils.load_trajectory(traj_dir: Path | str | None = None, verbose: bool = False, use_interp: bool = False, return_throttle: bool = False, return_mass: bool = True, return_ranges: bool = True, custom_traj_file: Path | str | None = None) tuple[ndarray, ndarray, ndarray]

Takes either a trajectory directory or custom file as input to load the trajectory. This function is flexible regarding whether mass is given and whether there are range measurements available.

Parameters:
  • traj_dir (Path|str|None) – The directory from which to load the trajectory. Defaults to None.

  • verbose (bool) – Flag to enable verbose output. Defaults to False.

  • use_interp (bool) – Flag to enable loading the interpolated state. Defaults to False.

  • return_throttle (bool) – Flag to specify whether the thrust exists and should be returned as a separate array. Defaults to False.

  • return_mass (bool) – Flag that keeps the mass (if it exists) in the state history, otherwise discard the last column (being mass). Defaults to True.

  • return_ranges (bool) – An option to determine whether or not the ranges are added to the state history

  • custom_traj_file (Path|str|None) – The path to the file containing the custom specified state history. Defaults to None.

Returns:

time, state history, thrusts.

Return type:

tuple(np.ndarray, np.ndarray, np.ndarray)

f2m.utils.pangu_craft_to_quaternion(yaw_deg: float, pitch_deg: float, roll_deg: float) ndarray

Converts PANGU craft-view yaw, pitch, and roll angles to a scalar-first quaternion.

Parameters:
  • yaw_deg (float) – The craft-view yaw angle in degrees. This rotates the boresight direction about the PANGU world z-axis.

  • pitch_deg (float) – The craft-view pitch angle in degrees. This sets the boresight elevation relative to the PANGU world horizontal plane.

  • roll_deg (float) – The craft-view roll angle in degrees. This twists the image about the resulting boresight direction.

Returns:

The scalar-first quaternion (q0, q1, q2, q3) representing the

camera-to-world rotation.

Return type:

np.ndarray

f2m.utils.pangu_craft_to_rotation(yaw_deg: float, pitch_deg: float, roll_deg: float) Rotation

Converts PANGU craft-view yaw, pitch, and roll angles to a scipy rotation object.

Parameters:
  • yaw_deg (float) – The craft-view yaw angle in degrees. This rotates the boresight direction about the PANGU world z-axis.

  • pitch_deg (float) – The craft-view pitch angle in degrees. This sets the boresight elevation relative to the PANGU world horizontal plane.

  • roll_deg (float) – The craft-view roll angle in degrees. This twists the image about the resulting boresight direction.

Returns:

The scipy rotation object representing the camera-to-world rotation.

Return type:

Rotation

f2m.utils.scale_pixel_coordinates(coord: ndarray, image_width: int) ndarray

Scale pixels coordinates in [-0.5, 0.5]

Parameters:
  • x (np.ndarray) – pixel coordinates

  • image_width (int) – image resolution

Returns:

rescaled coordinates

Return type:

np.ndarray

f2m.visualization module

f2m.visualization.generate_video_comparison(frames: ndarray, traj: ndarray, of_estimator: LKOpticalFlowEstimator, output_path: Path, rangefinder: bool = True, slope_estimation: bool = False, known_velocity: bool = False, alpha: float = None, beta: float = None, rotation_vector: ndarray = array([0, 0, 0]), focal_length: float = 0.866025, arrow_scale: float = 3.0, frame_interval: int = 1, fps: int = 4, verbose: bool = False, rotation: list[float] = [0, 0, 0]) None

Generate the video comparing ground truth optical flow to predicted optical flow for visual comparison

Parameters:
  • frames (np.ndarray) – cv2 arrays representing video frames in BGR.

  • traj (np.ndarray) – trajectory data.

  • of_estimator (LKOpticalFlowEstimator) – optical flow estimator.

  • output_path (Path) – Where to save output frames.

  • rotation_vector (np.ndarray, optional) – Rotation vector for camera perspective in radians [roll, -pitch, yaw]. Defaults to np.array([0, 0, 0]).

  • focal_length (float, optional) – Focal length of video source.

  • arrow_scale (float, optional) – scale optical flow arrows for better visualization. Defaults to 3.

  • frame_interval (int, optional) – Detect new points to track every frame_interval frames. Defaults to 1.

  • fps (int, optional) – framerate in the output video. Defaults to 4

  • verbose (bool, optional) – defaults to False

f2m.visualization.plot_angles(traj_dir: Path, save: bool = False) None
f2m.visualization.plot_angular_velocities(traj_dir: Path, plot_magnitude: bool = False, save: bool = False, as_svg: bool = False) None
f2m.visualization.plot_hohmann_transfer(traj_dir: Path, init_position, alt_a=None, alt_p=None, mu_body=4902800118000.0, planet_radius=1737400.0, save: bool = False)
f2m.visualization.plot_main_thruster(traj_dir: Path, save: bool = False) None
f2m.visualization.plot_mass(traj_dir: Path, save: bool = False) None
f2m.visualization.plot_orbit(orbit_arr: ndarray, planet_radius=1737400.0, translate_planet: ndarray = [0, 0, 0], save: bool = False, title='Generic Orbit', traj_dir=None, elev: float = None, azim: float = None)
f2m.visualization.plot_orbit_textured(orbit_arr: ndarray, texture_file: str, planet_radius: float = 1737400.0, translate_planet: ndarray = [0, 0, 0], downsample_scale: int = 1, sun_pos: tuple = (1000000000.0, 45, 45), save: bool = False, title='Textured Orbit', traj_dir=None, elev: float = None, azim: float = None, show_axes: bool = True)
f2m.visualization.plot_positions(traj_dir: Path, save: bool = False) None
f2m.visualization.plot_rho_vs_z(traj_dir: Path, estimate_z: bool = False, save: bool = False) None
f2m.visualization.plot_thrusters(traj_dir: Path, save: bool = False) None
f2m.visualization.plot_trajectory(traj_dir: Path, save: bool = False, as_svg: bool = False) None
f2m.visualization.plot_velocities(traj_dir: Path, save: bool = False) None
f2m.visualization.plot_velocity_boxplots(results, compare_param='landing_site', filter_params=None, components=['$v_x$', '$v_y$', '$v_z$'], title='Velocity Error Comparison', ylims=None, figsize=(8, 5), legend_label=None, legend_loc='upper right', logscale=False, error_metric='absolute', save=False, svg=False, output_path=None)

Creates a grouped box plot showing velocity component errors.

Parameters: - gt_by_group: list of arrays (n_components x time) for each group (e.g., landing site) - pred_by_group: list of arrays, same shape as gt_by_group - group_labels: list of labels (e.g., landing site names), same length as gt_by_group - components: list of component names to label plots - title: plot title - max_error: y-axis limit for error - figsize: figure size - save: whether to save the plot - output_path: path to save to (if save=True)

f2m.visualization.plot_velocity_comparison(gt, pred, components_to_plot=['vx (m/s)', 'vy (m/s)', 'vz (m/s)'], error_tol=1, title=None, max_error=3, output_path=None, save=False, figsize=(20, 7))
f2m.visualization.plot_velocity_comparison_paper(gt, pred, components_to_plot=['$v_x$ $(m/s)$', '$v_y$ $(m/s)$', '$v_z$ $(m/s)$'], title=None, ylims=None, output_path=None, save=False, svg=False, figsize=(8, 6))
f2m.visualization.set_axes_equal(ax)

Make axes of 3D plot have equal scale so that spheres appear as spheres, cubes as cubes, etc.

Input

ax: a matplotlib axis, e.g., as output from plt.gca().

Module contents