Module: spacecraftLocation

Executive Summary

This module determines if one satellite has a line of sight vector to another satellite. An oblate planet is modeled through the equatorial and the polar radius. If the line of sight vector between two satellites is above this surface, then the output access message is set to true. The module has a primary spacecraft which determines access to N other spacecraft orbiting the same planet.

Further, the module tracks a body-fixed location L on the primary spacecraft (i.e. location where an antenna is attached), and you can specify an optional sensor/communication bore sight axis \(\hat{\bf a}\) and an center-to-edge sensor/communication cone angle \(\theta\). In this case the access message is true if teh line of sight vector between spacecraft is above the surface and the relative position vector to the other spacecraft is within this cone.

Finaly, if the other spacecraft is accessable, the range to the other satellite is stored in the output message.

Module Assumptions and Limitations

This module assumes all spacecraft are orbiting the same planet. The planet shape is assumed to be an ellipoid specified through the equatorial and polar radius. To account for a planet’s atmosphere, increase these radii to account for the atmospheric height.

Message Connection Descriptions

The following table lists all the module input and output messages. The module msg variable name is set by the user from python. The msg type contains a link to the message structure definition, while the description provides information on what this message is used for.

Module I/O Messages

Msg Variable Name

Msg Type




(optional) planet state input message. Default is a zero state for the planet.



primary spacecraft state message relative to which to evaluate access to other spacecraft in scStateInMsgs



vector of other spacecraft state input messages. These are set through addSpacecraftToModel()



output vector of ground location access messages

Detailed Module Description

The spacecraftLocation module handles the following behavior:

  1. Spacecraft body-fixed location representation: a single spacecraftLocation instance represents one body-fixed location on a spacecraft

  2. The planet-centered planet-fixed frame is either a zero state (default), or read in through an optional planet ephemeris message

  3. An optional sensor axis can be specified to ensure the primary spacecraft is pointing this axis

  4. Computation of spacecraft visibility (i.e. access) considering range and relative field-of-view constraints

  5. Support for multiple other spacecraft given one spacecraftLocation instance

Determining Line of Sight to another spacecraft

Let P be the planet-centered planet-fixed frame. The inertial planet origin is given by \({}^{N} {\bf r}_{P/N}\). The inertial position vector of the primary spacecraft B is \({}^{N}{\bf r}_{B/N}\), while the inertial position vector to the other satellite is \({}^{\cal N}{\bf r}_{S/N}\). The first step is to determine the spacecraft positions, expressed in the P frame.

\[{}^{P} {\bf r}_{B/P} = [PN] ( {}^{N} {\bf r}_{B/N} - {}^{N} {\bf r}_{P/N})\]
\[{}^{P} {\bf r}_{S/P} = [PN] ( {}^{N} {\bf r}_{S/N} - {}^{N} {\bf r}_{P/N})\]
\[{}^{P} {\bf r}_{S/B} = {}^{P} {\bf r}_{S/P} - {}^{P} {\bf r}_{B/P})\]

The line of sight vector is defined through the point B and the direction \({\bf r}_{S/B}\). Let \(\kappa\) be a scaling factor, the line is then defined as:

\[{\bf l} = {\bf r}_{B/P} + \kappa * {\bf r}_{S/B}\]

The process of intersecting a line with an ellipsoid is simplified by using an affine project to render the ellipsoid a sphere. This affine mapping preserves a line to remain a line. The math of this is as follow. The planet is assumed to have rotational symmetry about the 3rd axis about which it is spinning. Thus, to map the ellipsoid into a sphere the planet relative 3rd coordinates must scaled by the ratio of the equatorial radius to the polar radius. The following math assumes this affine mapping has been performed in the above planet-relative position coordinates.

To determine the minimum distance of the line \(\bf l\) to the planet origin, consider the cost function \(J\):

\[J = {\bf l} \cdot {\bf l} = {\bf r}_{B/P} \cdot {\bf r}_{B/P} + 2 \kappa {\bf r}_{B/P} \cdot {\bf r}_{S/B} + \kappa^2 {\bf r}_{S/B} \cdot {\bf r}_{S/B}\]

Setting the first variation of \(J\) with respect to \(\kappa\) to zero and solving for optimal \(\kappa^\ast\) yields

\[\kappa^\ast = - \frac{{\bf r}_{B/P} \cdot {\bf r}_{S/B}}{{\bf r}_{S/B} \cdot {\bf r}_{S/B}}\]

Thus, the point of closed approach is determined through:

\[{\bf r}^\ast = {\bf r}_{B/P} + \kappa^\ast * {\bf r}_{S/B}\]

If \(|{\bf r}^\ast| > r_{eq}\) then the other spacecraft is visible relative to the primary spacecraft.

Determining Sensor Cone Inclusion

If the line of sight property is established, then the module can also take into consideration a sensor or communication boresight axis \(\hat {\bf a}\) which is fixed relative to the primary spacecraft body frame. The angle \(\phi\) between the relative position vector and this body fixed unit direction vector is found through:

\[\phi = \arccos \left( \frac{ {\bf r}_{S/B} \cdot \hat{\bf a}}{|{\bf r}_{S/B} |} \right)\]

The module sets the sensor cone half-angle \(\theta\). If \(\phi > \theta\) then the sensor or communication axis does not have access to the other spacecraft.

This \(\hat{\bf a}\) is considered, then the access output message sets the message elevation angle as

\[\text{elevation} = \frac{\pi}{2} - \phi\]

User Guide

A new instance of spacecraftLocation, alongside necessary user-supplied parameters, can be created by calling:

location = spacecraftLocation.SpacecraftLocation()
location.ModelTag = "scLocation"
location.rEquator = orbitalMotion.REQ_EARTH * 1000.
location.rPolar = orbitalMotion.RP_EARTH * 1000.  # optional, include to account for oblateness
location.maximumRange = 100e3 # optinal, sets maximum range for visibility in meters
scSim.AddModelToTask(simTaskName, location)

The variable maximumRange is optional and set to -1 by default. If it is set to a positive value, then the hasAccess variable is only set to 1 if the relative spacecraft distance is less than this maximum range.

A optional planet emphemeris is connected via the``planetInMsg`` input message:


It this message is not connected, then zero planet position and attitude orientation are set.

To set a primary spacecraft body fixed sensor or communication axis \(\hat{\bf a}\) and half-cone angle \(\theta\), use:

module.aHat_B = unitTestSupport.np2EigenVectorXd([xxx, xxx, xxx])
module.theta = xxx * macros.D2R

Spacecraft can be added to the model by calling:


The access output messages can be logged through:

dataRec0 = location.accessOutMsgs[0].recorder()
dataRec1 = location.accessOutMsgs[1].recorder()

class SpacecraftLocation : public SysModel
#include <spacecraftLocation.h>

ground location class

Public Functions


Creates an instance of the SpacecraftLocation class.




Empty destructor method.



void UpdateState(uint64_t CurrentSimNanos)

update module



void Reset(uint64_t CurrentSimNanos)

Resets the internal position to the specified initial position.

bool ReadMessages()

Read module messages

void WriteMessages(uint64_t CurrentClock)

write module messages

void addSpacecraftToModel(Message<SCStatesMsgPayload> *tmpScMsg)

Adds a scState message name to the vector of names to be subscribed to. Also creates a corresponding access message output name.

Public Members

double rEquator

[m] equatorial planet radius

double rPolar

[m] polar planet radius

double maximumRange

[m] Maximum slant range to compute access for; defaults to -1, which represents no maximum range.

Eigen::Vector3d aHat_B

[] (optional) unit direction vector vector of the senor/communication boresight axis

double theta

[r] (optional) sensor/communication half-cone angle, must be set if shat_B is specified

ReadFunctor<SCStatesMsgPayload> primaryScStateInMsg

primary spacecraft input message

ReadFunctor<SpicePlanetStateMsgPayload> planetInMsg

planet state input message

std::vector<Message<AccessMsgPayload>*> accessOutMsgs

vector of ground location access messages

std::vector<ReadFunctor<SCStatesMsgPayload>> scStateInMsgs

vector of other sc state input messages

Eigen::Vector3d r_LB_B

[m] position of the location relative to the spacecraft frame origin B, in B frame components

BSKLogger bskLogger

BSK Logging

Private Functions

void computeAccess()

compute the spacecraft to spacecraft access messages

Private Members

std::vector<AccessMsgPayload> accessMsgBuffer

buffer of access output data

std::vector<SCStatesMsgPayload> scStatesBuffer

buffer of other spacecraft states

SCStatesMsgPayload primaryScStatesBuffer

buffer of primary spacecraft states

SpicePlanetStateMsgPayload planetState

buffer of planet data

Eigen::Matrix3d dcm_PN

Rotation matrix from inertial frame N to planet-centered to planet-fixed frame P.

Eigen::Vector3d r_PN_N

[m] Planet to inertial frame origin vector.

Eigen::Vector3d r_BP_P

primary spacecraft relative to planet

Eigen::Vector3d r_BN_N

primary spacecraft relative to inertial

double zScale

ration of rEquator over rPolar, used for affine scaling to turn ellipsoid to sphere