Reserve contacts with custom ephemeris
Overview
When using custom ephemeris (TLE, OEM, or azimuth elevation), you can reserve contacts using the ReserveContact API. This section describes two common workflows for reserving contacts and important considerations for ensuring successful contact scheduling.
AWS Ground Station antennas are shared resources among multiple customers. This means that even if a contact
window appears available when you list contacts, another customer might reserve it before you do.
Therefore, it's crucial to verify that your contact reaches the SCHEDULED state
after reservation and to implement proper monitoring for contact state changes.
Important
For azimuth elevation ephemeris, the satelliteArn parameter may be omitted
from the ReserveContact request, and you must provide trackingOverrides
with the ephemeris ID. For TLE and OEM ephemeris, you still need to provide the satelliteArn.
Contact reservation workflows
There are two primary workflows for reserving contacts with custom ephemeris:
-
List-then-reserve workflow: First list available contact windows using ListContacts, then select and reserve a specific window. This approach is useful when you want to see all available opportunities before making a selection.
-
Direct reservation workflow: Directly reserve a contact for a specific time window without first listing available contacts. This approach is useful when you already know your desired contact time or are working with predetermined schedules.
Both workflows are valid and the choice depends on your operational requirements. The following sections provide examples of each approach.
Workflow 1: List available contacts then reserve
This workflow first queries for available contact windows, then reserves a specific window. This is useful when you want to see all available opportunities before making a selection.
Example: List and reserve with azimuth elevation ephemeris
import boto3 from datetime import datetime, timezone import time # Create AWS Ground Station client ground_station_client = boto3.client("groundstation") # Create azimuth elevation ephemeris print("Creating azimuth elevation ephemeris...") ephemeris_response = ground_station_client.create_ephemeris( name="AzEl Ephemeris for Contact", ephemeris={ "azEl": { "groundStation": "Ohio 1", "data": { "azElData": { "angleUnit": "DEGREE_ANGLE", "azElSegmentList": [ { "referenceEpoch": "2024-03-15T10:00:00Z", "validTimeRange": { "startTime": "2024-03-15T10:00:00Z", "endTime": "2024-03-15T10:15:00Z", }, "azElList": [ {"dt": 0.0, "az": 45.0, "el": 10.0}, {"dt": 180.0, "az": 50.0, "el": 15.0}, {"dt": 360.0, "az": 55.0, "el": 20.0}, {"dt": 540.0, "az": 60.0, "el": 25.0}, {"dt": 720.0, "az": 65.0, "el": 30.0}, {"dt": 900.0, "az": 70.0, "el": 35.0}, ], } ], } }, } }, ) ephemeris_id = ephemeris_response["ephemerisId"] print(f"Created ephemeris: {ephemeris_id}") # Wait for ephemeris to become ENABLED while True: status = ground_station_client.describe_ephemeris(ephemerisId=ephemeris_id)[ "status" ] if status == "ENABLED": print("Ephemeris is ENABLED") break elif status in ["INVALID", "ERROR"]: raise RuntimeError(f"Ephemeris failed: {status}") time.sleep(5) # List available contacts print("Listing available contacts...") contacts = ground_station_client.list_contacts( # Note: satelliteArn is omitted for azimuth elevation ephemeris groundStation="Ohio 1", missionProfileArn="arn:aws:groundstation:us-east-2:111122223333:mission-profile/example-profile", startTime=datetime(2024, 3, 15, 10, 0, 0, tzinfo=timezone.utc), endTime=datetime(2024, 3, 15, 10, 15, 0, tzinfo=timezone.utc), statusList=["AVAILABLE"], ephemeris={"azEl": {"id": ephemeris_id}}, ) if contacts["contactList"]: # Reserve the first available contact contact = contacts["contactList"][0] print(f"Reserving contact from {contact['startTime']} to {contact['endTime']}...") reservation = ground_station_client.reserve_contact( # Note: satelliteArn is omitted when using azimuth elevation ephemeris missionProfileArn="arn:aws:groundstation:us-east-2:111122223333:mission-profile/example-profile", groundStation="Ohio 1", startTime=contact["startTime"], endTime=contact["endTime"], trackingOverrides={ "programTrackSettings": {"azEl": {"ephemerisId": ephemeris_id}} }, ) print(f"Reserved contact: {reservation['contactId']}") else: print("No available contacts found")
Example: List and reserve with TLE ephemeris
import boto3 from datetime import datetime, timedelta, timezone import time # Create AWS Ground Station client ground_station_client = boto3.client("groundstation") satellite_id = "12345678-1234-1234-1234-123456789012" satellite_arn = f"arn:aws:groundstation::111122223333:satellite/{satellite_id}" # Create TLE ephemeris print("Creating TLE ephemeris...") ephemeris_response = ground_station_client.create_ephemeris( name="TLE Ephemeris for Contact", satelliteId=satellite_id, enabled=True, expirationTime=datetime.now(timezone.utc) + timedelta(days=7), priority=1, # Higher priority than default ephemeris ephemeris={ "tle": { "tleData": [ { "tleLine1": "1 25994U 99068A 24075.54719794 .00000075 00000-0 26688-4 0 9997", "tleLine2": "2 25994 98.2007 30.6589 0001234 89.2782 18.9934 14.57114995111906", "validTimeRange": { "startTime": datetime.now(timezone.utc), "endTime": datetime.now(timezone.utc) + timedelta(days=7), }, } ] } }, ) ephemeris_id = ephemeris_response["ephemerisId"] print(f"Created ephemeris: {ephemeris_id}") # Wait for ephemeris to become ENABLED while True: status = ground_station_client.describe_ephemeris(ephemerisId=ephemeris_id)[ "status" ] if status == "ENABLED": print("Ephemeris is ENABLED") break elif status in ["INVALID", "ERROR"]: raise RuntimeError(f"Ephemeris failed: {status}") time.sleep(5) # List available contacts print("Listing available contacts...") start_time = datetime.now(timezone.utc) + timedelta(hours=1) end_time = start_time + timedelta(days=1) contacts = ground_station_client.list_contacts( satelliteArn=satellite_arn, # Required for TLE/OEM ephemeris groundStation="Hawaii 1", missionProfileArn="arn:aws:groundstation:us-west-2:111122223333:mission-profile/example-profile", startTime=start_time, endTime=end_time, statusList=["AVAILABLE"], ) if contacts["contactList"]: # Reserve the first available contact contact = contacts["contactList"][0] print(f"Reserving contact from {contact['startTime']} to {contact['endTime']}...") reservation = ground_station_client.reserve_contact( satelliteArn=satellite_arn, # Required for TLE/OEM ephemeris missionProfileArn="arn:aws:groundstation:us-west-2:111122223333:mission-profile/example-profile", groundStation="Hawaii 1", startTime=contact["startTime"], endTime=contact["endTime"], # Note: trackingOverrides is optional for TLE/OEM # The system will use the highest priority ephemeris automatically ) print(f"Reserved contact: {reservation['contactId']}") else: print("No available contacts found")
Workflow 2: Direct contact reservation
This workflow directly reserves a contact without first listing available windows. This approach is useful when you already know your desired contact time or are implementing automated scheduling.
Example: Direct reservation with azimuth elevation ephemeris
import boto3 from datetime import datetime, timezone import time # Create AWS Ground Station client ground_station_client = boto3.client("groundstation") # Define contact window contact_start = datetime(2024, 3, 20, 14, 0, 0, tzinfo=timezone.utc) contact_end = datetime(2024, 3, 20, 14, 15, 0, tzinfo=timezone.utc) # Create azimuth elevation ephemeris for the specific contact time print("Creating azimuth elevation ephemeris...") ephemeris_response = ground_station_client.create_ephemeris( name="Direct Contact AzEl Ephemeris", ephemeris={ "azEl": { "groundStation": "Ohio 1", "data": { "azElData": { "angleUnit": "DEGREE_ANGLE", "azElSegmentList": [ { "referenceEpoch": contact_start.isoformat(), "validTimeRange": { "startTime": contact_start.isoformat(), "endTime": contact_end.isoformat(), }, "azElList": [ {"dt": 0.0, "az": 45.0, "el": 10.0}, {"dt": 180.0, "az": 50.0, "el": 15.0}, {"dt": 360.0, "az": 55.0, "el": 20.0}, {"dt": 540.0, "az": 60.0, "el": 25.0}, {"dt": 720.0, "az": 65.0, "el": 30.0}, {"dt": 900.0, "az": 70.0, "el": 35.0}, ], } ], } }, } }, ) ephemeris_id = ephemeris_response["ephemerisId"] print(f"Created ephemeris: {ephemeris_id}") # Wait for ephemeris to become ENABLED while True: status = ground_station_client.describe_ephemeris(ephemerisId=ephemeris_id)[ "status" ] if status == "ENABLED": print("Ephemeris is ENABLED") break elif status in ["INVALID", "ERROR"]: raise RuntimeError(f"Ephemeris failed: {status}") time.sleep(5) # Directly reserve the contact print(f"Reserving contact from {contact_start} to {contact_end}...") reservation = ground_station_client.reserve_contact( # Note: satelliteArn is omitted for azimuth elevation missionProfileArn="arn:aws:groundstation:us-east-2:111122223333:mission-profile/example-profile", groundStation="Ohio 1", startTime=contact_start, endTime=contact_end, trackingOverrides={"programTrackSettings": {"azEl": {"ephemerisId": ephemeris_id}}}, ) print(f"Reserved contact: {reservation['contactId']}")
Example: Direct reservation with TLE ephemeris
import boto3 from datetime import datetime, timedelta, timezone import time # Create AWS Ground Station client ground_station_client = boto3.client("groundstation") satellite_id = "12345678-1234-1234-1234-123456789012" satellite_arn = f"arn:aws:groundstation::111122223333:satellite/{satellite_id}" # Define contact window (based on predicted pass) contact_start = datetime(2024, 3, 21, 10, 30, 0, tzinfo=timezone.utc) contact_end = datetime(2024, 3, 21, 10, 42, 0, tzinfo=timezone.utc) # Create TLE ephemeris print("Creating TLE ephemeris...") ephemeris_response = ground_station_client.create_ephemeris( name="Direct Contact TLE Ephemeris", satelliteId=satellite_id, enabled=True, expirationTime=contact_end + timedelta(days=1), priority=1, ephemeris={ "tle": { "tleData": [ { "tleLine1": "1 25994U 99068A 24080.50000000 .00000075 00000-0 26688-4 0 9999", "tleLine2": "2 25994 98.2007 35.6589 0001234 89.2782 18.9934 14.57114995112000", "validTimeRange": { "startTime": (contact_start - timedelta(hours=1)).isoformat(), "endTime": (contact_end + timedelta(hours=1)).isoformat(), }, } ] } }, ) ephemeris_id = ephemeris_response["ephemerisId"] print(f"Created ephemeris: {ephemeris_id}") # Wait for ephemeris to become ENABLED while True: status = ground_station_client.describe_ephemeris(ephemerisId=ephemeris_id)[ "status" ] if status == "ENABLED": print("Ephemeris is ENABLED") break elif status in ["INVALID", "ERROR"]: raise RuntimeError(f"Ephemeris failed: {status}") time.sleep(5) # Directly reserve the contact print(f"Reserving contact from {contact_start} to {contact_end}...") reservation = ground_station_client.reserve_contact( satelliteArn=satellite_arn, # Required for TLE ephemeris missionProfileArn="arn:aws:groundstation:us-west-2:111122223333:mission-profile/example-profile", groundStation="Hawaii 1", startTime=contact_start, endTime=contact_end, # Note: trackingOverrides is optional for TLE # The system will use the highest priority ephemeris automatically ) print(f"Reserved contact: {reservation['contactId']}")
Monitoring contact state changes
After reserving a contact, it's important to monitor its state to ensure it successfully transitions
to SCHEDULED and to be notified of any issues. AWS Ground Station emits events to
Amazon EventBridge for all contact state changes.
Contact states follow this lifecycle:
-
SCHEDULING- The contact is being processed for scheduling -
SCHEDULED- The contact was successfully scheduled and will execute -
FAILED_TO_SCHEDULE- The contact could not be scheduled (terminal state)
For more information on contact states and lifecycle, see Understand contact lifecycle.
Implementing contact state monitoring with EventBridge
To monitor contact state changes in real-time, you can set up an Amazon EventBridge rule that triggers a Lambda function whenever a Ground Station contact changes state. This approach is more efficient and scalable than polling the contact status.
Implementation steps
-
Create a Lambda function to process contact state change events
-
Create an EventBridge rule that matches Ground Station contact state change events
-
Add the Lambda function as a target for the rule
Example Lambda function handler
For a complete example of a Lambda function that processes contact state change events, see the
GroundStationCloudWatchEventHandlerLambda resource in the
AquaSnppJpssTerraDigIF.yml AWS CloudFormation template. This template is available in the
AWS Ground Station customer onboarding Amazon S3 bucket. For instructions on accessing this template, see the
Putting it together section of the dataflow endpoint example.
EventBridge rule configuration
The EventBridge rule should use the following event pattern to match all Ground Station contact state changes:
{ "source": ["aws.groundstation"], "detail-type": ["Ground Station Contact State Change"] }
To filter for specific states only (e.g., failures), you can add a detail filter:
{ "source": ["aws.groundstation"], "detail-type": ["Ground Station Contact State Change"], "detail": { "contactStatus": [ "FAILED_TO_SCHEDULE", "FAILED", "AWS_FAILED", "AWS_CANCELLED" ] } }
For detailed instructions on creating EventBridge rules with Lambda targets, see Creating rules that react to events in the Amazon EventBridge User Guide.
Setting up EventBridge rules for automation
You can create EventBridge rules to automatically respond to contact state changes. For example:
-
Send notifications when a contact fails to schedule
-
Trigger Lambda functions to prepare resources when a contact enters
PREPASS -
Log contact completions for auditing purposes
For detailed information on setting up EventBridge rules for AWS Ground Station events, see Automate AWS Ground Station with Events.
Best practices and considerations
Handling scheduling conflicts
Since AWS Ground Station antennas are shared resources, a contact window that appears available in
ListContacts might be reserved by another customer before you can reserve it.
To handle this:
-
Always check the contact status after reservation
-
Implement retry logic with alternative time windows
-
Consider reserving contacts well in advance when possible
-
Use EventBridge events to monitor for
FAILED_TO_SCHEDULEstates
Ephemeris validation timing
Remember that ephemeris must be in ENABLED state before you can use it to
reserve contacts. The validation process typically takes a few seconds to a few minutes depending
on the ephemeris type and size. Always verify the ephemeris status before attempting to reserve contacts.
Contact timing considerations
When using custom ephemeris:
-
Ensure your ephemeris covers the entire contact duration
-
For azimuth elevation ephemeris, verify that the angles keep the antenna above the site mask throughout the contact
-
Consider ephemeris expiration times when scheduling future contacts
API differences by ephemeris type
The ReserveContact API behaves differently depending on the ephemeris type:
| Ephemeris Type | satelliteArn Required | trackingOverrides Required |
|---|---|---|
| TLE | Yes | No (optional) |
| OEM | Yes | No (optional) |
| Azimuth Elevation | No (optional) | Yes |