Note

Generated by nbsphinx from a Jupyter notebook. All the examples as Jupyter notebooks are available in the tudatpy-examples repo.

MRO - Comparing Doppler and Range Measurements from TNF Files to Simulated Observables#

Copyright (c) 2010-2022, Delft University of Technology. All rights reserved. This file is part of the Tudat. Redistribution and use in source and binary forms, with or without modification, are permitted exclusively under the terms of the Modified BSD license.

Important Preliminary Remarks#

Running this example automatically downloads required files if not found locally:

  • Trajectory and orientation kernels for MRO

  • Atmospheric correction files

  • TNF files with Doppler measurements

  • etc.

First run may take ~1 hour depending on internet connection. Files are cached locally for subsequent runs.

[1]:
# Load required standard modules
import multiprocessing as mp
import os
import shutil
import numpy as np
from matplotlib import pyplot as plt
from datetime import datetime
from urllib.request import urlretrieve
[2]:
# Load required tudatpy modules
from tudatpy.interface import spice
from tudatpy.math import interpolators
from tudatpy.astro import time_representation
from tudatpy import util
from tudatpy.data.processTrk234 import Trk234Processor

from tudatpy.dynamics import environment_setup
from tudatpy import estimation
from tudatpy.estimation import observable_models_setup
from tudatpy.estimation import observations, observations_setup

from load_pds_files import download_url_files_time, download_url_files_time_interval

Helper Functions#

1. File Retrieval Function#

This function retrieves all relevant files necessary to run the example over the time interval of interest (and automatically downloads them if they cannot be found locally). It returns a tuple containing the lists of relevant clock files, orientation kernels, tropospheric correction files, ionospheric correction files, odf files, trajectory files, MRO reference frames file, and MRO structure file that should be loaded.

[3]:
def get_mro_files(local_path, start_date, end_date):
    """
    Retrieve and download all necessary MRO files for the specified time interval.

    Returns: tuple of (clock_files, orientation_files, tro_files, ion_files,
                       tnf_files, trajectory_files, frames_def_file, structure_file)
    """

    # Check if local_path designates an existing directory and creates the directory is not
    if not os.path.isdir(local_path):
        os.mkdir(local_path)

    # Clock file (a single file is necessary)
    print("---------------------------------------------")
    print("Download MRO clock ")
    clock_files = ["mro_sclkscet_00112_65536.tsc"]
    # Define url where clock files can be downloaded for MRO
    url_clock_files = "https://naif.jpl.nasa.gov/pub/naif/pds/data/mro-m-spice-6-v1.0/mrosp_1000/data/sclk/"
    for file in clock_files:
        # Check if the relevant clock file exists locally
        if os.path.exists(local_path + file) == False:
            print("download", local_path + file)
            # If not, download it
            urlretrieve(url_clock_files + file, local_path + file)

    # Print and store all relevant clock file names
    print("relevant clock files")
    for k in range(len(clock_files)):
        clock_files[k] = local_path + clock_files[k]
        print(clock_files[k])

    # Orientation files (multiple orientation kerneks are required, typically covering intervals of a few days)
    # For this MRO example, orientation kernels should be loaded both for the spacecraft and for the MRO antenna specifically.
    print("---------------------------------------------")
    print("Download MRO orientation kernels")
    # Define url where orientation kernels can be downloaded for MRO
    url_orientation_files = "https://naif.jpl.nasa.gov/pub/naif/pds/data/mro-m-spice-6-v1.0/mrosp_1000/data/ck/"
    # Retrieve the names of all spacecraft orientation kernels required to cover the time interval of interest, and download them if they
    # do not exist locally yet.
    orientation_files = download_url_files_time_interval(
        local_path=local_path,
        filename_format="mro_sc_psp_*.bc",
        start_date=start_date,
        end_date=end_date,
        url=url_orientation_files,
        time_interval_format="%y%m%d_%y%m%d",
    )

    # Retrieve the names of all antenna orientation kernels required to cover the time interval of interest, and download them if they
    # do not exist locally yet
    antenna_files = download_url_files_time_interval(
        local_path=local_path,
        filename_format="mro_hga_psp_*.bc",
        start_date=start_date,
        end_date=end_date,
        url=url_orientation_files,
        time_interval_format="%y%m%d_%y%m%d",
    )

    # Print and store all relevant orientation file names (both for the MRO spacecraft and antenna)
    for file in antenna_files:
        orientation_files.append(file)

    print("relevant orientation files")
    for f in orientation_files:
        print(f)

    # Tropospheric corrections (multiple tropospheric correction files are required, typically covering intervals of a few days)
    print("---------------------------------------------")
    print("Download MRO tropospheric corrections files")
    # Define url where tropospheric correction files can be downloaded for MRO
    url_tro_files = "https://pds-geosciences.wustl.edu/mro/mro-m-rss-1-magr-v1/mrors_0xxx/ancillary/tro/"
    # Retrieve the names of all tropospheric correction files required to cover the time interval of interest, and download them if they
    # do not exist locally yet
    tro_files = download_url_files_time_interval(
        local_path=local_path,
        filename_format="mromagr*.tro",
        start_date=start_date,
        end_date=end_date,
        url=url_tro_files,
        time_interval_format="%Y_%j_%Y_%j",
    )

    # Print all relevant tropospheric correction file names
    print("relevant tropospheric corrections files")
    for f in tro_files:
        print(f)

    # Ionospheric corrections (multiple ionospheric correction files are required, typically covering intervals of a few days)
    print("---------------------------------------------")
    print("Download MRO ionospheric corrections files")
    # Define url where ionospheric correction files can be downloaded for MRO
    url_ion_files = "https://pds-geosciences.wustl.edu/mro/mro-m-rss-1-magr-v1/mrors_0xxx/ancillary/ion/"
    # Retrieve the names of all ionospheric correction files required to cover the time interval of interest, and download them if they do not exist locally yet
    ion_files = download_url_files_time_interval(
        local_path=local_path,
        filename_format="mromagr*.ion",
        start_date=start_date,
        end_date=end_date,
        url=url_ion_files,
        time_interval_format="%Y_%j_%Y_%j",
    )

    # Print all relevant ionospheric correction file names
    print("relevant ionospheric corrections files")
    for f in ion_files:
        print(f)

    # TNF files (multiple TNF files are required, typically one per day)
    print("---------------------------------------------")
    print("Download MRO TNF files")
    # Define url where TNF files can be downloaded for MRO
    url_odf = (
        "https://pds-geosciences.wustl.edu/mro/mro-m-rss-1-magr-v1/mrors_0xxx/tnf/"
    )
    # Retrieve the names of all existing TNF files within the time interval of interest, and download them if they do not exist locally yet
    tnf_files = download_url_files_time(
        local_path=local_path,
        filename_format="mromagr*_\w\w\w\wxmmmv1.tnf",
        start_date=start_date,
        end_date=end_date,
        url=url_odf,
        time_format="%Y_%j",
        indices_date_filename=[7],
    )

    # Print the name of all relevant TNF files that have been identified over the time interval of interest
    print("relevant TNF files")
    for f in tnf_files:
        print(f)

    # MRO trajectory files (multiple files are necessary to cover one entire year, typically each file covers ~ 3-4 months)
    # Note that the file names hard coded below cover calendar year 2012. This should be modified in case the time interval
    # of the example is modified.
    print("---------------------------------------------")
    print("Download MRO trajectory files")
    trajectory_files = [
        "mro_psp21.bsp",
        "mro_psp22.bsp",
        "mro_psp23.bsp",
        "mro_psp24.bsp",
        "mro_psp25.bsp",
    ]
    # Define url where trajectory files can be downloaded for MRO
    url_trajectory_files = "https://naif.jpl.nasa.gov/pub/naif/pds/data/mro-m-spice-6-v1.0/mrosp_1000/data/spk/"
    for file in trajectory_files:
        # Check if the relevant trajectory file exists locally
        if os.path.exists(local_path + file) == False:
            print("download", local_path + file)
            # If not, download it
            urlretrieve(url_trajectory_files + file, local_path + file)

    # Print and store all relevant trajectory file names
    print("relevant trajectory files")
    for k in range(len(trajectory_files)):
        trajectory_files[k] = local_path + trajectory_files[k]
        print(trajectory_files[k])

    # Frames definition file for the MRO spacecraft (only one file is necessary). This is useful for HGA and
    # spacecraft-fixed frames definition.
    print("---------------------------------------------")
    print("Download MRO frames definition file")
    frames_def_file = "mro_v16.tf"
    # Define url where the frames definition file can be downloaded for MRO
    url_frames_def_file = "https://naif.jpl.nasa.gov/pub/naif/pds/data/mro-m-spice-6-v1.0/mrosp_1000/data/fk/"
    # Check if the relevant frames definition file exists locally
    if os.path.exists(local_path + frames_def_file) == False:
        print("download", local_path + frames_def_file)
        # If not, download it
        urlretrieve(url_frames_def_file + frames_def_file, local_path + frames_def_file)

    # Print and store the frames definition file name
    print("relevant MRO frames definition file")
    frames_def_file = local_path + frames_def_file
    print(frames_def_file)

    # Structure file for the MRO spacecraft (only one file is necessary). This is useful to retrieve the antenna
    # position in spacecraft-fixed frame.
    print("---------------------------------------------")
    print("Download MRO structure file")
    structure_file = "mro_struct_v10.bsp"
    # Define url where the MRO structure file can be downloaded
    url_structure_file = "https://naif.jpl.nasa.gov/pub/naif/pds/data/mro-m-spice-6-v1.0/mrosp_1000/data/spk/"
    # Check if the relevant structure file exists locally
    if os.path.exists(local_path + structure_file) == False:
        print("download", local_path + structure_file)
        # If not, download it
        urlretrieve(url_structure_file + structure_file, local_path + structure_file)

    # Print and store the structure file name
    print("relevant MRO structure file")
    structure_file = local_path + structure_file
    print(structure_file)

    # Return filenames lists for clock files, orientation kernels, tropospheric and ionospheric corrections, odf files,
    # trajectory files, MRO reference frames file, and MRO structure file.
    return (
        clock_files,
        orientation_files,
        tro_files,
        ion_files,
        tnf_files,
        trajectory_files,
        frames_def_file,
        structure_file,
    )

2. Residuals Analysis Function#

This function performs Doppler and range residual analysis for the MRO spacecraft by adopting the following approach:

Step 1: Load SPICE kernels and data files

  • Standard SPICE kernels (planetary ephemerides, leap seconds, etc.)

  • MRO-specific orientation kernels (spacecraft and antenna attitude)

  • Clock correlation files

  • Trajectory files (spacecraft ephemeris)

  • Reference frames and structure files

Step 2: Create the dynamical environment

  • Set up celestial bodies (Earth, Sun, Mars, etc.) with appropriate models

  • Configure Earth with oblate spherical shape, high-precision rotation model (IAU 2006), and DSN ground stations

  • Create MRO spacecraft with translational and rotational ephemerides from SPICE

Step 3: Load and pre-process TNF observations

  • Load Doppler and range measurements from TNF files

  • Filter out observations on dates with incomplete orientation kernel coverage

  • Split observation sets at discontinuities in kernel coverage

  • Compress Doppler observations from 1-second to 60-second integration time

  • Set transponder delay for MRO

Step 4: Set antenna as reference point

  • Account for offset between MRO center-of-mass and spacecraft-fixed frame origin

  • Create tabulated ephemeris for the antenna position relative to center-of-mass

  • Set antenna as the reference point for all link ends (crucial for accurate Doppler modeling)

Step 5: Define observation model settings

  • Configure light-time corrections:

    • Second-order relativistic effects (Sun)

    • Tropospheric corrections (from tabulated files)

    • Ionospheric corrections (from tabulated files)

    • Solar corona corrections (for range only, based on MEX 2011 data)

  • Create observation simulators for both Doppler and range observables

  • Add dependent variables: elevation angle and Sun-Earth-Probe (SEP) angle

Step 6: Compute residuals and save results

  • Simulate synthetic observables using the observation models

  • Compute residuals as the difference between real (TNF) and simulated observations

  • Filter outliers (residuals > 0.1 Hz for Doppler, > 40 RU for range)

  • Save various outputs: residuals, RMS/mean statistics, time bounds, elevation/SEP angles, range conversion factors

  • For the first run (index 0), save detailed first-day results for visualization


Input arguments:

The inputs variable is a list with eleven entries:

  1. Index of the current run (for parallel execution)

  2. Start date of the time interval

  3. End date of the time interval

  4. List of TNF files to load

  5. List of clock files to load

  6. List of orientation kernels to load

  7. List of tropospheric correction files to load

  8. List of ionospheric correction files to load

  9. List of MRO trajectory files to load

  10. MRO reference frames definition file to load

  11. MRO structure file to load

[4]:
def perform_residuals_analysis(inputs):
    """
    Perform residuals analysis for MRO Doppler and range observations.

    Args:
        inputs: list containing [index, start_date, end_date, tnf_files, clock_files,
                orientation_files, tro_files, ion_files, trajectory_files,
                frames_def_file, structure_file]
    """

    # Unpack various input arguments
    input_index = inputs[0]

    # Convert start and end datetime objects to Tudat Time variables. A time buffer of one day is subtracted/added to the start/end date
    # to ensure that the simulation environment covers the full time span of the loaded TNF files. This is mostly needed because some TNF
    # files - while typically assigned to a certain date - actually spans over (slightly) longer than one day. Without this time buffer,
    # some observation epochs might thus lie outside the time boundaries within which the dynamical environment is defined.
    start_time = time_representation.DateTime.from_python_datetime(inputs[1]).to_epoch() - 86400.0
    end_time = time_representation.DateTime.from_python_datetime(inputs[2]).to_epoch() + 86400.0

    # Retrieve lists of relevant kernels and input files to load (TNF files, clock and orientation kernels,
    # tropospheric and ionospheric corrections)
    tnf_files = inputs[3]
    clock_files = inputs[4]
    orientation_files = inputs[5]
    tro_files = inputs[6]
    ion_files = inputs[7]
    trajectory_files = inputs[8]
    frames_def_file = inputs[9]
    structure_file = inputs[10]

    # Redirect the outputs of this run to a file names mro_estimation_output_x.dat, with x the run index
    with util.redirect_std(
        "outputs/mro_estimation_output_" + str(input_index) + ".dat", True, True
    ):

        print("input_index", input_index)

        filename_suffix = str(input_index) + ""

        ### ------------------------------------------------------------------------------------------
        ### LOAD ALL REQUESTED KERNELS AND FILES
        ### ------------------------------------------------------------------------------------------

        spice.load_standard_kernels()

        # Load MRO orientation kernels (over the entire relevant time period).
        # Note: each orientation kernel covers a certain time interval, usually spanning over a few days.
        # It must be noted, however, that some dates are not entirely covered, i.e., there is no orientation information available over
        # some short periods of time. This typically happens on dates coinciding with the transition from one orientation file to the
        # next one. As will be shown later in the examples, this requires the user to manually specify which dates should be overlooked,
        # and to filter out observations that were recorded on such dates.
        for orientation_file in orientation_files:
            spice.load_kernel(orientation_file)

        # Load MRO clock files
        for clock_file in clock_files:
            spice.load_kernel(clock_file)

        # Load MRO frame definition file (useful for HGA and spacecraft-fixed frames definition)
        spice.load_kernel(frames_def_file)

        # Load MRO trajectory kernels
        for trajectory_file in trajectory_files:
            spice.load_kernel(trajectory_file)

        # Load MRO spacecraft structure file (for antenna position in spacecraft-fixed frame)
        spice.load_kernel(structure_file)

        ### ------------------------------------------------------------------------------------------
        ### CREATE DYNAMICAL ENVIRONMENT
        ### ------------------------------------------------------------------------------------------

        # Create default body settings for celestial bodies
        bodies_to_create = [
            "Earth",
            "Sun",
            "Mercury",
            "Venus",
            "Mars",
            "Jupiter",
            "Saturn",
            "Moon",
        ]
        global_frame_origin = "SSB"
        global_frame_orientation = "J2000"
        body_settings = environment_setup.get_default_body_settings_time_limited(
            bodies_to_create,
            start_time,
            end_time,
            global_frame_origin,
            global_frame_orientation,
        )

        # Modify Earth default settings
        body_settings.get("Earth").shape_settings = (
            environment_setup.shape.oblate_spherical_spice()
        )
        body_settings.get("Earth").rotation_model_settings = (
            environment_setup.rotation_model.gcrs_to_itrs(
                environment_setup.rotation_model.iau_2006,
                global_frame_orientation,
                interpolators.interpolator_generation_settings(
                    interpolators.cubic_spline_interpolation(),
                    start_time,
                    end_time,
                    3600.0,
                ),
                interpolators.interpolator_generation_settings(
                    interpolators.cubic_spline_interpolation(),
                    start_time,
                    end_time,
                    3600.0,
                ),
                interpolators.interpolator_generation_settings(
                    interpolators.cubic_spline_interpolation(),
                    start_time,
                    end_time,
                    60.0,
                ),
            )
        )
        body_settings.get("Earth").gravity_field_settings.associated_reference_frame = (
            "ITRS"
        )
        body_settings.get("Earth").ground_station_settings = (
            environment_setup.ground_station.dsn_stations()
        )

        # Create empty settings for the MRO spacecraft
        spacecraft_name = "MRO"
        spacecraft_central_body = "Mars"
        body_settings.add_empty_settings(spacecraft_name)

        # Retrieve translational ephemeris from SPICE
        body_settings.get(spacecraft_name).ephemeris_settings = (
            environment_setup.ephemeris.interpolated_spice(
                start_time,
                end_time,
                10.0,
                spacecraft_central_body,
                global_frame_orientation,
            )
        )

        # Retrieve rotational ephemeris from SPICE
        body_settings.get(spacecraft_name).rotation_model_settings = (
            environment_setup.rotation_model.spice(
                global_frame_orientation, spacecraft_name + "_SPACECRAFT", ""
            )
        )

        # Create environment
        bodies = environment_setup.create_system_of_bodies(body_settings)

        ### ------------------------------------------------------------------------------------------
        ### LOAD TNF OBSERVATIONS AND PERFORM PRE-PROCESSING STEPS
        ### ------------------------------------------------------------------------------------------

        # Create observation collection from TNF files, only retaining Doppler and sequential range observations. An observation collection contains
        # multiple "observation sets". Within a given observation set, the observables are of the same type (here Doppler and range) and defined from the same link ends.
        # However, within the "global" observation collection, multiple observation sets can typically be found for a given observable type and link ends, but they
        # will cover different observation time intervals. When loading TNF data, a separate observation set is created for each TNF file (which means the time intervals of each
        # set match those of the corresponding TNF file).

        # Dates to filter out because of incomplete spacecraft orientation kernels (see note when loading orientation kernels on line 168)
        dates_to_filter = [
            time_representation.DateTime(2012, 10, 15, 0, 0, 0.0),
            time_representation.DateTime(2012, 10, 30, 0, 0, 0),
            time_representation.DateTime(2012, 11, 6, 0, 0, 0),
            time_representation.DateTime(2012, 11, 7, 0, 0, 0),
        ]

        # Remove latest TNF file.
        # This step is necessary because some TNF observation sets - although time-tagged to a specific date - might spill over the next day.
        # For the latest TNF data set, this might imply stepping outside the time interval that the loaded spice kernels cover.
        tnf_files = tnf_files[:-1]

        tnfProcessor = Trk234Processor(
            tnf_files, ["doppler", "range"], spacecraft_name="MRO"
        )
        original_odf_observations = tnfProcessor.process()
        tnfProcessor.set_tnf_information_in_bodies(bodies)

        # Filter out observations on dates when orientation kernels are incomplete
        dates_to_filter_float = []
        for date in dates_to_filter:
            dates_to_filter_float.append(date.to_epoch())
            # Create filter object for specific date
            date_filter = observations.observations_processing.observation_filter(
                observations.observations_processing.ObservationFilterType.time_bounds_filtering,
                date.to_epoch() - 3600.0,
                time_representation.add_days_to_datetime(date, 1.0).to_epoch() + 0.0,
            )
            # Filter out observations from observation collection
            original_odf_observations.filter_observations(date_filter)

        # Remove empty observation sets, if there is any once the filtering is completed
        original_odf_observations.remove_empty_observation_sets()

        # Split observation sets at dates when orientation kernels are incomplete.
        # While all problematic observation epochs have already been filtered out in the previous step, the splitting is still necessary
        # to ensure that the time span of each observation set is fully covered by the available kernels. Without this additional step,
        # one could not parse from the lower to upper time bounds of a given observation set without risking accessing unavailable information from spice.
        # This step only becomes relevant when retrieving the position of the MRO antenna over the observation time intervals, as will be done later in the example.
        date_splitter = observations.observations_processing.observation_set_splitter(
            observations.observations_processing.ObservationSetSplitterType.time_tags_splitter,
            dates_to_filter_float,
        )
        original_odf_observations.split_observation_sets(date_splitter)

        # Remove empty observation sets, if there is any once both the splitting is completed
        original_odf_observations.remove_empty_observation_sets()

        print("original_odf_observations")
        original_odf_observations.print_observation_sets_start_and_size()

        # Compress Doppler observations from 1.0 s integration time to 60.0 s
        compressed_observations = observations_setup.observations_wrapper.create_compressed_doppler_collection(
            original_odf_observations, 60, 10
        )
        print("Compressed observations: ")
        print(compressed_observations.concatenated_observations.size)

        # Add transpondr delay
        compressed_observations.set_transponder_delay("MRO", 1.4149e-6)

        ### ------------------------------------------------------------------------------------------
        ### SET ANTENNA AS REFERENCE POINT FOR DOPPLER OBSERVATIONS
        ### ------------------------------------------------------------------------------------------

        # Define MRO center-of-mass (COM) position w.r.t. the origin of the MRO-fixed reference frame (frame spice ID: MRO_SPACECRAFT)
        # This value was taken from Konopliv et al. (2011) doi:10.1016/j.icarus.2010.10.004
        # This is necessary to define the position of the antenna w.r.t. the COM, in the MRO-fixed frame (see below)
        com_position = np.array([0.0, -1.11, 0.0])

        # In the following lines, we create a tabulated history of the position of the MRO antenna with respect to the COM, in the MRO-fixed frame
        # (MRO_SPACECRAFT). This will be used to create a tabulated ephemeris for the antenna, necessary to correct for the position of the spacecraft's
        # reference point (antenna) when computing the Doppler observables.
        # This (manual) extra step is required to account for the offset between the COM and the origin of the MRO-fixed frame, as using spice kernels directly
        # would only provide the antenna position w.r.t. the origin of the MRO-fixed frame (no information on the COM position).
        antenna_position_history = dict()

        # Parsing the observation times in all observation sets.
        # Note: we use a time buffer of one hour with respect to the start and end times of each observation set.
        # This is to ensure that the antenna position history spans over a slightly extended time interval than the observation epochs
        # of the given set. When simulating Doppler data to compute the MRO residuals, we might indeed need to access the antenna position
        # slightly outside the exact time bounds defined by the observation epochs because of the light-time delay.
        for obs_times in compressed_observations.get_observation_times_objects():
            time = obs_times[0].to_float() - 3600.0
            while time <= obs_times[-1].to_float() + 3600.0:
                state = np.zeros((6, 1))

                # For each observation epoch, retrieve the antenna position (spice ID "-74214") w.r.t. the origin of the MRO-fixed frame (spice ID "-74000")
                state[:3, 0] = spice.get_body_cartesian_position_at_epoch(
                    "-74214", "-74000", "MRO_SPACECRAFT", "none", time
                )

                # Translate the antenna position to account for the offset between the origin of the MRO-fixed frame and the COM
                state[:3, 0] = state[:3, 0] - com_position

                # Store antenna position w.r.t. COM in the MRO-fixed frame
                antenna_position_history[time] = state
                time += 60.0

        # Create tabulated ephemeris settings from antenna position history
        antenna_ephemeris_settings = environment_setup.ephemeris.tabulated(
            antenna_position_history, "-74000", "MRO_SPACECRAFT"
        )

        # Create tabulated ephemeris for the MRO antenna
        antenna_ephemeris = environment_setup.ephemeris.create_ephemeris(
            antenna_ephemeris_settings, "Antenna"
        )

        # Set the spacecraft's reference point position to that of the antenna (in the MRO-fixed frame)
        compressed_observations.set_reference_point(
            bodies,
            antenna_ephemeris,
            "Antenna",
            "MRO",
            observable_models_setup.links.LinkEndType.reflector1,
        )

        ### ------------------------------------------------------------------------------------------
        ### DEFINE SETTINGS TO SIMULATE OBSERVATIONS AND COMPUTE RESIDUALS
        ### ------------------------------------------------------------------------------------------

        #  Create light-time corrections list
        light_time_correction_list = list()
        light_time_correction_list.append(
            observable_models_setup.light_time_corrections.approximated_second_order_relativistic_light_time_correction(
                ["Sun"]
            )
        )

        # Add tropospheric correction
        light_time_correction_list.append(
            observable_models_setup.light_time_corrections.dsn_tabulated_tropospheric_light_time_correction(
                tro_files
            )
        )

        # Add ionospheric correction
        spacecraft_name_per_id = dict()
        spacecraft_name_per_id[74] = "MRO"
        light_time_correction_list.append(
            observable_models_setup.light_time_corrections.dsn_tabulated_ionospheric_light_time_correction(
                ion_files, spacecraft_name_per_id
            )
        )

        # Create observation model settings for the Doppler observables. This first implies creating the link ends defining all relevant
        # tracking links between various ground stations and the MRO spacecraft. The list of light-time corrections defined above is then
        # added to each of these link ends.
        doppler_link_ends = compressed_observations.link_definitions_per_observable[
            estimation.observable_models_setup.model_settings.dsn_n_way_averaged_doppler_type
        ]

        observation_model_settings = list()
        for current_link_definition in doppler_link_ends:
            observation_model_settings.append(
                observable_models_setup.model_settings.dsn_n_way_doppler_averaged(
                    current_link_definition, light_time_correction_list
                )
            )

        # Add solar corona correction only for range observables.
        # Values form Verma et al. (2013), from MEX 2011 solar conjunction data
        light_time_correction_list.append(
            observable_models_setup.light_time_corrections.inverse_power_series_solar_corona_light_time_correction(
                [1.70e12],
                [2.44],
            )
        )

        # Create observation model settings for the range observables (similar to the Doppler observables).
        range_link_ends = compressed_observations.link_definitions_per_observable[
            estimation.observable_models_setup.model_settings.dsn_n_way_range_type
        ]

        for current_link_definition in range_link_ends:
            observation_model_settings.append(
                observable_models_setup.model_settings.dsn_n_way_range(
                    current_link_definition, light_time_correction_list
                )
            )

        # Create observation simulators.
        observation_simulators = observations_setup.observations_simulation_settings.create_observation_simulators(
            observation_model_settings, bodies
        )

        # Add elevation and SEP angles dependent variables to the compressed observation collection
        elevation_angle_settings = observations_setup.observations_dependent_variables.elevation_angle_dependent_variable(
            observable_models_setup.links.LinkEndType.receiver
        )
        elevation_angle_parser = compressed_observations.add_dependent_variable(
            elevation_angle_settings
        )
        sep_angle_settings = observations_setup.observations_dependent_variables.avoidance_angle_dependent_variable(
            "Sun",
            observable_models_setup.links.LinkEndType.retransmitter,
            observable_models_setup.links.LinkEndType.receiver,
        )
        sep_angle_parser = compressed_observations.add_dependent_variable(
            sep_angle_settings
        )

        # Compute and set residuals in the compressed observation collection
        observations.compute_residuals_and_dependent_variables(
            compressed_observations, observation_simulators, bodies
        )

        ### ------------------------------------------------------------------------------------------
        ### RETRIEVE AND SAVE VARIOUS OBSERVATION OUTPUTS
        ### ------------------------------------------------------------------------------------------

        for obsType, obsName in zip(
            [
                estimation.observable_models_setup.model_settings.dsn_n_way_range_type,
                estimation.observable_models_setup.model_settings.dsn_n_way_averaged_doppler_type,
            ],
            ["range", "doppler"],
        ):
            obsType_parser = observations.observations_processing.observation_parser(
                obsType
            )

            parsed_observations = observations.create_new_observation_collection(
                compressed_observations, obsType_parser
            )

            # Retrieve RMS and mean of the residuals, sorted per observation set
            rms_residuals = parsed_observations.get_rms_residuals()
            mean_residuals = parsed_observations.get_mean_residuals()

            np.savetxt(
                "outputs/mro_{}_unfiltered_residuals_rms_".format(obsName)
                + filename_suffix
                + ".dat",
                np.vstack(rms_residuals),
                delimiter=",",
            )
            np.savetxt(
                "outputs/mro_{}_unfiltered_residuals_mean_".format(obsName)
                + filename_suffix
                + ".dat",
                np.vstack(mean_residuals),
                delimiter=",",
            )

            # Retrieve the time bounds of each observation set within the observation collection
            time_bounds_per_set = (
                parsed_observations.get_time_bounds_per_set_time_object(obsType_parser)
            )
            time_bounds_array = np.zeros((len(time_bounds_per_set), 2))
            for j in range(len(time_bounds_per_set)):
                time_bounds_array[j, 0] = time_bounds_per_set[j][0].to_float()
                time_bounds_array[j, 1] = time_bounds_per_set[j][1].to_float()

            # Save time bounds of the (unfiltered) observation sets
            np.savetxt(
                "outputs/mro_{}_unfiltered_time_bounds_".format(obsName)
                + filename_suffix
                + ".dat",
                time_bounds_array,
                delimiter=",",
            )

            # Filter out outliers (i.e.,
            if obsName == "doppler":
                threshold = 0.1  # residuals > 0.1 Hz
            elif obsName == "range":
                threshold = 40  # residuals > 40 RU

            filter_residuals = observations.observations_processing.observation_filter(
                observations.observations_processing.ObservationFilterType.residual_filtering,
                threshold,
            )
            parsed_observations.filter_observations(filter_residuals, obsType_parser)

            # Remove empty observation sets, if there is any once the filtering is performed
            parsed_observations.remove_empty_observation_sets()

            # Save unfiltered residuals, observation times and link end IDs.
            np.savetxt(
                "outputs/mro_{}_filtered_residuals_".format(obsName)
                + filename_suffix
                + ".dat",
                parsed_observations.get_concatenated_residuals(),
                delimiter=",",
            )
            np.savetxt(
                "outputs/mro_{}_filtered_time_".format(obsName)
                + filename_suffix
                + ".dat",
                parsed_observations.concatenated_times,
                delimiter=",",
            )

            # Retrieve RMS and mean residuals after outliers filtering
            rms_filtered_residuals = parsed_observations.get_rms_residuals()
            mean_filtered_residuals = parsed_observations.get_mean_residuals()

            # Save RMS and mean residuals
            np.savetxt(
                "outputs/mro_{}_filtered_residuals_rms_".format(obsName)
                + filename_suffix
                + ".dat",
                np.vstack(rms_filtered_residuals),
                delimiter=",",
            )
            np.savetxt(
                "outputs/mro_{}_filtered_residuals_mean_".format(obsName)
                + filename_suffix
                + ".dat",
                np.vstack(mean_filtered_residuals),
                delimiter=",",
            )

            # Retrieve time bounds per observation set
            time_bounds_per_filtered_set = (
                parsed_observations.get_time_bounds_per_set_time_object()
            )
            time_bounds_filtered_array = np.zeros(
                (len(time_bounds_per_filtered_set), 2)
            )
            for j in range(len(time_bounds_per_filtered_set)):
                time_bounds_filtered_array[j, 0] = time_bounds_per_filtered_set[j][
                    0
                ].to_float()
                time_bounds_filtered_array[j, 1] = time_bounds_per_filtered_set[j][
                    1
                ].to_float()

            # Save time bounds of each observation set
            np.savetxt(
                "outputs/mro_{}_filtered_time_bounds_".format(obsName)
                + filename_suffix
                + ".dat",
                time_bounds_filtered_array,
                delimiter=",",
            )

            # Retrieve concatenated elevation angle dependent variables
            concatenated_elevation_angles = (
                parsed_observations.concatenated_dependent_variable(
                    elevation_angle_settings
                )[0]
            )

            # Retrieve concatenated SEP angle dependent variables
            concatenated_sep_angles = (
                parsed_observations.concatenated_dependent_variable(sep_angle_settings)[
                    0
                ]
            )

            # Save elevation and SEP angles
            np.savetxt(
                "outputs/mro_{}_elevation_angles_".format(obsName)
                + filename_suffix
                + ".dat",
                concatenated_elevation_angles,
                delimiter=",",
            )
            np.savetxt(
                "outputs/mro_{}_sep_angles_".format(obsName) + filename_suffix + ".dat",
                concatenated_sep_angles,
                delimiter=",",
            )

            # Retrieve range conversion factors (from RU to m) from observation ancillary settings
            if obsName == "range":
                single_obs_sets = parsed_observations.get_single_observation_sets()
                rangeConversionFactors = []

                for obs_set in single_obs_sets:
                    rangeConversionFactor = obs_set.ancillary_settings.get_float_settings(
                        observations_setup.ancillary_settings.range_conversion_factor,
                    )
                    rangeConversionFactors.append(rangeConversionFactor)

                    # This is needed because there are multiple range observation with the same
                    # time tag.
                    if obs_set.total_observation_set_size > 1:
                        rangeConversionFactors.append(rangeConversionFactor)

                np.savetxt(
                    "outputs/mro_range_conversion_factors_" + filename_suffix + ".dat",
                    rangeConversionFactors,
                    delimiter=",",
                )

            if input_index == 0:
                # Create observation parser to retrieve observation-related quantities over the first day of data
                # (starting from the first observation epoch)
                # first_day_parser = estimation.observation_parser((start_time + 2.0 * 86400.0, start_time + 3.0 * 86400.0))
                start_obs_times = time_bounds_per_filtered_set[0][0].to_float()
                first_day_parser = (
                    observations.observations_processing.observation_parser(
                        (start_obs_times, start_obs_times + 86400.0)
                    )
                )

                # Retrieve residuals, observation times and dependent variables over the first day
                first_day_observation_times = (
                    parsed_observations.get_concatenated_observation_times(
                        first_day_parser
                    )
                )
                first_day_residuals = parsed_observations.get_concatenated_residuals(
                    first_day_parser
                )
                first_day_elevation_angles = (
                    parsed_observations.concatenated_dependent_variable(
                        elevation_angle_settings, observation_parser=first_day_parser
                    )[0]
                )
                first_day_link_ends_ids = (
                    parsed_observations.get_concatenated_link_definition_ids(
                        first_day_parser
                    )
                )

                # Save first day results
                np.savetxt(
                    "outputs/mro_{}_first_day_residuals.dat".format(obsName),
                    first_day_residuals,
                    delimiter=",",
                )
                np.savetxt(
                    "outputs/mro_{}_first_day_times.dat".format(obsName),
                    first_day_observation_times,
                    delimiter=",",
                )
                np.savetxt(
                    "outputs/mro_{}_first_day_link_end_ids.dat".format(obsName),
                    first_day_link_ends_ids,
                    delimiter=",",
                )
                np.savetxt(
                    "outputs/mro_{}_first_day_elevation_angles.dat".format(obsName),
                    first_day_elevation_angles,
                    delimiter=",",
                )

                if obsName == "range":
                    first_day_range_conversion_factors = []

                    for obs_set in single_obs_sets:
                        rangeConversionFactor = obs_set.ancillary_settings.get_float_settings(
                            observations_setup.ancillary_settings.range_conversion_factor,
                        )
                        first_day_range_conversion_factors.append(rangeConversionFactor)

                    np.savetxt(
                        "outputs/mro_range_conversion_factors_first_day.dat",
                        first_day_range_conversion_factors,
                        delimiter=",",
                    )

3. Output Folder Setup Function#

Function to create the outputs directory if it doesn’t exist and clear its contents if it does.

[5]:
def setup_outputs_folder(folder_path):
    """Create outputs folder or clear it if it already exists."""
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)
    else:
        # Check if the directory is empty
        if os.listdir(folder_path):
            # Remove all contents of the directory
            for filename in os.listdir(folder_path):
                file_path = os.path.join(folder_path, filename)
                try:
                    if os.path.isfile(file_path) or os.path.islink(file_path):
                        os.unlink(file_path)
                    elif os.path.isdir(file_path):
                        shutil.rmtree(file_path)
                except Exception as e:
                    print(f"Failed to delete {file_path}. Reason: {e}")

Main Analysis#

Setup#

[6]:
inputs = []

# Setup the outputs folder
setup_outputs_folder("outputs")

# Specify the number of cores over which this example is to run
nb_cores = 6

# Define start and end dates for the six time intervals to be analysed in parallel computations.
# Each parallel run covers two months of data for the example to parse a total timespan of one year.
start_dates = [
    datetime(2012, 1, 1),
    datetime(2012, 3, 1),
    datetime(2012, 5, 1),
    datetime(2012, 7, 1),
    datetime(2012, 9, 1),
    datetime(2012, 11, 1),
]

end_dates = [
    datetime(2012, 2, 29),
    datetime(2012, 4, 30),
    datetime(2012, 6, 30),
    datetime(2012, 8, 31),
    datetime(2012, 10, 31),
    datetime(2012, 12, 31),
]

# For each parallel run
for i in range(nb_cores):
    # First retrieve the names of all the relevant kernels and data files necessary to cover the specified time interval
    (
        clock_files,
        orientation_files,
        tro_files,
        ion_files,
        odf_files,
        trajectory_files,
        frames_def_file,
        structure_file,
    ) = get_mro_files("mro_kernels/", start_dates[i], end_dates[i])

    # Construct a list of input arguments containing the arguments needed this specific parallel run.
    # These include the start and end dates, along with the names of all relevant kernels and data files that should be loaded
    inputs.append(
        [
            i,
            start_dates[i],
            end_dates[i],
            odf_files,
            clock_files,
            orientation_files,
            tro_files,
            ion_files,
            trajectory_files,
            frames_def_file,
            structure_file,
        ]
    )
---------------------------------------------
Download MRO clock
relevant clock files
mro_kernels/mro_sclkscet_00112_65536.tsc
---------------------------------------------
Download MRO orientation kernels
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
relevant orientation files
mro_kernels/mro_sc_psp_120131_120206.bc
mro_kernels/mro_sc_psp_120228_120305.bc
mro_kernels/mro_sc_psp_120103_120109.bc
mro_kernels/mro_sc_psp_120110_120116.bc
mro_kernels/mro_sc_psp_120207_120213.bc
mro_kernels/mro_sc_psp_120117_120123.bc
mro_kernels/mro_sc_psp_111227_120102.bc
mro_kernels/mro_sc_psp_120124_120130.bc
mro_kernels/mro_sc_psp_120214_120220.bc
mro_kernels/mro_sc_psp_120221_120227.bc
mro_kernels/mro_hga_psp_120117_120123.bc
mro_kernels/mro_hga_psp_120221_120227.bc
mro_kernels/mro_hga_psp_120228_120305.bc
mro_kernels/mro_hga_psp_120124_120130.bc
mro_kernels/mro_hga_psp_120207_120213.bc
mro_kernels/mro_hga_psp_120103_120109.bc
mro_kernels/mro_hga_psp_111227_120102.bc
mro_kernels/mro_hga_psp_120131_120206.bc
mro_kernels/mro_hga_psp_120214_120220.bc
mro_kernels/mro_hga_psp_120110_120116.bc
---------------------------------------------
Download MRO tropospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant tropospheric corrections files
mro_kernels/mromagr2012_001_2012_032.tro
mro_kernels/mromagr2012_032_2012_061.tro
mro_kernels/mromagr2011_335_2012_001.tro
---------------------------------------------
Download MRO ionospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant ionospheric corrections files
mro_kernels/mromagr2012_001_2012_032.ion
mro_kernels/mromagr2012_032_2012_061.ion
mro_kernels/mromagr2011_335_2012_001.ion
---------------------------------------------
Download MRO TNF files
nb existing files 328
relevant TNF files
mro_kernels/mromagr2012_001_2220xmmmv1.tnf
mro_kernels/mromagr2012_002_1426xmmmv1.tnf
mro_kernels/mromagr2012_003_1407xmmmv1.tnf
mro_kernels/mromagr2012_004_1550xmmmv1.tnf
mro_kernels/mromagr2012_005_1255xmmmv1.tnf
mro_kernels/mromagr2012_006_1355xmmmv1.tnf
mro_kernels/mromagr2012_007_1640xmmmv1.tnf
mro_kernels/mromagr2012_008_2200xmmmv1.tnf
mro_kernels/mromagr2012_009_0545xmmmv1.tnf
mro_kernels/mromagr2012_010_1345xmmmv1.tnf
mro_kernels/mromagr2012_011_1900xmmmv1.tnf
mro_kernels/mromagr2012_012_0620xmmmv1.tnf
mro_kernels/mromagr2012_013_1405xmmmv1.tnf
mro_kernels/mromagr2012_014_2250xmmmv1.tnf
mro_kernels/mromagr2012_016_0520xmmmv1.tnf
mro_kernels/mromagr2012_017_0520xmmmv1.tnf
mro_kernels/mromagr2012_018_2200xmmmv1.tnf
mro_kernels/mromagr2012_019_1230xmmmv1.tnf
mro_kernels/mromagr2012_020_1315xmmmv1.tnf
mro_kernels/mromagr2012_022_0505xmmmv1.tnf
mro_kernels/mromagr2012_023_1130xmmmv1.tnf
mro_kernels/mromagr2012_024_1326xmmmv1.tnf
mro_kernels/mromagr2012_025_1150xmmmv1.tnf
mro_kernels/mromagr2012_026_1251xmmmv1.tnf
mro_kernels/mromagr2012_027_1200xmmmv1.tnf
mro_kernels/mromagr2012_029_0055xmmmv1.tnf
mro_kernels/mromagr2012_030_1147xmmmv1.tnf
mro_kernels/mromagr2012_031_1217xmmmv1.tnf
mro_kernels/mromagr2012_032_1055xmmmv1.tnf
mro_kernels/mromagr2012_033_1400xmmmv1.tnf
mro_kernels/mromagr2012_034_1205xmmmv1.tnf
mro_kernels/mromagr2012_035_0505xmmmv1.tnf
mro_kernels/mromagr2012_036_1625xmmmv1.tnf
mro_kernels/mromagr2012_037_1730xmmmv1.tnf
mro_kernels/mromagr2012_038_1452xmmmv1.tnf
mro_kernels/mromagr2012_039_2115xmmmv1.tnf
mro_kernels/mromagr2012_040_1950xmmmv1.tnf
mro_kernels/mromagr2012_041_1520xmmmv1.tnf
mro_kernels/mromagr2012_043_0330xmmmv1.tnf
mro_kernels/mromagr2012_044_1115xmmmv1.tnf
mro_kernels/mromagr2012_045_1115xmmmv1.tnf
mro_kernels/mromagr2012_046_1925xmmmv1.tnf
mro_kernels/mromagr2012_047_1132xmmmv1.tnf
mro_kernels/mromagr2012_048_1005xmmmv1.tnf
mro_kernels/mromagr2012_049_1925xmmmv1.tnf
mro_kernels/mromagr2012_051_0955xmmmv1.tnf
mro_kernels/mromagr2012_052_1105xmmmv1.tnf
mro_kernels/mromagr2012_053_0950xmmmv1.tnf
mro_kernels/mromagr2012_054_1218xmmmv1.tnf
mro_kernels/mromagr2012_055_0945xmmmv1.tnf
mro_kernels/mromagr2012_056_0940xmmmv1.tnf
mro_kernels/mromagr2012_057_1110xmmmv1.tnf
mro_kernels/mromagr2012_058_2140xmmmv1.tnf
mro_kernels/mromagr2012_059_1240xmmmv1.tnf
mro_kernels/mromagr2012_060_0930xmmmv1.tnf
---------------------------------------------
Download MRO trajectory files
relevant trajectory files
mro_kernels/mro_psp21.bsp
mro_kernels/mro_psp22.bsp
mro_kernels/mro_psp23.bsp
mro_kernels/mro_psp24.bsp
mro_kernels/mro_psp25.bsp
---------------------------------------------
Download MRO frames definition file
relevant MRO frames definition file
mro_kernels/mro_v16.tf
---------------------------------------------
Download MRO structure file
relevant MRO structure file
mro_kernels/mro_struct_v10.bsp
---------------------------------------------
Download MRO clock
relevant clock files
mro_kernels/mro_sclkscet_00112_65536.tsc
---------------------------------------------
Download MRO orientation kernels
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
relevant orientation files
mro_kernels/mro_sc_psp_120327_120402.bc
mro_kernels/mro_sc_psp_120228_120305.bc
mro_kernels/mro_sc_psp_120410_120416.bc
mro_kernels/mro_sc_psp_120403_120409.bc
mro_kernels/mro_sc_psp_120424_120430.bc
mro_kernels/mro_sc_psp_120417_120423.bc
mro_kernels/mro_sc_psp_120306_120312.bc
mro_kernels/mro_sc_psp_120313_120319.bc
mro_kernels/mro_sc_psp_120320_120326.bc
mro_kernels/mro_hga_psp_120306_120312.bc
mro_kernels/mro_hga_psp_120410_120416.bc
mro_kernels/mro_hga_psp_120417_120423.bc
mro_kernels/mro_hga_psp_120228_120305.bc
mro_kernels/mro_hga_psp_120403_120409.bc
mro_kernels/mro_hga_psp_120424_120430.bc
mro_kernels/mro_hga_psp_120313_120319.bc
mro_kernels/mro_hga_psp_120320_120326.bc
mro_kernels/mro_hga_psp_120327_120402.bc
---------------------------------------------
Download MRO tropospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant tropospheric corrections files
mro_kernels/mromagr2012_092_2012_122.tro
mro_kernels/mromagr2012_061_2012_092.tro
mro_kernels/mromagr2012_032_2012_061.tro
---------------------------------------------
Download MRO ionospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant ionospheric corrections files
mro_kernels/mromagr2012_092_2012_122.ion
mro_kernels/mromagr2012_032_2012_061.ion
mro_kernels/mromagr2012_061_2012_092.ion
---------------------------------------------
Download MRO TNF files
nb existing files 328
relevant TNF files
mro_kernels/mromagr2012_061_1245xmmmv1.tnf
mro_kernels/mromagr2012_062_1340xmmmv1.tnf
mro_kernels/mromagr2012_064_0135xmmmv1.tnf
mro_kernels/mromagr2012_065_2000xmmmv1.tnf
mro_kernels/mromagr2012_066_1105xmmmv1.tnf
mro_kernels/mromagr2012_067_1831xmmmv1.tnf
mro_kernels/mromagr2012_068_1924xmmmv1.tnf
mro_kernels/mromagr2012_069_1400xmmmv1.tnf
mro_kernels/mromagr2012_070_0810xmmmv1.tnf
mro_kernels/mromagr2012_071_1950xmmmv1.tnf
mro_kernels/mromagr2012_072_1000xmmmv1.tnf
mro_kernels/mromagr2012_073_1017xmmmv1.tnf
mro_kernels/mromagr2012_074_1940xmmmv1.tnf
mro_kernels/mromagr2012_075_0850xmmmv1.tnf
mro_kernels/mromagr2012_076_0840xmmmv1.tnf
mro_kernels/mromagr2012_077_1735xmmmv1.tnf
mro_kernels/mromagr2012_079_1020xmmmv1.tnf
mro_kernels/mromagr2012_080_1055xmmmv1.tnf
mro_kernels/mromagr2012_081_1247xmmmv1.tnf
mro_kernels/mromagr2012_082_0715xmmmv1.tnf
mro_kernels/mromagr2012_083_0835xmmmv1.tnf
mro_kernels/mromagr2012_084_1835xmmmv1.tnf
mro_kernels/mromagr2012_085_0820xmmmv1.tnf
mro_kernels/mromagr2012_086_0850xmmmv1.tnf
mro_kernels/mromagr2012_087_0935xmmmv1.tnf
mro_kernels/mromagr2012_088_1020xmmmv1.tnf
mro_kernels/mromagr2012_089_1534xmmmv1.tnf
mro_kernels/mromagr2012_090_1015xmmmv1.tnf
mro_kernels/mromagr2012_091_1525xmmmv1.tnf
mro_kernels/mromagr2012_092_0805xmmmv1.tnf
mro_kernels/mromagr2012_093_0620xmmmv1.tnf
mro_kernels/mromagr2012_094_0605xmmmv1.tnf
mro_kernels/mromagr2012_095_1458xmmmv1.tnf
mro_kernels/mromagr2012_096_0750xmmmv1.tnf
mro_kernels/mromagr2012_097_1019xmmmv1.tnf
mro_kernels/mromagr2012_098_0000xmmmv1.tnf
mro_kernels/mromagr2012_099_0150xmmmv1.tnf
mro_kernels/mromagr2012_100_0640xmmmv1.tnf
mro_kernels/mromagr2012_101_1514xmmmv1.tnf
mro_kernels/mromagr2012_102_1709xmmmv1.tnf
mro_kernels/mromagr2012_103_1755xmmmv1.tnf
mro_kernels/mromagr2012_104_1715xmmmv1.tnf
mro_kernels/mromagr2012_106_0645xmmmv1.tnf
mro_kernels/mromagr2012_107_0815xmmmv1.tnf
mro_kernels/mromagr2012_108_0815xmmmv1.tnf
mro_kernels/mromagr2012_109_1410xmmmv1.tnf
mro_kernels/mromagr2012_110_0615xmmmv1.tnf
mro_kernels/mromagr2012_111_0645xmmmv1.tnf
mro_kernels/mromagr2012_112_0807xmmmv1.tnf
mro_kernels/mromagr2012_113_1930xmmmv1.tnf
mro_kernels/mromagr2012_114_1715xmmmv1.tnf
mro_kernels/mromagr2012_115_0725xmmmv1.tnf
mro_kernels/mromagr2012_116_1430xmmmv1.tnf
mro_kernels/mromagr2012_117_1735xmmmv1.tnf
mro_kernels/mromagr2012_118_1645xmmmv1.tnf
mro_kernels/mromagr2012_119_0711xmmmv1.tnf
mro_kernels/mromagr2012_120_1515xmmmv1.tnf
mro_kernels/mromagr2012_121_1603xmmmv1.tnf
---------------------------------------------
Download MRO trajectory files
relevant trajectory files
mro_kernels/mro_psp21.bsp
mro_kernels/mro_psp22.bsp
mro_kernels/mro_psp23.bsp
mro_kernels/mro_psp24.bsp
mro_kernels/mro_psp25.bsp
---------------------------------------------
Download MRO frames definition file
relevant MRO frames definition file
mro_kernels/mro_v16.tf
---------------------------------------------
Download MRO structure file
relevant MRO structure file
mro_kernels/mro_struct_v10.bsp
---------------------------------------------
Download MRO clock
relevant clock files
mro_kernels/mro_sclkscet_00112_65536.tsc
---------------------------------------------
Download MRO orientation kernels
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
relevant orientation files
mro_kernels/mro_sc_psp_120612_120618.bc
mro_kernels/mro_sc_psp_120508_120514.bc
mro_kernels/mro_sc_psp_120522_120528.bc
mro_kernels/mro_sc_psp_120605_120611.bc
mro_kernels/mro_sc_psp_120515_120521.bc
mro_kernels/mro_sc_psp_120529_120604.bc
mro_kernels/mro_sc_psp_120501_120507.bc
mro_kernels/mro_sc_psp_120626_120702.bc
mro_kernels/mro_sc_psp_120619_120625.bc
mro_kernels/mro_hga_psp_120529_120604.bc
mro_kernels/mro_hga_psp_120522_120528.bc
mro_kernels/mro_hga_psp_120515_120521.bc
mro_kernels/mro_hga_psp_120508_120514.bc
mro_kernels/mro_hga_psp_120612_120618.bc
mro_kernels/mro_hga_psp_120501_120507.bc
mro_kernels/mro_hga_psp_120605_120611.bc
mro_kernels/mro_hga_psp_120619_120625.bc
mro_kernels/mro_hga_psp_120626_120702.bc
---------------------------------------------
Download MRO tropospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant tropospheric corrections files
mro_kernels/mromagr2012_092_2012_122.tro
mro_kernels/mromagr2012_122_2012_153.tro
mro_kernels/mromagr2012_153_2012_183.tro
---------------------------------------------
Download MRO ionospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant ionospheric corrections files
mro_kernels/mromagr2012_092_2012_122.ion
mro_kernels/mromagr2012_153_2012_183.ion
mro_kernels/mromagr2012_122_2012_153.ion
---------------------------------------------
Download MRO TNF files
nb existing files 328
relevant TNF files
mro_kernels/mromagr2012_122_1406xmmmv1.tnf
mro_kernels/mromagr2012_123_1345xmmmv1.tnf
mro_kernels/mromagr2012_124_1401xmmmv1.tnf
mro_kernels/mromagr2012_125_1357xmmmv1.tnf
mro_kernels/mromagr2012_126_1614xmmmv1.tnf
mro_kernels/mromagr2012_127_1315xmmmv1.tnf
mro_kernels/mromagr2012_128_1510xmmmv1.tnf
mro_kernels/mromagr2012_129_1540xmmmv1.tnf
mro_kernels/mromagr2012_130_2000xmmmv1.tnf
mro_kernels/mromagr2012_131_1450xmmmv1.tnf
mro_kernels/mromagr2012_132_1325xmmmv1.tnf
mro_kernels/mromagr2012_133_1555xmmmv1.tnf
mro_kernels/mromagr2012_134_1305xmmmv1.tnf
mro_kernels/mromagr2012_135_1822xmmmv1.tnf
mro_kernels/mromagr2012_136_2035xmmmv1.tnf
mro_kernels/mromagr2012_137_2124xmmmv1.tnf
mro_kernels/mromagr2012_138_1840xmmmv1.tnf
mro_kernels/mromagr2012_139_1745xmmmv1.tnf
mro_kernels/mromagr2012_141_0553xmmmv1.tnf
mro_kernels/mromagr2012_142_1952xmmmv1.tnf
mro_kernels/mromagr2012_143_1740xmmmv1.tnf
mro_kernels/mromagr2012_144_1300xmmmv1.tnf
mro_kernels/mromagr2012_145_1403xmmmv1.tnf
mro_kernels/mromagr2012_146_1452xmmmv1.tnf
mro_kernels/mromagr2012_148_1010xmmmv1.tnf
mro_kernels/mromagr2012_149_1529xmmmv1.tnf
mro_kernels/mromagr2012_150_1931xmmmv1.tnf
mro_kernels/mromagr2012_152_1335xmmmv1.tnf
mro_kernels/mromagr2012_152_1335xmmmv1.tnf
mro_kernels/mromagr2012_153_1339xmmmv1.tnf
mro_kernels/mromagr2012_154_2005xmmmv1.tnf
mro_kernels/mromagr2012_156_1420xmmmv1.tnf
mro_kernels/mromagr2012_157_1225xmmmv1.tnf
mro_kernels/mromagr2012_158_1530xmmmv1.tnf
mro_kernels/mromagr2012_159_1655xmmmv1.tnf
mro_kernels/mromagr2012_160_1636xmmmv1.tnf
mro_kernels/mromagr2012_162_0238xmmmv1.tnf
mro_kernels/mromagr2012_163_1402xmmmv1.tnf
mro_kernels/mromagr2012_164_1515xmmmv1.tnf
mro_kernels/mromagr2012_165_1225xmmmv1.tnf
mro_kernels/mromagr2012_166_1921xmmmv1.tnf
mro_kernels/mromagr2012_167_1505xmmmv1.tnf
mro_kernels/mromagr2012_168_2025xmmmv1.tnf
mro_kernels/mromagr2012_169_1545xmmmv1.tnf
mro_kernels/mromagr2012_170_1202xmmmv1.tnf
mro_kernels/mromagr2012_171_1805xmmmv1.tnf
mro_kernels/mromagr2012_172_1215xmmmv1.tnf
mro_kernels/mromagr2012_173_2015xmmmv1.tnf
mro_kernels/mromagr2012_174_1809xmmmv1.tnf
mro_kernels/mromagr2012_176_0658xmmmv1.tnf
mro_kernels/mromagr2012_177_1700xmmmv1.tnf
mro_kernels/mromagr2012_178_1140xmmmv1.tnf
mro_kernels/mromagr2012_179_1450xmmmv1.tnf
mro_kernels/mromagr2012_180_1250xmmmv1.tnf
mro_kernels/mromagr2012_181_1250xmmmv1.tnf
mro_kernels/mromagr2012_182_2000xmmmv1.tnf
---------------------------------------------
Download MRO trajectory files
relevant trajectory files
mro_kernels/mro_psp21.bsp
mro_kernels/mro_psp22.bsp
mro_kernels/mro_psp23.bsp
mro_kernels/mro_psp24.bsp
mro_kernels/mro_psp25.bsp
---------------------------------------------
Download MRO frames definition file
relevant MRO frames definition file
mro_kernels/mro_v16.tf
---------------------------------------------
Download MRO structure file
relevant MRO structure file
mro_kernels/mro_struct_v10.bsp
---------------------------------------------
Download MRO clock
relevant clock files
mro_kernels/mro_sclkscet_00112_65536.tsc
---------------------------------------------
Download MRO orientation kernels
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
relevant orientation files
mro_kernels/mro_sc_psp_120828_120903.bc
mro_kernels/mro_sc_psp_120703_120709.bc
mro_kernels/mro_sc_psp_120731_120806.bc
mro_kernels/mro_sc_psp_120807_120813.bc
mro_kernels/mro_sc_psp_120814_120820.bc
mro_kernels/mro_sc_psp_120821_120827.bc
mro_kernels/mro_sc_psp_120724_120730.bc
mro_kernels/mro_sc_psp_120717_120723.bc
mro_kernels/mro_sc_psp_120626_120702.bc
mro_kernels/mro_sc_psp_120710_120716.bc
mro_kernels/mro_hga_psp_120724_120730.bc
mro_kernels/mro_hga_psp_120828_120903.bc
mro_kernels/mro_hga_psp_120814_120820.bc
mro_kernels/mro_hga_psp_120717_120723.bc
mro_kernels/mro_hga_psp_120731_120806.bc
mro_kernels/mro_hga_psp_120807_120813.bc
mro_kernels/mro_hga_psp_120703_120709.bc
mro_kernels/mro_hga_psp_120821_120827.bc
mro_kernels/mro_hga_psp_120710_120716.bc
mro_kernels/mro_hga_psp_120626_120702.bc
---------------------------------------------
Download MRO tropospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant tropospheric corrections files
mro_kernels/mromagr2012_153_2012_183.tro
mro_kernels/mromagr2012_183_2012_214.tro
mro_kernels/mromagr2012_214_2012_245.tro
---------------------------------------------
Download MRO ionospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant ionospheric corrections files
mro_kernels/mromagr2012_183_2012_214.ion
mro_kernels/mromagr2012_153_2012_183.ion
mro_kernels/mromagr2012_214_2012_245.ion
---------------------------------------------
Download MRO TNF files
nb existing files 328
relevant TNF files
mro_kernels/mromagr2012_184_1700xmmmv1.tnf
mro_kernels/mromagr2012_185_1715xmmmv1.tnf
mro_kernels/mromagr2012_186_1155xmmmv1.tnf
mro_kernels/mromagr2012_187_1500xmmmv1.tnf
mro_kernels/mromagr2012_188_1645xmmmv1.tnf
mro_kernels/mromagr2012_190_0005xmmmv1.tnf
mro_kernels/mromagr2012_191_1521xmmmv1.tnf
mro_kernels/mromagr2012_192_1200xmmmv1.tnf
mro_kernels/mromagr2012_193_1111xmmmv1.tnf
mro_kernels/mromagr2012_194_1250xmmmv1.tnf
mro_kernels/mromagr2012_195_1331xmmmv1.tnf
mro_kernels/mromagr2012_197_0725xmmmv1.tnf
mro_kernels/mromagr2012_198_1418xmmmv1.tnf
mro_kernels/mromagr2012_199_1616xmmmv1.tnf
mro_kernels/mromagr2012_200_1050xmmmv1.tnf
mro_kernels/mromagr2012_201_1345xmmmv1.tnf
mro_kernels/mromagr2012_202_1815xmmmv1.tnf
mro_kernels/mromagr2012_204_0020xmmmv1.tnf
mro_kernels/mromagr2012_205_1510xmmmv1.tnf
mro_kernels/mromagr2012_206_1515xmmmv1.tnf
mro_kernels/mromagr2012_207_1200xmmmv1.tnf
mro_kernels/mromagr2012_208_1515xmmmv1.tnf
mro_kernels/mromagr2012_209_1206xmmmv1.tnf
mro_kernels/mromagr2012_210_1825xmmmv1.tnf
mro_kernels/mromagr2012_212_1040xmmmv1.tnf
mro_kernels/mromagr2012_213_1155xmmmv1.tnf
mro_kernels/mromagr2012_214_1040xmmmv1.tnf
mro_kernels/mromagr2012_215_1145xmmmv1.tnf
mro_kernels/mromagr2012_216_1206xmmmv1.tnf
mro_kernels/mromagr2012_217_1125xmmmv1.tnf
mro_kernels/mromagr2012_218_1050xmmmv1.tnf
mro_kernels/mromagr2012_219_1035xmmmv1.tnf
mro_kernels/mromagr2012_220_1035xmmmv1.tnf
mro_kernels/mromagr2012_221_1035xmmmv1.tnf
mro_kernels/mromagr2012_222_1035xmmmv1.tnf
mro_kernels/mromagr2012_223_1030xmmmv1.tnf
mro_kernels/mromagr2012_225_0155xmmmv1.tnf
mro_kernels/mromagr2012_226_1039xmmmv1.tnf
mro_kernels/mromagr2012_227_1218xmmmv1.tnf
mro_kernels/mromagr2012_228_1030xmmmv1.tnf
mro_kernels/mromagr2012_229_1120xmmmv1.tnf
mro_kernels/mromagr2012_230_1205xmmmv1.tnf
mro_kernels/mromagr2012_232_0254xmmmv1.tnf
mro_kernels/mromagr2012_233_1400xmmmv1.tnf
mro_kernels/mromagr2012_234_1321xmmmv1.tnf
mro_kernels/mromagr2012_235_1457xmmmv1.tnf
mro_kernels/mromagr2012_236_1430xmmmv1.tnf
mro_kernels/mromagr2012_237_1137xmmmv1.tnf
mro_kernels/mromagr2012_238_1850xmmmv1.tnf
mro_kernels/mromagr2012_239_1625xmmmv1.tnf
mro_kernels/mromagr2012_240_1040xmmmv1.tnf
mro_kernels/mromagr2012_241_1320xmmmv1.tnf
mro_kernels/mromagr2012_242_1035xmmmv1.tnf
mro_kernels/mromagr2012_243_1520xmmmv1.tnf
mro_kernels/mromagr2012_244_1320xmmmv1.tnf
---------------------------------------------
Download MRO trajectory files
relevant trajectory files
mro_kernels/mro_psp21.bsp
mro_kernels/mro_psp22.bsp
mro_kernels/mro_psp23.bsp
mro_kernels/mro_psp24.bsp
mro_kernels/mro_psp25.bsp
---------------------------------------------
Download MRO frames definition file
relevant MRO frames definition file
mro_kernels/mro_v16.tf
---------------------------------------------
Download MRO structure file
relevant MRO structure file
mro_kernels/mro_struct_v10.bsp
---------------------------------------------
Download MRO clock
relevant clock files
mro_kernels/mro_sclkscet_00112_65536.tsc
---------------------------------------------
Download MRO orientation kernels
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
relevant orientation files
mro_kernels/mro_sc_psp_120828_120903.bc
mro_kernels/mro_sc_psp_120911_120917.bc
mro_kernels/mro_sc_psp_120925_121001.bc
mro_kernels/mro_sc_psp_121002_121008.bc
mro_kernels/mro_sc_psp_121023_121029.bc
mro_kernels/mro_sc_psp_120918_120924.bc
mro_kernels/mro_sc_psp_120904_120910.bc
mro_kernels/mro_sc_psp_121009_121015.bc
mro_kernels/mro_sc_psp_121016_121022.bc
mro_kernels/mro_sc_psp_121030_121105.bc
mro_kernels/mro_hga_psp_120904_120910.bc
mro_kernels/mro_hga_psp_121030_121105.bc
mro_kernels/mro_hga_psp_120925_121001.bc
mro_kernels/mro_hga_psp_120828_120903.bc
mro_kernels/mro_hga_psp_121016_121022.bc
mro_kernels/mro_hga_psp_120918_120924.bc
mro_kernels/mro_hga_psp_121002_121008.bc
mro_kernels/mro_hga_psp_120911_120917.bc
mro_kernels/mro_hga_psp_121023_121029.bc
mro_kernels/mro_hga_psp_121009_121015.bc
---------------------------------------------
Download MRO tropospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant tropospheric corrections files
mro_kernels/mromagr2012_245_2012_275.tro
mro_kernels/mromagr2012_214_2012_245.tro
mro_kernels/mromagr2012_275_2012_306.tro
---------------------------------------------
Download MRO ionospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant ionospheric corrections files
mro_kernels/mromagr2012_275_2012_306.ion
mro_kernels/mromagr2012_245_2012_275.ion
mro_kernels/mromagr2012_214_2012_245.ion
---------------------------------------------
Download MRO TNF files
nb existing files 328
relevant TNF files
mro_kernels/mromagr2012_245_1703xmmmv1.tnf
mro_kernels/mromagr2012_247_0620xmmmv1.tnf
mro_kernels/mromagr2012_248_1315xmmmv1.tnf
mro_kernels/mromagr2012_249_1240xmmmv1.tnf
mro_kernels/mromagr2012_250_1406xmmmv1.tnf
mro_kernels/mromagr2012_251_1515xmmmv1.tnf
mro_kernels/mromagr2012_252_1849xmmmv1.tnf
mro_kernels/mromagr2012_254_1015xmmmv1.tnf
mro_kernels/mromagr2012_255_1445xmmmv1.tnf
mro_kernels/mromagr2012_256_1534xmmmv1.tnf
mro_kernels/mromagr2012_257_1135xmmmv1.tnf
mro_kernels/mromagr2012_258_1425xmmmv1.tnf
mro_kernels/mromagr2012_259_2050xmmmv1.tnf
mro_kernels/mromagr2012_261_1015xmmmv1.tnf
mro_kernels/mromagr2012_262_1015xmmmv1.tnf
mro_kernels/mromagr2012_263_1350xmmmv1.tnf
mro_kernels/mromagr2012_264_1200xmmmv1.tnf
mro_kernels/mromagr2012_265_1325xmmmv1.tnf
mro_kernels/mromagr2012_267_0358xmmmv1.tnf
mro_kernels/mromagr2012_268_1206xmmmv1.tnf
mro_kernels/mromagr2012_269_2300xmmmv1.tnf
mro_kernels/mromagr2012_270_1100xmmmv1.tnf
mro_kernels/mromagr2012_271_1032xmmmv1.tnf
mro_kernels/mromagr2012_272_1130xmmmv1.tnf
mro_kernels/mromagr2012_273_1740xmmmv1.tnf
mro_kernels/mromagr2012_275_1125xmmmv1.tnf
mro_kernels/mromagr2012_276_1145xmmmv1.tnf
mro_kernels/mromagr2012_277_1030xmmmv1.tnf
mro_kernels/mromagr2012_278_1125xmmmv1.tnf
mro_kernels/mromagr2012_279_1016xmmmv1.tnf
mro_kernels/mromagr2012_281_0055xmmmv1.tnf
mro_kernels/mromagr2012_282_1015xmmmv1.tnf
mro_kernels/mromagr2012_283_1200xmmmv1.tnf
mro_kernels/mromagr2012_284_1205xmmmv1.tnf
mro_kernels/mromagr2012_285_1030xmmmv1.tnf
mro_kernels/mromagr2012_286_1750xmmmv1.tnf
mro_kernels/mromagr2012_287_1844xmmmv1.tnf
mro_kernels/mromagr2012_289_1010xmmmv1.tnf
mro_kernels/mromagr2012_290_1010xmmmv1.tnf
mro_kernels/mromagr2012_291_1307xmmmv1.tnf
mro_kernels/mromagr2012_292_1010xmmmv1.tnf
mro_kernels/mromagr2012_293_1035xmmmv1.tnf
mro_kernels/mromagr2012_294_2200xmmmv1.tnf
mro_kernels/mromagr2012_296_1005xmmmv1.tnf
mro_kernels/mromagr2012_297_1014xmmmv1.tnf
mro_kernels/mromagr2012_298_1025xmmmv1.tnf
mro_kernels/mromagr2012_299_1010xmmmv1.tnf
mro_kernels/mromagr2012_300_1005xmmmv1.tnf
mro_kernels/mromagr2012_301_1715xmmmv1.tnf
mro_kernels/mromagr2012_303_1005xmmmv1.tnf
mro_kernels/mromagr2012_304_1235xmmmv1.tnf
mro_kernels/mromagr2012_305_1230xmmmv1.tnf
---------------------------------------------
Download MRO trajectory files
relevant trajectory files
mro_kernels/mro_psp21.bsp
mro_kernels/mro_psp22.bsp
mro_kernels/mro_psp23.bsp
mro_kernels/mro_psp24.bsp
mro_kernels/mro_psp25.bsp
---------------------------------------------
Download MRO frames definition file
relevant MRO frames definition file
mro_kernels/mro_v16.tf
---------------------------------------------
Download MRO structure file
relevant MRO structure file
mro_kernels/mro_struct_v10.bsp
---------------------------------------------
Download MRO clock
relevant clock files
mro_kernels/mro_sclkscet_00112_65536.tsc
---------------------------------------------
Download MRO orientation kernels
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
size_time_format 6
size_interval_format 13
nb existing files 53
dates_without_file []
relevant orientation files
mro_kernels/mro_sc_psp_121211_121217.bc
mro_kernels/mro_sc_psp_121120_121126.bc
mro_kernels/mro_sc_psp_121225_121231.bc
mro_kernels/mro_sc_psp_121127_121203.bc
mro_kernels/mro_sc_psp_121106_121112.bc
mro_kernels/mro_sc_psp_121113_121119.bc
mro_kernels/mro_sc_psp_121204_121210.bc
mro_kernels/mro_sc_psp_121218_121224.bc
mro_kernels/mro_sc_psp_121030_121105.bc
mro_kernels/mro_hga_psp_121030_121105.bc
mro_kernels/mro_hga_psp_121218_121224.bc
mro_kernels/mro_hga_psp_121204_121210.bc
mro_kernels/mro_hga_psp_121211_121217.bc
mro_kernels/mro_hga_psp_121113_121119.bc
mro_kernels/mro_hga_psp_121120_121126.bc
mro_kernels/mro_hga_psp_121106_121112.bc
mro_kernels/mro_hga_psp_121225_121231.bc
mro_kernels/mro_hga_psp_121127_121203.bc
---------------------------------------------
Download MRO tropospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant tropospheric corrections files
mro_kernels/mromagr2012_306_2012_336.tro
mro_kernels/mromagr2012_336_2013_001.tro
mro_kernels/mromagr2012_275_2012_306.tro
---------------------------------------------
Download MRO ionospheric corrections files
size_time_format 8
size_interval_format 17
nb existing files 13
dates_without_file []
relevant ionospheric corrections files
mro_kernels/mromagr2012_275_2012_306.ion
mro_kernels/mromagr2012_306_2012_336.ion
mro_kernels/mromagr2012_336_2013_001.ion
---------------------------------------------
Download MRO TNF files
nb existing files 328
relevant TNF files
mro_kernels/mromagr2012_306_1005xmmmv1.tnf
mro_kernels/mromagr2012_307_1435xmmmv1.tnf
mro_kernels/mromagr2012_309_0135xmmmv1.tnf
mro_kernels/mromagr2012_310_1005xmmmv1.tnf
mro_kernels/mromagr2012_311_1210xmmmv1.tnf
mro_kernels/mromagr2012_312_1345xmmmv1.tnf
mro_kernels/mromagr2012_313_1010xmmmv1.tnf
mro_kernels/mromagr2012_314_1115xmmmv1.tnf
mro_kernels/mromagr2012_315_1120xmmmv1.tnf
mro_kernels/mromagr2012_316_1125xmmmv1.tnf
mro_kernels/mromagr2012_317_1400xmmmv1.tnf
mro_kernels/mromagr2012_318_1735xmmmv1.tnf
mro_kernels/mromagr2012_319_1000xmmmv1.tnf
mro_kernels/mromagr2012_320_1000xmmmv1.tnf
mro_kernels/mromagr2012_321_1705xmmmv1.tnf
mro_kernels/mromagr2012_322_1835xmmmv1.tnf
mro_kernels/mromagr2012_324_1000xmmmv1.tnf
mro_kernels/mromagr2012_325_1045xmmmv1.tnf
mro_kernels/mromagr2012_326_1747xmmmv1.tnf
mro_kernels/mromagr2012_327_1700xmmmv1.tnf
mro_kernels/mromagr2012_329_1111xmmmv1.tnf
mro_kernels/mromagr2012_331_1009xmmmv1.tnf
mro_kernels/mromagr2012_332_0951xmmmv1.tnf
mro_kernels/mromagr2012_333_1210xmmmv1.tnf
mro_kernels/mromagr2012_334_0954xmmmv1.tnf
mro_kernels/mromagr2012_335_1517xmmmv1.tnf
mro_kernels/mromagr2012_336_1655xmmmv1.tnf
mro_kernels/mromagr2012_338_1008xmmmv1.tnf
mro_kernels/mromagr2012_339_1705xmmmv1.tnf
mro_kernels/mromagr2012_340_1825xmmmv1.tnf
mro_kernels/mromagr2012_341_0945xmmmv1.tnf
mro_kernels/mromagr2012_342_1050xmmmv1.tnf
mro_kernels/mromagr2012_344_0400xmmmv1.tnf
mro_kernels/mromagr2012_345_0955xmmmv1.tnf
mro_kernels/mromagr2012_346_1415xmmmv1.tnf
mro_kernels/mromagr2012_347_1004xmmmv1.tnf
mro_kernels/mromagr2012_348_2145xmmmv1.tnf
mro_kernels/mromagr2012_349_1650xmmmv1.tnf
mro_kernels/mromagr2012_351_1130xmmmv1.tnf
mro_kernels/mromagr2012_352_0930xmmmv1.tnf
mro_kernels/mromagr2012_353_1715xmmmv1.tnf
mro_kernels/mromagr2012_354_1005xmmmv1.tnf
mro_kernels/mromagr2012_355_1000xmmmv1.tnf
mro_kernels/mromagr2012_356_1355xmmmv1.tnf
mro_kernels/mromagr2012_357_1430xmmmv1.tnf
mro_kernels/mromagr2012_358_1745xmmmv1.tnf
mro_kernels/mromagr2012_360_0929xmmmv1.tnf
mro_kernels/mromagr2012_361_1015xmmmv1.tnf
mro_kernels/mromagr2012_362_1040xmmmv1.tnf
mro_kernels/mromagr2012_363_1725xmmmv1.tnf
mro_kernels/mromagr2012_365_0245xmmmv1.tnf
mro_kernels/mromagr2012_366_1030xmmmv1.tnf
---------------------------------------------
Download MRO trajectory files
relevant trajectory files
mro_kernels/mro_psp21.bsp
mro_kernels/mro_psp22.bsp
mro_kernels/mro_psp23.bsp
mro_kernels/mro_psp24.bsp
mro_kernels/mro_psp25.bsp
---------------------------------------------
Download MRO frames definition file
relevant MRO frames definition file
mro_kernels/mro_v16.tf
---------------------------------------------
Download MRO structure file
relevant MRO structure file
mro_kernels/mro_struct_v10.bsp

Run Parallel Analysis#

[7]:
# Run parallel residuals analyses over several cores
print("---------------------------------------------")
print(
    "The output of each parallel run is saved in a separate file named mro_estimation_output_x.dat, with x the index of the run "
    "(these files are saved in the ./output directory)"
)
with mp.get_context("fork").Pool(nb_cores) as pool:
    pool.map(perform_residuals_analysis, inputs)
---------------------------------------------
The output of each parallel run is saved in a separate file named mro_estimation_output_x.dat, with x the index of the run (these files are saved in the ./output directory)
input_indexinput_indexinput_indexinput_indexinput_indexinput_index      230145





original_odf_observations
original_odf_observations
original_odf_observations
original_odf_observations
original_odf_observations
Compressed observations:
28677
Compressed observations:
38809
Compressed observations:
38730
Compressed observations:
41922
original_odf_observations
Compressed observations:
44208
Compressed observations:
34665

Results Processing and Visualization#

The following cell loads all results from the parallel analyses and generates visualization plots. For each observable type (Doppler and Range), three figures are automatically created:

Plot 1: Residuals Over Time#

Doppler Residuals:

  • Displays filtered Doppler residuals over the entire year (2012)

  • Each point represents the difference between observed Doppler measurement (from TNF files) and simulated Doppler observable (computed using SPICE kernels)

  • Residuals expressed in Hertz (Hz)

  • Outliers with residuals exceeding 0.1 Hz have been filtered out

  • Y-axis limited to [-0.1, 0.1] Hz for better visualization

Range Residuals:

  • Shows filtered range residuals over the entire year (2012)

  • Each point represents the difference between observed range measurement (from TNF files) and simulated range observable (computed using SPICE kernels)

  • Left y-axis: residuals in Range Units (RU)

  • Right y-axis (red): same residuals converted to meters using range conversion factors

  • Outliers with residuals exceeding 40 RU have been filtered out

Plot 2: Statistics and Angles (6-panel figure)#

This figure provides comprehensive statistical analysis and geometric information:

Top row (RMS and Mean residuals - unfiltered):

  • Shows root-mean-square (RMS) and mean of residuals per observation set before outlier filtering

  • Useful to identify observation sets with consistently high residuals

  • Residuals expressed in Hz for Doppler, RU for Range

Middle row (RMS and Mean residuals - post-filtering):

  • Shows RMS and mean of residuals per observation set after outliers have been removed (> 0.1 Hz for Doppler, > 40 RU for Range)

  • Demonstrates the improvement in data quality after filtering

  • Allows assessment of overall measurement quality

Bottom row (Elevation and SEP angles):

  • Elevation angle: The angle between the ground station’s horizon and the line-of-sight to the spacecraft

    • Low elevation angles often correlate with higher residuals due to increased atmospheric effects

    • Longer signal paths through atmosphere at low elevations increase tropospheric delays

    • Range: 0-90 degrees

  • SEP angle (Sun-Earth-Probe angle): The angle between the Sun and the spacecraft as seen from the receiving ground station

    • Small SEP angles indicate times when the spacecraft is near solar conjunction

    • Solar plasma can affect signal quality during solar conjunctions

    • Particularly important for range measurements due to solar corona effects

Plot 3: First Day Analysis#

Doppler - First Day:

  • Detailed view of Doppler residuals over the first 24-hour period of the dataset

  • Color-coded scatter points represent residuals from different link ends (different ground station tracking configurations)

  • Red line shows elevation angle at the receiving ground station throughout the day

  • Useful for:

    • Identifying patterns in residuals related to spacecraft orbital geometry

    • Observing how residuals vary with elevation angle

    • Detecting systematic effects that repeat with the spacecraft’s orbital period

    • Comparing residual behavior across different ground station configurations

Range - First Day:

  • Detailed view of range residuals over the first 24-hour period

  • Color-coded scatter points represent residuals from different link ends

  • Red line shows elevation angle at the receiving ground station throughout the day

  • Helps to:

    • Identify daily patterns in range residuals

    • Correlate residual magnitudes with elevation angles

    • Detect systematic effects related to the orbital period

    • Compare performance across different tracking configurations

  • Range residuals shown in Range Units (RU), where conversion to meters varies depending on ranging system configuration


Note: All plots for both Doppler and Range are generated automatically in the following cell and displayed together.

[8]:
for obsName in ["range", "doppler"]:
    # Initialize lists to store results from all parallel analyses
    filtered_residuals_list = []
    filtered_times_list = []
    elevation_angles_list = []
    sep_angles_list = []
    rms_residuals_list = []
    mean_residuals_list = []
    rms_filtered_residuals_list = []
    mean_filtered_residuals_list = []
    time_bounds_list = []
    time_bounds_filtered_list = []
    range_conversion_factors_list = []

    # Load and add results from all parallel analyses
    for i in range(nb_cores):
        filtered_times_list.append(
            np.loadtxt(
                "outputs/mro_{}_filtered_time_".format(obsName) + str(i) + ".dat",
                delimiter=",",
            )
        )
        filtered_residuals_list.append(
            np.loadtxt(
                "outputs/mro_{}_filtered_residuals_".format(obsName) + str(i) + ".dat",
                delimiter=",",
            )
        )
        elevation_angles_list.append(
            np.loadtxt(
                "outputs/mro_{}_elevation_angles_".format(obsName) + str(i) + ".dat",
                delimiter=",",
            )
        )
        sep_angles_list.append(
            np.loadtxt(
                "outputs/mro_{}_sep_angles_".format(obsName) + str(i) + ".dat",
                delimiter=",",
            )
        )
        rms_residuals_list.append(
            np.loadtxt(
                "outputs/mro_{}_unfiltered_residuals_rms_".format(obsName)
                + str(i)
                + ".dat",
                delimiter=",",
            )
        )
        mean_residuals_list.append(
            np.loadtxt(
                "outputs/mro_{}_unfiltered_residuals_mean_".format(obsName)
                + str(i)
                + ".dat",
                delimiter=",",
            )
        )
        rms_filtered_residuals_list.append(
            np.loadtxt(
                "outputs/mro_{}_filtered_residuals_rms_".format(obsName)
                + str(i)
                + ".dat",
                delimiter=",",
            )
        )
        mean_filtered_residuals_list.append(
            np.loadtxt(
                "outputs/mro_{}_filtered_residuals_mean_".format(obsName)
                + str(i)
                + ".dat",
                delimiter=",",
            )
        )
        time_bounds_list.append(
            np.loadtxt(
                "outputs/mro_{}_unfiltered_time_bounds_".format(obsName)
                + str(i)
                + ".dat",
                delimiter=",",
            )
        )
        time_bounds_filtered_list.append(
            np.loadtxt(
                "outputs/mro_{}_filtered_time_bounds_".format(obsName)
                + str(i)
                + ".dat",
                delimiter=",",
            )
        )
        if obsName == "range":
            range_conversion_factors_list.append(
                np.loadtxt(
                    "outputs/mro_range_conversion_factors_" + str(i) + ".dat",
                    delimiter=",",
                )
            )

    # Concatenate the above results into single output variables
    filtered_times = np.concatenate(filtered_times_list, axis=0)
    filtered_residuals = np.concatenate(filtered_residuals_list, axis=0)
    rms_residuals = np.concatenate(rms_residuals_list, axis=0)
    mean_residuals = np.concatenate(mean_residuals_list, axis=0)
    rms_filtered_residuals = np.concatenate(rms_filtered_residuals_list, axis=0)
    mean_filtered_residuals = np.concatenate(mean_filtered_residuals_list, axis=0)
    time_bounds = np.concatenate(time_bounds_list, axis=0)
    time_bounds_filtered = np.concatenate(time_bounds_filtered_list, axis=0)
    elevation_angles = np.concatenate(elevation_angles_list, axis=0)
    sep_angles = np.concatenate(sep_angles_list, axis=0)
    if obsName == "range":
        range_conversion_factors = np.concatenate(range_conversion_factors_list, axis=0)

    # Load first day detailed results
    first_day_residuals = np.loadtxt(
        "outputs/mro_{}_first_day_residuals.dat".format(obsName)
    )
    first_day_times = np.loadtxt("outputs/mro_{}_first_day_times.dat".format(obsName))
    first_day_elevation_angles = np.loadtxt(
        "outputs/mro_{}_first_day_elevation_angles.dat".format(obsName)
    )
    first_day_link_ends_ids = np.loadtxt(
        "outputs/mro_{}_first_day_link_end_ids.dat".format(obsName)
    )
    if obsName == "range":
        first_day_range_conversion_factors = np.loadtxt(
            "outputs/mro_range_conversion_factors_first_day.dat"
        )

    if obsName == "doppler":
        supTitle = "Averaged Doppler"
        unit = "Hz"
    elif obsName == "range":
        unit = "RU"
        supTitle = "Sequential Range"

    # Plot residuals over time
    fig, ax1 = plt.subplots()
    plt.suptitle(supTitle)
    ax1.scatter(
        (filtered_times - np.min(filtered_times)) / 86400.0, filtered_residuals, s=2
    )
    ax1.grid()
    if obsName == "doppler":
        ax1.set_ylim([-0.1, 0.1])
    ax1.set_xlim([0, 365])
    ax1.set_xlabel("Time [days]")
    ax1.set_ylabel("Residuals [{}]".format(unit))

    if obsName == "range":
        ax2 = ax1.twinx()
        color = "tab:red"
        ax2.scatter(
            (filtered_times - np.min(filtered_times)) / 86400.0,
            filtered_residuals * range_conversion_factors,
            s=2,
        )

        ax2.set_ylabel("Residuals [m]", color=color)
        ax2.tick_params(axis="y", labelcolor=color)

    fig.tight_layout()

    # Plot RMS and mean residuals, both pre- and post-filtering of the outliers
    # The elevation and SEP angles are also plotted.
    fig2, axs = plt.subplots(3, 2, figsize=(10, 8))
    plt.suptitle(supTitle)

    axs[0, 0].plot(
        (time_bounds[:, 0] - np.min(time_bounds)) / 86400,
        rms_residuals,
        ".",
        markersize=1,
    )
    axs[0, 0].grid()
    if obsName == "doppler":
        axs[0, 0].set_ylim([-0.01, 0.1])
    axs[0, 0].set_xlim([0, 365])
    axs[0, 0].set_xlabel("Time [days]")
    axs[0, 0].set_ylabel("RMS residuals [{}]".format(unit))
    axs[0, 0].set_title("RMS residuals")

    axs[0, 1].plot(
        (time_bounds[:, 0] - np.min(time_bounds)) / 86400,
        mean_residuals,
        ".",
        markersize=1,
    )
    axs[0, 1].grid()
    if obsName == "doppler":
        axs[0, 1].set_ylim([-0.02, 0.02])
    axs[0, 1].set_xlim([0, 365])
    axs[0, 1].set_xlabel("Time [days]")
    axs[0, 1].set_ylabel("Mean residuals [{}]".format(unit))
    axs[0, 1].set_title("Mean residuals")

    axs[1, 0].plot(
        (time_bounds_filtered[:, 0] - np.min(time_bounds_filtered)) / 86400,
        rms_filtered_residuals,
        ".",
        markersize=1,
    )
    axs[1, 0].grid()
    if obsName == "doppler":
        axs[1, 0].set_ylim([-0.01, 0.1])
    axs[1, 0].set_xlim([0, 365])
    axs[1, 0].set_xlabel("Time [days]")
    axs[1, 0].set_ylabel("RMS residuals [{}]".format(unit))
    axs[1, 0].set_title("RMS residuals (post-filtering)")

    axs[1, 1].plot(
        (time_bounds_filtered[:, 0] - np.min(time_bounds_filtered)) / 86400,
        mean_filtered_residuals,
        ".",
        markersize=1,
    )
    axs[1, 1].grid()
    if obsName == "doppler":
        axs[1, 1].set_ylim([-0.02, 0.02])
    axs[1, 1].set_xlim([0, 365])
    axs[1, 1].set_xlabel("Time [days]")
    axs[1, 1].set_ylabel("Mean residuals [{}]".format(unit))
    axs[1, 1].set_title("Mean residuals (post-filtering)")

    axs[2, 0].plot(
        (filtered_times - np.min(filtered_times)) / 86400,
        elevation_angles * 180 / np.pi,
        ".",
        markersize=1,
    )
    axs[2, 0].grid()
    axs[2, 0].set_ylim([0, 90])
    axs[2, 0].set_xlim([0, 365])
    axs[2, 0].set_xlabel("Time [days]")
    axs[2, 0].set_ylabel("Elevation angle [deg]")
    axs[2, 0].set_title("Elevation angle")

    axs[2, 1].plot(
        (filtered_times - np.min(filtered_times)) / 86400,
        sep_angles * 180 / np.pi,
        ".",
        markersize=1,
    )
    axs[2, 1].grid()
    axs[2, 1].set_xlim([0, 365])
    axs[2, 1].set_xlabel("Time [days]")
    axs[2, 1].set_ylabel("SEP angle [deg]")
    axs[2, 1].set_title("SEP angle")

    fig2.tight_layout()

    # Plot residuals and elevation angles over one day
    fig3, ax1 = plt.subplots()
    plt.suptitle(supTitle)
    ax1.scatter(
        (first_day_times - np.min(first_day_times)) / 3600.0,
        first_day_residuals,
        c=first_day_link_ends_ids,
        s=10,
    )
    if obsName == "doppler":
        ax1.set_ylim([-0.02, 0.02])
    ax1.set_ylabel("Residuals [{}]".format(unit))

    ax2 = ax1.twinx()
    color = "tab:red"
    ax2.set_ylabel("Elevation angle [deg]", color=color)
    ax2.plot(
        (first_day_times - np.min(first_day_times)) / 3600.0,
        first_day_elevation_angles * 180 / np.pi,
        ".",
        markersize=1,
        color=color,
    )
    ax2.tick_params(axis="y", labelcolor=color)

    plt.grid()
    ax1.set_xlim([0, 24])
    ax1.set_xlabel("Time [hours]")
    fig3.tight_layout()
    ax1.set_title("Residuals over one day")

plt.show()
../../../_images/examples_tudatpy-examples_estimation_mro_tnf_residuals_analysis_14_0.png
../../../_images/examples_tudatpy-examples_estimation_mro_tnf_residuals_analysis_14_1.png
../../../_images/examples_tudatpy-examples_estimation_mro_tnf_residuals_analysis_14_2.png
../../../_images/examples_tudatpy-examples_estimation_mro_tnf_residuals_analysis_14_3.png
../../../_images/examples_tudatpy-examples_estimation_mro_tnf_residuals_analysis_14_4.png
../../../_images/examples_tudatpy-examples_estimation_mro_tnf_residuals_analysis_14_5.png