Simulated Body Navigation Model
Overview
NavModelBodySimulated is the
simulated-image variant of the body navigation model. It renders a body from
operator-supplied ellipsoid (or polyhedral-mesh) parameters – centre, axes, rotation,
lighting – instead of from SPICE prediction, then emits the body features the navigation
techniques consume:
always a
BODY_DISCcarrying the rendered template, forBodyDiscCorrelateNav;a
BODY_BLOB(the orientation-free lit-weighted centroid, built by the sharedNavModelBodyBase) when the predicted diameter clears the blob floor, forBodyBlobNav;a
LIMB_ARC(the silhouette boundary as a vertex polyline with outward normals) when the body is well resolved (diameter at least 100 px) and at low phase (at most 60 degrees), forBodyLimbNav.
The model overrides instances_for_obs() to build
one instance per body of a simulated observation; the parent
NavModelBody declines simulated observations, so the
autonomous registry routes simulated frames here.
Theory
Simulated body rendering is a controlled-input version of the same silhouette-extraction
pipeline that drives NavModelBody. The operator
specifies a body in image-plane coordinates (centre and per-axis radii) plus a phase /
lighting geometry, and the renderer paints the corresponding ellipsoidal body onto an
extended-FOV image plus matching mask.
The rendered template is the BODY_DISC feature payload the disc correlation navigates against. The blob and limb features extend the simulated body across the technique ladder: the blob is the orientation-independent fallback for small, high-phase, or irregular bodies, and the limb is the resolved-body distance-transform fit. Which feature is load-bearing tracks resolution and phase the same way it does on a real frame, so the range and phase parameter sweeps (see Simulator Performance and Sensitivity Report) show the primary technique transitioning limb -> disc -> blob as a body shrinks. The simulated body’s geometry is operator-known by construction, so the simulated path is the calibration regime – it lets a developer probe the navigation pipeline with bodies whose true offset is known to the pixel.
Restrictions and assumptions
The operator must supply finite, positive ellipsoid axes. Degenerate inputs (zero radius, negative axes) are rejected by
create_simulated_body().Crater and anti-aliasing keys in the sim-params dict are accepted but ignored; the simulated renderer always uses maximum anti-aliasing.
The simulated body is rendered onto a fixed extfov image without per-instrument noise or PSF smearing; the operator’s downstream noise-injection pipeline supplies those.
Sources of uncertainty
The simulated body has no measurement uncertainty by construction; the rendered template is an exact ellipsoid. The downstream technique’s reported covariance reflects only the correlation-curvature CRLB at the chosen NCC peak.
Configuration
The simulated body model consumes no YAML configuration of its own; every parameter comes
in via the per-instance sim_params dict. Expected keys:
name— body label used in metadata and the summary PNG.center_v,center_u— pixel coordinates of the body centre.range— subject distance in km (defaults to+inf).axis1,axis2,axis3— ellipsoid semi-axes in km.axis3defaults tomin(axis1, axis2).rotation_z— rotation about the line of sight (degrees).rotation_tilt— tilt of the body (degrees).illumination_angle— degrees.phase_angle— degrees. Also gates LIMB_ARC emission (limb only at or below 60 degrees).shape_model—ellipsoid(default) orpolyhedral_meshfor an irregular body; a mesh readsmesh_lumpiness,mesh_seed, andpose_euler_deg(seemesh_spec_from_params()).km_per_pixel— optional physical scale at the limb; when absent the phase-irregularity factor collapses to the regular-body case.nav_override— optional mapping overlaid on the body params to build the predicted body, separating the render geometry from the navigation geometry (see Render geometry vs navigation geometry below).
Crater and anti-aliasing keys are accepted but ignored. The predicted silhouette diameter gates the blob (at least 8 px) and limb (at least 100 px) emission; the diameter floor on the limb keeps the LM-refined fit off marginally-resolved bodies, where it would inject cross-process jitter into the fused offset.
Render geometry vs navigation geometry
In real navigation the body’s pose (the body-fixed to camera rotation) is an input from SPICE: the navigator renders its predicted body at that pose and solves only for the pointing offset; it never estimates orientation from the pixels. The simulator has no SPICE, so the pose is scene ground truth carried on the body params. By default the predicted body is built from the same params the renderer drew, so the navigator knows the truth (the agreeing case).
An optional nav_override mapping breaks that tie. The renderer ignores it and
always draws the true geometry; the navigator builds its predicted body from the
body params with nav_override overlaid (_nav_params). This is the channel
that lets the navigation geometry diverge from the render geometry, which the
irregular-body scenarios exercise:
Same geometry (no override) – mesh vs mesh at the true pose. The resolved-mesh limb is exact by construction.
Shape mismatch – render a lumpy mesh, predict its zero-relief (ellipsoidal) limit by overriding
mesh_lumpinessto0.0at the same pose. The only residual is shape; the disc correlation still aligns the two filled silhouettes and the recovered centroid bias grows with the rendered relief. Realising the ellipsoidal prediction as the smooth limit of the mesh keeps both silhouettes on one renderer, so the residual is purely the shape mismatch under test.Pose disagreement – render the mesh at the true pose, predict the same mesh at a different
pose_euler_deg. The wrong-pose silhouette boundary drives the limb distance-transform fit to a confidently-wrong offset, while the lit-weighted blob centroid – which a centrally-symmetric (low-relief triaxial) body keeps near the body centre under rotation – stays accurate.
The override never changes the centre, so the predicted body stays at the unshifted position the planted offset is measured from.
Implementation
Source file: src/nav/nav_model/nav_model_body_simulated.py —
NavModelBodySimulated.
Public class NavModelBodySimulated, base
NavModelBodyBase. The class overrides
instances_for_obs() to build one instance per body
of a simulated observation; the parent
NavModelBody returns an empty list for a simulated
observation, so the orchestrator’s
build_models_for_obs() driver routes simulated frames to
this subclass.
Public methods (autodocumented at nav.nav_model):
create_model()— renders the simulated body (ellipsoid viacreate_simulated_body(), or a mesh viarender_mesh_body_image()), computes the limb mask via the sharedNavModelBodyBasehelper, and records the predicted diameter and tight bounding box used to gate and emit features.to_features()— emits the BODY_DISC plus, when the resolution and phase gates pass, the BODY_BLOB and LIMB_ARC features described under Overview.to_annotations()— reuses the shared body annotation helper onNavModelBodyBaseto render body silhouette and labels onto the summary PNG.
Inherited NavModel properties:
name,
obs,
metadata.
Call path
Call path traced through
create_model():
Open a logged section. Read the operator-supplied sim parameters off the per-instance dict.
Convert per-axis rotations and angle parameters from degrees to radians.
Call
create_simulated_body()with the per-axis radii and geometry; the helper returns the rendered simulated body image.Derive the body mask from the rendered image (every non-zero pixel is on the body).
Compute the limb mask via
NavModelBodyBase’s shared discrete-mask neighbour-shift helper.Promote the rendered image and the masks from sensor-shaped arrays to extfov-shaped arrays (zero-padded for the extfov margin).
Record the predicted centre, the subject range, and the bounding box on the model’s internal state for downstream feature emission.
Call path traced through
to_features():
Crop the rendered template image and mask to the per-instance tight bounding box (the silhouette bbox plus slop, matching the SPICE-backed model, so a downstream moment stays local to the body rather than integrating over the whole frame).
Construct one
BODY_DISCNavFeaturecarrying the cropped template image, the cropped mask, the predicted centre, the subject range, and aBodyDiscFlagswith the operator-supplied body name plusoverflow_fov_fraction = 0.0.When the predicted diameter clears the blob floor, append a BODY_BLOB built by the shared blob-feature helper on
NavModelBodyBase.When the diameter and phase gates pass, append a LIMB_ARC: the silhouette boundary is sampled into a vertex polyline with outward normals and a fixed per-vertex sigma.
Reliability on each feature is fixed at
1.0(the simulated body is by construction reliable; downstream gates do not drop it).
Examples
The simulated body model is consumed by the simulated-image GUI driver
(nav_create_simulated_image). An operator specifies a body — say a Mimas-like
ellipsoid centred at (512, 512) with semi-axes 200 km, illumination angle 60
degrees, phase angle 30 degrees — and the simulator renders the corresponding
extended-FOV image plus mask. The downstream
BodyDiscCorrelateNav correlates the
template against an injected synthetic-noise image and recovers the operator-known
(0, 0) offset (or whatever offset the operator injected) within sub-pixel. The
operator uses the residual to validate per-instrument plate-scale and PSF assumptions
without a real Cassini observation.