ESA pointing simulations#

To enable pointing simulation for instrument teams planning, ESA developed an XML-like syntax to create Planning Timeline Request (PTR) files. These files allow the mission team member to design custom attitude spacecraft pointing. It is readable by AGM and MAPPS softwares to detect spacecraft constrains violations, power conception and surface coverage. It can also be used to compute custom spacecraft attitude (quaternions and ck).

This format will be used for the Juice mission to defined the spacecraft pointing, and can be tested on the Juice pointing tool.

Warning

At the moment only the Juice mission is supported. To use esa-ptr in the planetary-coverage, you need to explicitly add install this optional dependency:

pip install planetary-coverage[juice]  # with pip
# or
poetry install --extras juice          # with poetry

(install with conda is not yet supported)

Details about ESA-PTR module can be found on its dedicated documentation.

Juice attitude simulation#

In order to make custom pointing simulation for Juice, we can either:

  • read a known example.ptr file, that was manually written or generated with an other tool.

  • create you own pointing object with esa-ptr module.

Create your own PTR (advanced)#

To illustrate how to create a PTR from scratch we will need a few objects. Don’t hesitate to read the ESA-PTR documentation for more details.

from ptr import PointingRequestMessage, ObsBlock, Element

Then, we can make our own function to create a custom pointing request message:

def nav_cam_ptr(start, end, target):
    """Custom NavCam PTR single observation block."""
    return PointingRequestMessage(
        ObsBlock(
            start, end,
            Element(
                'attitude',
                Element('boresight', ref='SC_Zaxis'),
                Element('target', ref=target),
                Element('phaseAngle', {'yDir': False}, ref='powerOptimised'),
                ref='track',
            ),
            metadata='Track Power Optimised C3.0',
        )
    )

Here we will have a single observation block pointing toward a given target between a given time window (start/end). For example, you can point to CALLISTO on 2030-10-31 and during 15 minutes:

ptr = nav_cam_ptr('2030-10-31T00:00:00', '2030-10-31T00:15:00', 'Callisto')
<prm>
  <body>
    <segment>
      <data>
        <timeline frame="SC">
          <block ref="OBS">
            <metadata>
              <comment> Track Power Optimised C3.0 </comment>
            </metadata>
            <startTime> 2030-10-31T00:00:00 </startTime>
            <endTime> 2030-10-31T00:15:00 </endTime>
            <attitude ref="track">
              <boresight ref="SC_Zaxis"/>
              <target ref="Callisto"/>
              <phaseAngle ref="powerOptimised">
                <yDir> False </yDir>
              </phaseAngle>
            </attitude>
          </block>
        </timeline>
      </data>
    </segment>
  </body>
</prm>

Perform the simulation and load the results in the planetary-coverage#

When you have a valid PTR, you can try to submit it to ESA Attitude Generator Module (AGM) to know if the new attitude is valid:

Hint

The 2 parameters to provide corresponds to the default metakernel attitude and a JUICE_API key to query the Juice REST-API endpoint.

ptr_simulation = ptr.simulate('juice_crema_5_0', 'JUICE_API')

Your request will be send to the Juice pointing REST API and you will receive a success of failure status. If your simulation is valid, the simulation result will contains the generated camera kernel (ck) that contains the attitude changes of the spacecraft. This ck kernel can then be loaded on the planetary-coverage like any other kernel:

You can extend a Trajectory object:

new_trajectory = original_trajectory.add_kernel(ptr_simulation)

or a TourConfig object:

new_tour = original_tour.add_kernel(ptr_simulation)

You can use it globally at the initialization of a TourConfig object (and chain all the cells above):

TourConfig(
    mk='5_0',
    kernels=nav_cam_ptr('2030-10-31', '2030-10-31T00:15:00', 'Ganymede').simulate('juice_crema_5_0', 'JUICE_API'),
    spacecraft='JUICE',
    target='GANYMEDE',
)