Planet Viewing Example

This example shows how to plot geometric quantities using the frames system.

First we import numpy and the plotting module.

In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

Then we import GODOT modules and create a universe from configuration.

The universe file contains an entry to insert a keplerian orbit point, named 'SC' into the frames system.

In [2]:
from godot.core import tempo, astro
from godot import cosmos

# optionally avoid verbose logging messages
import godot.core.util as util
util.suppressLogger()

# create the universe
uni_config = cosmos.util.load_yaml('universe.yml')
uni = cosmos.Universe(uni_config)

To see what is in the universe file:

In [3]:
print (open('universe.yml').read())
version: '3.0'

spacetime:
  system: BCRS

ephemeris:
  - name: de432
    files:
      - data/ephemeris/de432.jpl
  - name: gm431
    files:
      - data/ephemeris/gm_de431.tpc

frames:
  - name: ephem1
    type: Ephem
    config:
      source: de432

  - name: ITRF
    type: AxesOrient
    config:
      model: IERS2000
      nutation: data/orient/nutation2000A.ipf
      erp: ''

  - name: SC
    type: PointOrbit
    config:
      center: Earth   # Center of orbit
      axes: ICRF      # Orbital axes (X-Y plane)
      epoch: 2014-05-23T00:00:00 TDB   # reference epoch
      sma: 40000.0 km   # Semi-major axes
      ecc: 0.01         # Eccentricity
      inc: 3.407 deg    # Inclination
      ran: 73.23959 deg # RAAN
      aop: 319.2485 deg # Argument of pericentre
      tan: 3.360110 deg # True anomaly at reference epoch
      gm: EarthGM   # GM of central body

  - name: SC_LOF
    type: AxesLocalOrbital
    config:
      center: Earth
      target: SC
      axes: CRA

constants:
  ephemeris:
    - source: gm431

geometry:

- name: SC_Mars_Earth_Occultation_Geometric
  type: Occultation
  config:
    observer: SC
    target: MarsBarycenter
    occulter: Earth
    occulterRadius: 6378.1366 km
    solveLightTime: false

- name: SC_Mars_Moon_Occultation_Geometric
  type: Occultation
  config:
    observer: SC
    target: MarsBarycenter
    occulter: Moon
    occulterRadius: 1737.400 km
    solveLightTime: false

We can check that the SC, and some planets are contained in the frames system:

In [4]:
for p in uni.frames.listPointNames():
    print(f"Frames contains point {p}")
for a in uni.frames.listAxesNames():
    print(f"Frames contains axes {a}")
Frames contains point Sun
Frames contains point Earth
Frames contains point Moon
Frames contains point PlutoBarycenter
Frames contains point NeptuneBarycenter
Frames contains point SC
Frames contains point UranusBarycenter
Frames contains point MercuryBarycenter
Frames contains point SaturnBarycenter
Frames contains point SSB
Frames contains point VenusBarycenter
Frames contains point EarthBarycenter
Frames contains point MarsBarycenter
Frames contains point JupiterBarycenter
Frames contains axes GCRS_ITRF
Frames contains axes ITRF
Frames contains axes ICRF
Frames contains axes SC_LOF
Frames contains axes GCRS_ICRF

Occultation Times

We want to compute the times when Mars is in occultation due to the Earth, as viewed from the spacecraft.

We can extract from the universe a time evaluable function for this purpose. In the universe file there are geometry functions added for occultations:

In [5]:
uni.evaluables.contains("SC_Mars_Earth_Occultation_Geometric")
Out[5]:
True

We can plot this function:

In [6]:
# specify a time grid
start = tempo.Epoch('2022-04-01T00:00:00 TDB')
end = tempo.Epoch('2022-05-01T00:00:00 TDB')
ran = tempo.EpochRange( start, end )
grid = ran.createGrid(60.0) # 60 seconds stepsize

# get the time evaluable for the occulation distance of Mars by Earth
model = uni.evaluables["SC_Mars_Earth_Occultation_Geometric"]
min_value = 200

fig = plt.figure(figsize=(9, 5))
plt.title('Mars Earth Occultation distance')
plt.xlabel('Time (MJD)')
plt.ylabel('Mars Earth Distance (km)')
times = [e.mjd() for e in grid]
values = [model.eval(e) for e in grid]
plt.plot(times, values )
plt.plot(times,[min_value]*len(times),'--')
plt.tight_layout()

It looks like this function drops below zero in this time period, which means that Mars is occulted by the Earth. We can find the exact times of these occultations as follows:

Create an event generator for when this function drops below a minimum value (zero here)

In [7]:
from godot.model import eventgen

model = uni.evaluables["SC_Mars_Earth_Occultation_Geometric"]
min_value = 200
func = model - min_value
eps = 1e-6
tol = 1e-6

generator = eventgen.EventGenerator(-func, eps, tol) # use negative to compute periods with no visibility

Then we can compute the time periods when mars is not visible due to occultations by the Earth:

In [8]:
event_grid = ran.contract(eps).createGrid(60.0)
mars_occult = generator.computeEventIntervals(event_grid)
for interval in mars_occult:
    start = interval.start().value()
    end = interval.end().value()
    print( f"Occultation from {start} to {end}, duration {end - start} secs" )
Occultation from 2022-04-15T12:52:40.063085 TDB to 2022-04-15T12:56:26.194907 TDB, duration 226.13182187360013 secs
Occultation from 2022-04-15T23:58:59.444583 TDB to 2022-04-16T00:14:17.091313 TDB, duration 917.6467290970065 secs
Occultation from 2022-04-16T10:55:45.564556 TDB to 2022-04-16T11:12:08.988598 TDB, duration 983.4240421234141 secs
Occultation from 2022-04-16T22:05:03.830898 TDB to 2022-04-16T22:26:54.125166 TDB, duration 1310.2942684960337 secs
Occultation from 2022-04-17T09:01:58.864171 TDB to 2022-04-17T09:24:43.660953 TDB, duration 1364.7967817271253 secs
Occultation from 2022-04-17T20:11:57.853153 TDB to 2022-04-17T20:38:41.117399 TDB, duration 1603.2642463395653 secs
Occultation from 2022-04-18T07:08:57.842739 TDB to 2022-04-18T07:36:32.338710 TDB, duration 1654.4959711617078 secs
Occultation from 2022-04-18T18:19:17.500828 TDB to 2022-04-18T18:50:02.086997 TDB, duration 1844.5861690710492 secs
Occultation from 2022-04-19T05:16:21.297876 TDB to 2022-04-19T05:47:56.231090 TDB, duration 1894.933214353402 secs
Occultation from 2022-04-19T16:26:53.667830 TDB to 2022-04-19T17:01:06.147684 TDB, duration 2052.4798538950413 secs
Occultation from 2022-04-20T03:24:00.798131 TDB to 2022-04-20T03:59:03.775484 TDB, duration 2102.977353606901 secs
Occultation from 2022-04-20T14:34:41.721392 TDB to 2022-04-20T15:11:57.938899 TDB, duration 2236.2175063599807 secs
Occultation from 2022-04-21T01:31:51.961067 TDB to 2022-04-21T02:09:59.359294 TDB, duration 2287.3982268267255 secs
Occultation from 2022-04-21T12:42:38.920397 TDB to 2022-04-21T13:22:40.207458 TDB, duration 2401.2870612001034 secs
Occultation from 2022-04-21T23:39:52.161226 TDB to 2022-04-22T00:20:45.612006 TDB, duration 2453.450780313751 secs
Occultation from 2022-04-22T10:50:43.483038 TDB to 2022-04-22T11:33:14.739988 TDB, duration 2551.2569499293013 secs
Occultation from 2022-04-22T21:47:59.678117 TDB to 2022-04-22T22:31:24.257347 TDB, duration 2604.5792297385515 secs
Occultation from 2022-04-23T08:58:54.173726 TDB to 2022-04-23T09:43:42.776184 TDB, duration 2688.6024576680156 secs
Occultation from 2022-04-23T19:56:13.311968 TDB to 2022-04-23T20:41:56.497729 TDB, duration 2743.185760971833 secs
Occultation from 2022-04-24T07:07:10.094167 TDB to 2022-04-24T07:54:05.217951 TDB, duration 2815.123783602964 secs
Occultation from 2022-04-24T18:04:32.187007 TDB to 2022-04-24T18:52:23.211182 TDB, duration 2871.0241748762937 secs
Occultation from 2022-04-25T05:15:30.567305 TDB to 2022-04-25T06:04:22.745667 TDB, duration 2932.178361304104 secs
Occultation from 2022-04-25T16:12:55.641224 TDB to 2022-04-25T17:02:45.061813 TDB, duration 2989.4205891056704 secs
Occultation from 2022-04-26T03:23:55.068162 TDB to 2022-04-26T04:14:35.887541 TDB, duration 3040.819378794604 secs
Occultation from 2022-04-26T14:21:23.160218 TDB to 2022-04-26T15:13:02.566144 TDB, duration 3099.4059253745145 secs
Occultation from 2022-04-27T01:32:23.180300 TDB to 2022-04-27T02:24:45.063331 TDB, duration 3141.883031163545 secs
Occultation from 2022-04-27T12:29:54.335332 TDB to 2022-04-27T13:23:16.135142 TDB, duration 3201.7998092697817 secs
Occultation from 2022-04-27T23:40:54.567170 TDB to 2022-04-28T00:34:50.613129 TDB, duration 3236.0459587189766 secs
Occultation from 2022-04-28T10:38:28.835983 TDB to 2022-04-28T11:33:26.102023 TDB, duration 3297.2660394261193 secs
Occultation from 2022-04-28T21:49:28.952575 TDB to 2022-04-28T22:44:52.817024 TDB, duration 3323.864449548337 secs
Occultation from 2022-04-29T08:47:06.390720 TDB to 2022-04-29T09:43:32.741300 TDB, duration 3386.3505801863785 secs
Occultation from 2022-04-29T19:58:06.106919 TDB to 2022-04-29T20:54:51.908936 TDB, duration 3405.8020168899147 secs
Occultation from 2022-04-30T06:55:46.773859 TDB to 2022-04-30T07:53:36.282225 TDB, duration 3469.508365943774 secs
Occultation from 2022-04-30T18:06:45.837291 TDB to 2022-04-30T19:04:48.086592 TDB, duration 3482.2493006116006 secs

Instrument Observations

Now we look at the instrument boresight, which is the frames Axes 'SC_LOF' oriented cross-radial,along track. We assume the boresight is in the along track direction. We use the existing time grid.

In [9]:
from godot.model import common
class AnglefromInstrumentBoresight(common.ScalarTimeEvaluable):
    def __init__(self,body):
        common.ScalarTimeEvaluable.__init__(self)
        self.__body = body

    def eval(self, epoch):
        vec = uni.frames.vector3('SC',self.__body,'SC_LOF',epoch)
        # compute the colatitude in the RCA frame (z-axis is along track)
        return np.pi/2 - astro.sphericalFromCart(vec)[2]

model = AnglefromInstrumentBoresight('MarsBarycenter')

boresight_angle = 20.0

fig = plt.figure(figsize=(9, 5))
plt.title('Mars Angle From Instrument Boresight')
plt.xlabel('Time (MJD)')
plt.ylabel('Mars Angle From Boresight (deg)')
times = [e.mjd() for e in grid]
values = np.rad2deg([model.eval(e) for e in grid])
plt.plot(times, values)
plt.plot(times,[boresight_angle]*len(times),'--')

plt.tight_layout()

The boresight angle to Mars drops below 20 deg, let's find the times when this happens:

In [10]:
# func is positive when Mars is in the instrument boresight
func = np.deg2rad(boresight_angle )- model
eps = 1e-6
tol = 1e-6

generator = eventgen.EventGenerator(func, eps, tol)
event_grid = ran.contract(eps).createGrid(600.0)

mars_in_fov = generator.computeEventIntervals(event_grid)

for interval in mars_in_fov:
    print(f"From {interval.start().value()} to {interval.end().value()} lasting {interval.width()} seconds")
From 2022-04-01T00:00:00.000001 TDB to 2022-04-01T00:58:57.532352 TDB lasting 3537.532351045555 seconds
From 2022-04-01T21:12:58.275951 TDB to 2022-04-01T23:09:08.093245 TDB lasting 6969.817294166597 seconds
From 2022-04-02T19:21:45.519805 TDB to 2022-04-02T21:19:17.411208 TDB lasting 7051.891402655239 seconds
From 2022-04-03T17:30:33.486927 TDB to 2022-04-03T19:29:25.500301 TDB lasting 7132.013374218635 seconds
From 2022-04-04T15:39:22.166340 TDB to 2022-04-04T17:39:32.374162 TDB lasting 7210.207822029903 seconds
From 2022-04-05T13:48:11.548228 TDB to 2022-04-05T15:49:38.046046 TDB lasting 7286.497817207128 seconds
From 2022-04-06T11:57:01.623832 TDB to 2022-04-06T13:59:42.528853 TDB lasting 7360.905021150811 seconds
From 2022-04-07T10:05:52.385347 TDB to 2022-04-07T12:09:45.835150 TDB lasting 7433.449803518185 seconds
From 2022-04-08T08:14:43.825846 TDB to 2022-04-08T10:19:47.977195 TDB lasting 7504.151349019763 seconds
From 2022-04-09T06:23:35.939207 TDB to 2022-04-09T08:29:48.966963 TDB lasting 7573.027756632349 seconds
From 2022-04-10T04:32:28.720057 TDB to 2022-04-10T06:39:48.816188 TDB lasting 7640.096131322721 seconds
From 2022-04-11T02:41:22.163725 TDB to 2022-04-11T04:49:47.536395 TDB lasting 7705.372669813493 seconds
From 2022-04-12T00:50:16.266202 TDB to 2022-04-12T02:59:45.138940 TDB lasting 7768.87273735601 seconds
From 2022-04-12T22:59:11.024100 TDB to 2022-04-13T01:09:41.635034 TDB lasting 7830.61093466665 seconds
From 2022-04-13T21:08:06.434604 TDB to 2022-04-13T23:19:37.035755 TDB lasting 7890.601151203598 seconds
From 2022-04-14T19:17:02.495414 TDB to 2022-04-14T21:29:31.352017 TDB lasting 7948.856603007404 seconds
From 2022-04-15T17:25:59.204665 TDB to 2022-04-15T19:39:24.594518 TDB lasting 8005.389852747786 seconds
From 2022-04-16T15:34:56.560816 TDB to 2022-04-16T17:49:16.773630 TDB lasting 8060.212814477241 seconds
From 2022-04-17T13:43:54.562523 TDB to 2022-04-17T15:59:07.899270 TDB lasting 8113.336747167268 seconds
From 2022-04-18T11:52:53.208509 TDB to 2022-04-18T14:08:57.980758 TDB lasting 8164.772248589558 seconds
From 2022-04-19T10:01:52.497432 TDB to 2022-04-19T12:18:47.026692 TDB lasting 8214.529259622794 seconds
From 2022-04-20T08:10:52.427796 TDB to 2022-04-20T10:28:35.044885 TDB lasting 8262.617089513962 seconds
From 2022-04-21T06:19:52.997901 TDB to 2022-04-21T08:38:22.042366 TDB lasting 8309.044464616463 seconds
From 2022-04-22T04:28:54.205847 TDB to 2022-04-22T06:48:08.025445 TDB lasting 8353.81959823593 seconds
From 2022-04-23T02:37:56.049567 TDB to 2022-04-23T04:57:52.999839 TDB lasting 8396.950271590502 seconds
From 2022-04-24T00:46:58.526896 TDB to 2022-04-24T03:07:36.970814 TDB lasting 8438.4439174091 seconds
From 2022-04-24T22:56:01.635637 TDB to 2022-04-25T01:17:19.943334 TDB lasting 8478.307697531896 seconds
From 2022-04-25T21:05:05.373632 TDB to 2022-04-25T23:27:01.922203 TDB lasting 8516.5485710872 seconds
From 2022-04-26T19:14:09.738829 TDB to 2022-04-26T21:36:42.912180 TDB lasting 8553.173351262303 seconds
From 2022-04-27T17:23:14.729332 TDB to 2022-04-27T19:46:22.918082 TDB lasting 8588.188749907358 seconds
From 2022-04-28T15:32:20.343452 TDB to 2022-04-28T17:56:01.944865 TDB lasting 8621.601412334147 seconds
From 2022-04-29T13:41:26.579740 TDB to 2022-04-29T16:05:39.997682 TDB lasting 8653.417942095872 seconds
From 2022-04-30T11:50:33.437014 TDB to 2022-04-30T14:15:17.081931 TDB lasting 8683.644916757488 seconds

Now plot the events on top of the function plot

In [11]:
# extract the start and end times of the incursion into the FOV in plottable form
starts = [entry.start().value().mjd() for entry in mars_in_fov if entry.start().isSolved()]
ends = [entry.end().value().mjd() for entry in mars_in_fov if entry.end().isSolved()]

# generate the plots    
fig = plt.figure(figsize=(9, 5))
plt.title('Mars Angle From Instrument Boresight')
plt.xlabel('Time (MJD)')
plt.ylabel('Mars Angle From Boresight (deg)')
times = [e.mjd() for e in grid]
values = np.rad2deg([model.eval(e) for e in grid])
plt.plot(times, values)
plt.plot(times,[boresight_angle]*len(times),'--')

plt.plot(starts, [boresight_angle] * len(starts), 'o', label="entry")
plt.plot(ends, [boresight_angle] * len(ends), 'o', label="exit")

# plot a single entry, for clarity
plt.ylim(boresight_angle-2.5 ,boresight_angle+2.5)
plt.xlim(8126,8129)

plt.tight_layout()

We want to know when Mars is in the boresight, but not in occulation by the Earth

In [12]:
from godot.core import events
# we could compute mars visibility event interval sets (not in occultation)
mars_visib = mars_occult.complement()
# find all event interval set with mars in occultation and in instrument fov
mars_occulted_and_in_fov = events.overlap([mars_in_fov, mars_occult])
print('MARS OCCULTED AND IN FOV')
for interval in mars_occulted_and_in_fov:
    print(f"From {interval.start().value()} to {interval.end().value()} lasting {interval.width()} seconds")
MARS OCCULTED AND IN FOV
/local/scratch/gvarga/godottut/env/lib64/python3.6/site-packages/ipykernel_launcher.py:5: DeprecationWarning: The overlap(list()) function is deprecated and it will be soon removed, please use the "all" function instead
  """