Remote Sensing Hands-On Lesson using BepiColombo MPO

Virtual SPICE Training for BepiColombo, July 21-22, 2020

Overview

In this lesson you will develop a series of simple programs that demonstrate the usage of SpiceyPy to compute a variety of different geometric quantities applicable to experiments carried out by a remote sensing instrument flown on an interplanetary spacecraft.

You may find it useful to consult the permuted index, the headers of various source modules, and several Required Reading documents available at the NAIF site.

Initialise SPICE by importing SpiceyPy

For the following exercises, instead of loading the meta-kernel try to sort out the exact kernels that you need to load for the given execrcise and load them (unless indicated).

In [2]:
import spiceypy 

Time Conversion

Write a program that given a UTC time string, converts it to the following time systems and output formats:

  • Ephemeris Time (ET) in seconds past J2000
  • Calendar Ephemeris Time
  • Spacecraft Clock Time (TIP: You will need to know the NAIF ID of MPO)

and displays the results. Use the program to convert "2020-OCT-15 03:57:49" UTC into these alternate systems.

In [3]:
#
# We need to load the leapseconds kernel and the SCLK kernel.
#
spiceypy.furnsh('../kernels/lsk/naif0012.tls')
spiceypy.furnsh('../kernels/sclk/bc_mpo_step_20200713.tsc')

et = spiceypy.utc2et('2020-10-15T03:57:49')
print('   Ephemeris Time (ET) in seconds past J2000: {}'.format(et))

calet = spiceypy.timout( et, 'YYYY-MON-DDTHR:MN:SC ::TDB' )
print( '   Calendar Ephemeris Time:                   {:s}'.format( calet ) )

#
# We will need the SCLK ID of MPO that we can retrieve from the SCLK file itself.
#
sclkid = -121

sclkst = spiceypy.sce2s( sclkid, et )
print( '   Spacecraft Clock Time:                     {:s}'.format( sclkst ) )

#
# We unload all the kernels in the kernel pool
#
spiceypy.kclear()
   Ephemeris Time (ET) in seconds past J2000: 656006338.1823678
   Calendar Ephemeris Time:                   2020-OCT-15T03:58:58
   Spacecraft Clock Time:                     1/0667454267:48104

Obtaining Target States and Positions - Venus Flyby

Write a program that given a UTC time string computes the following quantities at that epoch:

  • The apparent state of Venus as seen from BepiColombo MPO in the J2000 frame, in kilometers and kilometers/second. This vector itself is not of any particular interest, but it is a useful intermediate quantity in some geometry calculations.

  • The one-way light time between BepiColombo MPO and the apparent position of Earth, in seconds.

  • The actual (geometric) distance between the Sun and Venus, in astronomical units.

and displays the results. Use the program to compute these quantities at "2020-OCT-15 03:57:49" UTC.

title

In [4]:
#
# We need to load the leapseconds kernel the Solar System ephemeris and the MPO SPK.
#
spiceypy.furnsh('../kernels/lsk/naif0012.tls')
spiceypy.furnsh('../kernels/spk/de432s_20201013_20201016.bsp')
spiceypy.furnsh('../kernels/spk/bc_mpo_fcp_Venus1SwingbyMTP_v01.bsp')

et = spiceypy.utc2et('2020-10-15T03:57:49')

#
# Compute the apparent state of Mercury as seen from BepiColombo MPO in the J2000 frame.  
# All of the ephemeris readers return states in units of kilometers and km/s.
#
[state, ltime] = spiceypy.spkezr('VENUS', et,'J2000','LT+S','MPO' )
 
print( ' Apparent state of Venus as seen from BepiColombo MPO in the J2000\n'
       ' frame (km, km/s):')
print( '      X = {:16.3f}'.format(state[0])       )
print( '      Y = {:16.3f}'.format(state[1])       )
print( '      Z = {:16.3f}'.format(state[2])       )
print( '     VX = {:16.3f}'.format(state[3])       )
print( '     VY = {:16.3f}'.format(state[4])       )
print( '     VZ = {:16.3f}\n'.format(state[5])     )
 
#
# Compute the apparent position of Earth as seen from
# BepiColombo MPO in the J2000 frame.  
#
[pos, ltime] = spiceypy.spkpos( 'EARTH', et, 'J2000', 'LT+S', 'MPO')
 
print( ' One way light time between BepiColombo MPO and the apparent\n'
       ' position of Earth: {} seconds\n'.format(ltime))
 
#
# Now we need to compute the actual distance between the Sun and Mercury.  
# We need to adjust our aberration correction appropriately.
#
[pos, ltime] = spiceypy.spkpos( 'SUN',  et, 'J2000','NONE', 'VENUS')
 
#
# Compute the distance between the body centers in kilometers.
#
dist = spiceypy.vnorm( pos )
 
#
# Convert this value to AU using convrt.
#
dist = spiceypy.convrt( dist, 'KM', 'AU' )
 
print( ' Actual distance between Sun and Venus body centers: {} AU'.format(dist))
 
spiceypy.kclear()
 Apparent state of Venus as seen from BepiColombo MPO in the J2000
 frame (km, km/s):
      X =        14496.540
      Y =          786.114
      Z =         8286.990
     VX =            2.258
     VY =           -9.366
     VZ =           -3.061

 One way light time between BepiColombo MPO and the apparent
 position of Earth: 582.6527711004886 seconds

 Actual distance between Sun and Venus body centers: 0.7188927953509128 AU

Spacecraft Orientation and Reference Frames

Write a program that given a UTC time string computes and displays the following at the epoch of interest:

  • The angular separation between the apparent position of BepiColombo MMO as seen from BepiColombo MPO and the nominal instrument view direction.

The nominal instrument view direction is not provided by any kernel variable, but it is indicated in the BepiColombo MPO frame kernel.

Use the program to compute these quantities at the epoch "2027 JUN 11 20:50:00" UTC.

title

In [6]:
#
# Since we need orientation information in addition to the kernels we loaded before
# now we need to load the Frames Kernel, the SCLK kernel and the MPO CK kernel.
# It is also important to note that we need to load the frames kernel containing the 
# MPO Science frames
#
spiceypy.furnsh('../kernels/lsk/naif0012.tls')
spiceypy.furnsh('../kernels/spk/de432s_20270609_20270614.bsp')
spiceypy.furnsh('../kernels/spk/bc_mpo_mlt_50037_20270609_20270614_v01.bsp')
spiceypy.furnsh('../kernels/spk/bc_mmo_mlt_50038_20270609_20270614_v01.bsp')
spiceypy.furnsh('../kernels/spk/bc_mpo_cog_v01.bsp')
spiceypy.furnsh('../kernels/sclk/bc_mpo_step_20200713.tsc')
spiceypy.furnsh('../kernels/ck/bc_mpo_sc_slt_50028_20270609_20270614_s20200713_v01.bc')
spiceypy.furnsh('../kernels/fk/bc_mpo_v23.tf')
spiceypy.furnsh('../kernels/fk/bc_sci_v06.tf')

 
et = spiceypy.utc2et('2027-06-11T20:50:00')

#
# We compute the apparent position of MMO as seen from 
# MPO in the J2000 frame.
#
[pos, ltime] = spiceypy.spkpos('MMO',et,'J2000','LT+S','MPO')
[pos, ltime] = spiceypy.spkpos('MMO',et,'MPO_SPACECRAFT','LT+S','MPO')

#
# Now compute the location of the nominal instrument view
# direction.  From reading the frame kernel we know that
# the instrument view direction is nominally the +Z axis
# of the TGO_SPACECRAFT frame defined there.
#
bsight = [ 0.0, 0.0, 1.0]

#
# Now compute the rotation matrix from MPO_SPACERAFT into
# J2000.
#
pform = spiceypy.pxform('MPO_SPACECRAFT','J2000', et )

#
# And multiply the result to obtain the nominal instrument
# view direction in the J2000 reference frame.
#
#bsight = spiceypy.mxv(pform, bsight)


#
# Lastly compute the angular separation.
#
sep =  spiceypy.convrt(spiceypy.vsep(bsight, pos),'RADIANS','DEGREES')

print(' Angular separation between the apparent position of MMO and the\n'
      ' MPO nominal instrument view direction (degrees): {:.3f}'.format(sep))


spiceypy.kclear()
 Angular separation between the apparent position of MMO and the
 MPO nominal instrument view direction (degrees): 79.750

Computing Sub-s/c and Sub-solar Points on an Ellipsoid

Write a program that given a UTC time string computes the following quantities at that epoch:

  • The apparent sub-observer point of MPO on Mercury, in the body fixed frame IAU_MERCURY, in kilometers.
  • The apparent sub-solar point on Mercury, as seen from MPO in the body fixed frame IAU_MERCURY, in kilometers.

For the computations use the ellipsoidal shape model:

near point/ellipsoid

definition.

The program displays the results. Use the program to compute these quantities at "2027 JUN 11 20:10:00" UTC.

title

In [7]:
spiceypy.furnsh('../kernels/lsk/naif0012.tls')
spiceypy.furnsh('../kernels/spk/de432s_20270609_20270614.bsp')
spiceypy.furnsh('../kernels/spk/bc_mpo_mlt_50037_20270609_20270614_v01.bsp')
spiceypy.furnsh('../kernels/spk/bc_mmo_mlt_50038_20270609_20270614_v01.bsp')
spiceypy.furnsh('../kernels/spk/bc_mpo_cog_v01.bsp')
spiceypy.furnsh('../kernels/sclk/bc_mpo_step_20200713.tsc')
spiceypy.furnsh('../kernels/ck/bc_mpo_sc_slt_50028_20270609_20270614_s20200713_v01.bc')
spiceypy.furnsh('../kernels/fk/bc_mpo_v23.tf')
spiceypy.furnsh('../kernels/fk/bc_sci_v06.tf')
spiceypy.furnsh('../kernels/pck/pck00010.tpc')

et = spiceypy.utc2et('2027-06-11T20:10:00')

#
# Use the "near point" sub-point definition
# and an ellipsoidal model.
#
method = 'NEAR POINT/Ellipsoid'

#
# Compute the apparent sub-observer point of MPO on Mercury.
#
[spoint, trgepc, srfvec] = spiceypy.subpnt(method,'MERCURY',et,
                                           'IAU_MERCURY','LT+S','MPO' )

print( ' Apparent sub-observer point of MPO on Mercury\n'
       ' in the IAU_MERCURY frame (km):' )
print( '      X = {:.3f}'.format(spoint[0])              )
print( '      Y = {:.3f}'.format(spoint[1])              )
print( '      Z = {:.3f}'.format(spoint[2])              )
print( '    ALT = {:.3f}\n'.format(spiceypy.vnorm(srfvec)) )


#
# Compute the apparent sub-solar point on Mercury as seen from MPO
#
[spoint, trgepc, srfvec] = spiceypy.subslr(method,'MERCURY',et,'IAU_MERCURY',
                                           'LT+S','MPO' )

print( ' Apparent sub-solar point on Mercury as seen from MPO \n'
       ' in the IAU_MERCURY frame (km):'  )
print( '      X = {:.3f}'.format(spoint[0])   )
print( '      Y = {:.3f}'.format(spoint[1])   )
print( '      Z = {:.3f}\n'.format(spoint[2])   )


#
# We unload all the kernels in the kernel pool
#
spiceypy.kclear()
 Apparent sub-observer point of MPO on Mercury
 in the IAU_MERCURY frame (km):
      X = -1491.318
      Y = 1817.025
      Z = -653.089
    ALT = 307.635

 Apparent sub-solar point on Mercury as seen from MPO 
 in the IAU_MERCURY frame (km):
      X = -908.060
      Y = 2264.412
      Z = -0.801

Intersecting Vectors with an Ellipsoid (fovint)

Write a program given an input UTC time string that computes the intersection of the MPO SERENA ELENA boresight and field of view (FOV) boundary vectors with the surface of Mercury.
The program presents each point of intersection as

  • Planetocentric (latitudinal) coordinates in the IAU_MERCURY frame.

For each of the sensor FOV boundary and boresight vectors, if an intersection is found, the program displays the results of the above computations, otherwise it indicates no intersection exists.

At each point of intersection compute the following:

  • Phase angle
  • Solar incidence angle
  • Emission angle

Use this program to compute values at "2027 JUN 11 20:10:00" UTC.

title

In [10]:
spiceypy.furnsh('../kernels/mk/bc_training_class.tm')


et = spiceypy.utc2et('2027-06-11T20:10:00')

#
# Now we need to obtain the FOV configuration of
# the SERENA ELENA aperture.  To do this we will
# need the ID code for MPO_SERENA_ELENA.
#
lnonid = spiceypy.bodn2c('MPO_SERENA_ELENA')

#
# Now retrieve the field of view parameters.
#
[shape,insfrm,bsight,n,bounds ] = spiceypy.getfov(lnonid,4)

#
# `bounds' is a numpy array. We'll convert it to a list.
#
# Rather than treat BSIGHT as a separate vector,
# copy it into the last slot of BOUNDS.
#
bounds = bounds.tolist()
bounds.append( bsight )

#
# Set vector names to be used for output.
#
vecnam = [ 'Boundary Corner 1',
           'Boundary Corner 2',
           'Boundary Corner 3',
           'Boundary Corner 4',
           'MPO_SERENA_ELENA Boresight' ]

#
# Now perform the same set of calculations for each
# vector listed in the BOUNDS array.
#
for i  in  range(5):
    #
    # Call sincpt to determine coordinates of the
    # intersection of this vector with the surface
    # of Mercury.
    #
    print( ' Vector: {:s}\n'.format( vecnam[i] ) )

    [point,trgepc,srfvec] = spiceypy.sincpt('ELLIPSOID','MERCURY',et,
                                            'IAU_MERCURY', 'LT+S','MPO',
                                            insfrm, bounds[i])

    #
    # Display the planetocentric latitude and longitude of the intercept.
    #
    [radius,lon,lat] = spiceypy.reclat( point )
 
    print( ' Planetocentric coordinates of the intercept (degrees):')
    print( '     LAT = {:.3f}'.format(lat * spiceypy.dpr() )  )
    print( '     LON = {:.3f}\n'.format(lon * spiceypy.dpr() )  )
    
    #
    # Compute the illumination angles at this point.
    #
    [trgepc,srfvec,phase,solar,emissn, visibl,lit] = spiceypy.illumf(
             'ELLIPSOID','MERCURY','SUN',et,'IAU_MERCURY', 
             'LT+S','MPO', point)
 
    print( '  Phase angle (degrees): {:.3f}'.format(phase*spiceypy.dpr()))
    print( '  Solar incidence angle (degrees): {:.3f}'.format(solar*spiceypy.dpr()))
    print( '  Emission angle (degrees): {:.3f}'.format(emissn*spiceypy.dpr()))
    print( '  Observer visible:  {:s}'.format(str(visibl)))
    print( '  Sun visible:       {:s}\n'.format(str(lit)))


#
# End of vector loop.
#
              
spiceypy.kclear()
 Vector: Boundary Corner 1

 Planetocentric coordinates of the intercept (degrees):
     LAT = -15.956
     LON = 120.441

  Phase angle (degrees): 65.596
  Solar incidence angle (degrees): 18.052
  Emission angle (degrees): 56.075
  Observer visible:  True
  Sun visible:       True

 Vector: Boundary Corner 2

 Planetocentric coordinates of the intercept (degrees):
     LAT = -14.645
     LON = 120.501

  Phase angle (degrees): 67.390
  Solar incidence angle (degrees): 16.944
  Emission angle (degrees): 56.091
  Observer visible:  True
  Sun visible:       True

 Vector: Boundary Corner 3

 Planetocentric coordinates of the intercept (degrees):
     LAT = -14.875
     LON = 133.871

  Phase angle (degrees): 22.403
  Solar incidence angle (degrees): 26.354
  Emission angle (degrees): 35.037
  Observer visible:  True
  Sun visible:       True

 Vector: Boundary Corner 4

 Planetocentric coordinates of the intercept (degrees):
     LAT = -16.107
     LON = 133.891

  Phase angle (degrees): 16.039
  Solar incidence angle (degrees): 27.048
  Emission angle (degrees): 35.012
  Observer visible:  True
  Sun visible:       True

 Vector: MPO_SERENA_ELENA Boresight

 Planetocentric coordinates of the intercept (degrees):
     LAT = -15.517
     LON = 129.377

  Phase angle (degrees): 23.289
  Solar incidence angle (degrees): 23.234
  Emission angle (degrees): 0.085
  Observer visible:  True
  Sun visible:       True

Extra Credit: Lessons with WGC

Try to reproduce all the previous calculations with WebGeocalc. You will need to load the appropriate meta-kernel: "SPICE CLASS -- BepiColombo Remote Sensing Lesson Kernels"