Ground tracks for the entire mission and for station visibility

Fellow astrodynamicists,

I am looking to generate ground tracks for a visible satellite given a TLE and date range. My current method uses the ElevationDetector and ElevationExtremumDetector, resulting only getting the ground track points for the start of the contact, the maximum, and the end of the contact window.

It looks roughly like:

 station = GeodeticPoint(
                radians(latAnalysis[indexGs]),
                radians(lonAnalysis[indexGs]),
                float(altAnalysis[indexGs]),
            )
stationFrame = TopocentricFrame(earth, station, "Esrange")
logger = EventsLogger()
elevationDetector = ElevationDetector(stationFrame).withConstantElevation(minElevation).withThreshold(1.0e-6).withHandler(ContinueOnEvent())
loggerElDetector = logger.monitorDetector(elevationDetector)
maxDetector = ElevationExtremumDetector(stationFrame).withThreshold(1.0e-6).withHandler(ContinueOnEvent())
loggerMaxDetector = logger.monitorDetector(maxDetector)

# This is where the magic happens. OreKit does orbit propagation and logs the events for
# elevation and max elevation
propagator = TLEPropagator.selectExtrapolator(satelliteTLE)
propagator.addEventDetector(loggerElDetector)
propagator.addEventDetector(loggerMaxDetector)
propagator.propagate(startDate, endDate)
oreKitEvents = logger.getLoggedEvents()

 ###############d###############
 # Post Process Orekit Results #
 ###############################
pvList = []
elevationList = []
timeList = []
startTime = []
stopTime = []
accessTime = []
maxElevation = []
index = 0
lengthEvents = 0
numContact = 0

  # This method detects when the satellite is at 5 deg el, its elevation extreme (can be negative)
  # For post-process I determine the time at the 5 deg el crossings and the maximum elevation > 0
  for oreKitEvent in oreKitEvents:
      eventState = oreKitEvent.getState()
      pv = eventState.getPVCoordinates()  # .getPosition()

      # print(pv)
      # pvList.append(pv)
      # This might be for PV when satellite is detected by the ground station (above elevation threshold)
      groundPoint = earth.transform(pv.position, inertialFrame, pv.date)
      print(f"{degrees(groundPoint.longitude)},{degrees(groundPoint.latitude)}")

      elevation = degrees(
          stationFrame.getElevation(
              eventState.getPVCoordinates().getPosition(),
              eventState.getFrame(),
              eventState.getDate(),
          )
      )
      elevationList.append(elevation)
      timeList.append(eventState.getDate())
      lengthEvents += 1
  print("elevationList:")
  print(elevationList)

  for elevationListItem in elevationList:
      if index != 0 and index < lengthEvents - 1 and int(elevationListItem) > degrees(minElevation):
          startTimeTemp = timeList[index - 1]
          startTime.append(str(startTimeTemp))
          stopTimeTemp = timeList[index + 1]
          stopTime.append(str(stopTimeTemp))
          accessTimeTemp = round(stopTimeTemp.durationFrom(startTimeTemp) / 60, 2)
          accessTime.append(accessTimeTemp)
          maxElevationTemp = round(elevationList[index], 2)
          maxElevation.append(maxElevationTemp)

          numContact += 1
      index += 1

The post-processing for finding start/stop times of that contact window isn’t the best, but it works.

My next thought was to follow the orekit_map.ipynb example:

startDate = AbsoluteDate(2012, 1, 26, 11, 0, 00.000, utc)

# Overall duration in seconds for extrapolation
duration = 24 * 60 * 60
step_time = 10

# Time array in orekit AbsoluteDate format
t = [startDate.shiftedBy(float(dt)) \
        for dt in np.arange(0, duration, step_time)]

pv = [eck_prop.propagate(tt).getPVCoordinates() for tt in t]
p = [tpv.getPosition() for tpv in pv]
.
.
.
lat = np.degrees([gp.getLatitude()  for gp in subpoint])
lon = np.degrees([gp.getLongitude() for gp in subpoint])

I’d like to create a TimeArray with a variable step_time. Is there a way to create the TimeArray with just a start data, end date, and step_time (dt)?

Is there a clever way to get the ground point for the duration of the orbit and detect when elevations are above a threshold? Maybe it’s a combination of using the propagator and the events logger?

Hi @dretek

Welcome to the Orekit forum!

The purpose of the event logger is to log events during the propagation. The log is not a print of values, it is a save of the events occurring during the propagation. It can be useful for post processing (i.e., after the propagation).
You can initialize it like that

logger = new EventsLogger()
propagator.addEventDetector(logger.monitorDetector(yourDetector))

To get the satellite’s ground point during the whole propagation, you can look at the step handler features of Orekit. For instance, an OrekitFixedStepHandler can be used to compute the ground track at a fixed time step during the propagation. Just note that it’s an interface. So, you have to implement it.
As a Python user, I suppose you will have to implement PythonOrekitFixedStepHandler.
Here an example:

class MyHandler(PythonOrekitFixedStepHandler):
    def init(self, s0, t, step):
        # Do what you want
        pass

    def handleStep(self, currentState, isLast):
        groundPoint = earth.transform(currentState.getPVCoordinates().getPosition(), currentState.getFrame(), currentState.getDate())
        ...

For a variable step size, you can use the PythonOrekitStepHandler.
Both can be added to the propagator using propagator.getMultiplexer().add(handler) for the variable step handler and propagator.getMultiplexer().add(stepSize, handler) for the fixed step size handler.

Finally, the EventsLogger is very useful to save events occurring during the propagation. The only drawback is that you can access them only at the end of the propagation.
To access events during the propagation, you can use a PythonEventsHandler.

class MyHandler(PythonEventHandler):
    def init(self, initialstate, target):
        pass

    def eventOccurred(self, s, T, increasing):
        # Do what you want (e.g. start of event, end of event, ground points, etc.)

Regards,
Bryan