Class Hierarchy

The following Mermaid diagram shows the complete class hierarchy of the RMS-NAV system:

        classDiagram
   direction RL
   class NavBase {
       +__init__(*, config=None, **kwargs)
       +logger
       +config
   }

   class DataSet {
       <<abstract>>
       +__init__(*, config=None)
       +_img_name_valid(name)*
       +add_selection_arguments(parser, group)*
       +yield_image_files_from_arguments(args)*
       +yield_image_files_index(**kwargs)*
       +supported_grouping()
       +pds4_bundle_template_dir()
       +pds4_bundle_name()
       +pds4_bundle_path_for_image(name)
       +pds4_path_stub(image_file)
       +pds4_template_variables(...)
       +pds4_image_name_to_data_lid(name)
       +pds4_image_name_to_data_lidvid(name)
       +pds4_image_name_to_browse_lid(name)
       +pds4_image_name_to_browse_lidvid(name)
   }

   class DataSetPDS3 {
       +__init__(pds3_holdings_root=None, *, index_filecache=None, pds3_holdings_filecache=None, config=None)
       +_img_name_valid(name)
       +yield_image_files_index(**kwargs)
   }

   class DataSetPDS3CassiniISS {
       +__init__(*, config=None)
       +_img_name_valid(name)
       +pds4_bundle_template_dir()
       +pds4_bundle_name()
       +pds4_bundle_path_for_image(name)
       +pds4_path_stub(image_file)
       +pds4_template_variables(...)
       +pds4_image_name_to_data_lid(name)
       +pds4_image_name_to_data_lidvid(name)
       +pds4_image_name_to_browse_lid(name)
       +pds4_image_name_to_browse_lidvid(name)
   }

   class DataSetPDS3CassiniISSCruise {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class DataSetPDS3CassiniISSSaturn {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class DataSetPDS3VoyagerISS {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class DataSetPDS3GalileoSSI {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class DataSetPDS3NewHorizonsLORRI {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class DataSetSim {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class DataSetPDS4 {
       +__init__(*, config=None)
       +_img_name_valid(name)
   }

   class Obs {
       <<abstract>>
       +__init__(*, config=None, **kwargs)
   }

   class ObsSnapshot {
       +__init__(snapshot, *, extfov_margin_vu=None, config=None, **kwargs)
       +inventory_body_in_fov(inv)
       +inventory_body_in_extfov(inv)
       +clip_rect_fov(u_min, u_max, v_min, v_max)
       +clip_rect_extfov(u_min, u_max, v_min, v_max)
       +bp
       +ext_bp
       +corner_bp
       +center_bp
   }

   class ObsInst {
       <<abstract>>
       +from_file(path, *, config=None, extfov_margin_vu=None, **kwargs)*
       +star_min_usable_vmag()*
       +star_max_usable_vmag()*
       +get_public_metadata()*
   }

   class ObsSnapshotInst {
       <<abstract>>
       +from_file(path, *, config=None, extfov_margin_vu=None, **kwargs)*
   }

   class ObsCassiniISS {
       +from_file(path, *, config=None, extfov_margin_vu=None)
   }

   class ObsVoyagerISS {
       +from_file(path, *, config=None, extfov_margin_vu=None)
   }

   class ObsGalileoSSI {
       +from_file(path, *, config=None, extfov_margin_vu=None)
   }

   class ObsNewHorizonsLORRI {
       +from_file(path, *, config=None, extfov_margin_vu=None)
   }

   class ObsSim {
       +from_file(path, *, config=None, extfov_margin_vu=None)
   }

   class NavMaster {
       +__init__(obs, *, nav_models=None, nav_techniques=None, config=None)
       +obs
       +models
       +compute_all_models()
       +navigate()
       +metadata_serializable()
   }

   class NavModel {
       <<abstract>>
       +__init__(name, obs, *, config=None)
       +name
       +obs
       +models
       +metadata
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)*
   }

   class NavModelStars {
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
       +star_list
   }

   class NavModelBodyBase {
       <<abstract>>
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)*
   }

   class NavModelBody {
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
   }

   class NavModelBodySimulated {
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
   }

   class NavModelRingsBase {
       <<abstract>>
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)*
   }

   class NavModelRings {
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
   }

   class NavModelRingsSimulated {
       +__init__(name, obs, ring_name, sim_params, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
   }

   class RingFeatureType {
       <<enumeration>>
       GAP
       RINGLET
   }

   class RingBaseOrbitMode {
       <<frozen dataclass>>
       +a: float
       +ae: float
       +long_peri: float
       +rate_peri: float
       +rms: float
   }

   class RingPerturbationMode {
       <<frozen dataclass>>
       +mode_num: int
       +amplitude: float
       +phase: float
       +pattern_speed: float
   }

   class RingFeature {
       <<frozen dataclass>>
       +key: str
       +name: str | None
       +feature_type: RingFeatureType
       +inner_edge: RingEdgeData | None
       +outer_edge: RingEdgeData | None
       +is_visible_at(obs_time_et) bool
       +is_in_radius_range(min_r, max_r) bool
       +uncertainty: float
       +all_base_radii() Sequence
       +from_config(key, data)$ RingFeature
       +render(context) list[RingRenderResult]
   }

   class RingEdgeData {
       <<frozen dataclass>>
       +base_orbit: RingBaseOrbitMode
       +perturbations: tuple[RingPerturbationMode, ...]
       +base_radius: float
       +rms: float
       +radial_perturbations() tuple
       +parsed_modes_for_backplane() list
   }

   class RingFeatureFilter {
       +__init__(obs_time_et, min_radius, max_radius, ..., logger)
       +filter(features) list[RingFeature]
   }

   class RingsRenderContext {
       <<frozen dataclass>>
       +obs
       +ring_target: str
       +epoch: float
       +resolutions: ndarray
       +fade_width_pix: float
       +all_edge_radii: tuple
       +logger
   }

   class RingRenderResult {
       +model_img: ndarray
       +model_mask: ndarray
       +uncertainty: float
       +edge_info_list: list
   }

   class NavModelTitan {
       +__init__(name, obs, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
   }

   class NavModelCombined {
       +__init__(name, obs, models, *, config=None)
       +create_model(always_create_model=False, never_create_model=False, create_annotations=True)
   }

   class NavTechnique {
       <<abstract>>
       +__init__(nav_master, *, config=None)
       +nav_master
       +navigate()*
       +offset
       +uncertainty
       +confidence
   }

   class NavTechniqueCorrelateAll {
       +__init__(nav_master, *, config=None)
       +navigate()
       +combined_model()
   }

   class NavTechniqueManual {
       +__init__(nav_master, *, config=None)
       +navigate()
       +combined_model()
   }

   class NavTechniqueTitan {
       +__init__(nav_master, *, config=None)
       +navigate()
   }

   class Annotation {
       <<abstract>>
       +__init__(*, config=None)
       +draw(image)*
   }

   class Annotations {
       +__init__(*, config=None)
       +annotations: List[Annotation]
       +add(annotation)
       +draw_all(image)
   }

   class AnnotationTextInfo {
       +__init__(text, position, *, config=None)
       +draw(image)
   }

   class Config {
       +__init__()
       +read_config(config_path=None, reread=False)
       +update_config(config_path, read_default=True)
       +category(name)
   }

   NavBase <|-- DataSet
   NavBase <|-- Obs
   NavBase <|-- NavMaster
   NavBase <|-- NavModel
   NavBase <|-- NavTechnique
   NavBase <|-- Annotation

   DataSet <|-- DataSetPDS3
   DataSet <|-- DataSetSim
   DataSet <|-- DataSetPDS4
   DataSetPDS3 <|-- DataSetPDS3CassiniISS
   DataSetPDS3 <|-- DataSetPDS3VoyagerISS
   DataSetPDS3 <|-- DataSetPDS3GalileoSSI
   DataSetPDS3 <|-- DataSetPDS3NewHorizonsLORRI
   DataSetPDS3CassiniISS <|-- DataSetPDS3CassiniISSCruise
   DataSetPDS3CassiniISS <|-- DataSetPDS3CassiniISSSaturn

   Obs <|-- ObsSnapshot
   ObsInst <|-- ObsSnapshotInst
   ObsSnapshot <|-- ObsSnapshotInst
   ObsSnapshotInst <|-- ObsCassiniISS
   ObsSnapshotInst <|-- ObsVoyagerISS
   ObsSnapshotInst <|-- ObsGalileoSSI
   ObsSnapshotInst <|-- ObsNewHorizonsLORRI
   ObsSnapshotInst <|-- ObsSim

   NavModel <|-- NavModelStars
   NavModel <|-- NavModelBodyBase
   NavModel <|-- NavModelRingsBase
   NavModel <|-- NavModelTitan
   NavModel <|-- NavModelCombined

   NavModelBodyBase <|-- NavModelBody
   NavModelBodyBase <|-- NavModelBodySimulated

   NavModelRingsBase <|-- NavModelRings
   NavModelRingsBase <|-- NavModelRingsSimulated

   RingFeature --> RingFeatureType : feature_type
   RingEdgeData *-- RingBaseOrbitMode : base_orbit
   RingEdgeData "1" o-- "0..*" RingPerturbationMode : perturbations

   NavModelRings --> RingFeature : retrieves & renders
   RingFeature --> RingEdgeData : inner_edge / outer_edge
   NavModelRings --> RingFeatureFilter : constructs
   RingFeature ..> RingFeatureFilter : filter pipeline
   RingFeature --> RingsRenderContext : render(context)
   RingFeature --> RingRenderResult : render() returns

   NavTechnique <|-- NavTechniqueCorrelateAll
   NavTechnique <|-- NavTechniqueManual
   NavTechnique <|-- NavTechniqueTitan

   Annotation <|-- AnnotationTextInfo
   Annotations --> Annotation
    

Key Components

Dataset

DataSet handles access to image files and metadata. It defines _img_name_valid(...), add_selection_arguments(...), yield_image_files_from_arguments(...), and yield_image_files_index(...) for dataset-specific selection and iteration. For PDS4 bundle generation, it defines non-abstract stubs (each raising NotImplementedError) that subclasses may override: pds4_bundle_template_dir(), pds4_bundle_name(), pds4_bundle_path_for_image(), pds4_path_stub(), pds4_template_variables(), and the four LID/LIDVID converters (pds4_image_name_to_{data,browse}_{lid,lidvid}()). Datasets that do not yet support PDS4 bundle generation leave these as the default stubs. DataSetPDS3 provides volume and index-based iteration for archives, while instrument-specific subclasses tailor parsing and volume sets. Instrument-specific dataset classes include DataSetPDS3CassiniISS (base class for all Cassini ISS volumes), DataSetPDS3CassiniISSCruise (volumes 1001-1009), DataSetPDS3CassiniISSSaturn (volumes 2001-2116), DataSetPDS3VoyagerISS, DataSetPDS3GalileoSSI, DataSetPDS3NewHorizonsLORRI, and DataSetSim (for simulated images). Dataset name mapping is defined in nav.dataset.__init__ (coiss, coiss_cruise, coiss_saturn, gossi, nhlorri, vgiss, their *_pds3 aliases, and sim). Cassini ISS adds --camera (NAC or WAC) and supports a botsim grouping that pairs NAC/WAC images when available.

Obs and ObsSnapshot

Obs is the abstract base class for observations. ObsSnapshot extends it with backplane handling and accessors, while ObsInst defines the instrument-specific contract with a from_file(...) constructor and metadata helpers. Instrument snapshots extend ObsSnapshotInst and must accept config and extfov_margin_vu keyword arguments. Instrument classes include ObsCassiniISS, ObsVoyagerISS, ObsGalileoSSI, ObsNewHorizonsLORRI, and ObsSim (for simulated images).

Annotation

The annotation subsystem composes labels and graphical elements into an overlay used by the final PNG. Annotations aggregates model-provided annotations and renders them with appropriate coloring and contrast stretching, optionally using per-region stretching via stretch_regions.