WarpTwin
Documentation for WarpTwin models and classes.
Loading...
Searching...
No Matches
script.py
###############################################################################
# Copyright (c) ATTX LLC 2025. All Rights Reserved.
#
# This software and associated documentation (the "Software") are the 
# proprietary and confidential information of ATTX INC. The Software is 
# furnished under a license agreement between ATTX and the user organization 
# and may be used or copied only in accordance with the terms of the agreement.
# Refer to 'license/attx_license.adoc' for standard license terms.
#
# EXPORT CONTROL NOTICE: THIS SOFTWARE MAY INCLUDE CONTENT CONTROLLED UNDER THE
# INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) OR THE EXPORT ADMINISTRATION 
# REGULATIONS (EAR99). No part of the Software may be used, reproduced, or 
# transmitted in any form or by any means, for any purpose, without the express 
# written permission of ATTX INC.
###############################################################################
"""
Simple Comms Study
-----------------------
This script demonstrates how to set up a simple data transfer simulation. It 
initializes a spacecraft, two ground stations, and simulates data transfer
between the spacecraft and the ground stations over a specified time period.

Author: Daniel Krobath
"""
import sys
from warptwin.WarpTwinPy import SimulationExecutive, CsvLogger, Time, connectSignals, DEGREES_TO_RADIANS
from warptwin.Spacecraft import Spacecraft
from warptwin.SpicePlanet import SpicePlanet
from warptwin.GroundStationModel import GroundStationModel
from warptwin.OrbitalElementsStateInit import OrbitalElementsStateInit
from warptwin.CommunicationsDataModel import CommunicationsDataModel
from warptwin.VisualsModel import VisualsModel

exc = SimulationExecutive()                     # Create our executive -- by convention named exc
exc.args().addDefaultArgument("end", 86400*15)  # Set end time in seconds
exc.parseArgs(sys.argv)                         # this interperets command-line inputs
exc.setRateSec(10)                              # We can setRateHz or setRateSec

# Create the Earth from SPICE kernels
earth = SpicePlanet(exc, "earth")

# Initialize orbital elements state
oe = OrbitalElementsStateInit(exc)
oe.params.a(6978140.0)                              # Semi-major axis in meters
oe.params.e(0.01)                                   # Eccentricity
oe.params.i(DEGREES_TO_RADIANS*75.0)                # Inclination in radians
oe.params.RAAN(DEGREES_TO_RADIANS*0.0)              # Right Ascension of Ascending Node in radians
oe.params.w(DEGREES_TO_RADIANS*0.0)                 # Argument of Periapsis in radians
oe.params.f(DEGREES_TO_RADIANS*0.0)                 # Mean Anomaly in radians

# Create the spacecraft and connect its initial state to the orbital elements
sc = Spacecraft(exc, "sc")
connectSignals(oe.outputs.pos__inertial, sc.params.initial_position)
connectSignals(oe.outputs.vel__inertial, sc.params.initial_velocity)
connectSignals(earth.outputs.self_id, sc.params.planet_ptr)

# Create ground stations
attx = GroundStationModel(exc, "ATTX")                                  # Create the ATTX ground station and establish the ENU frame
attx.params.spacecraft_frame.mapTo(sc.outputs.body)                     # Assign the spacecraft that we will be tracking
attx.params.planet_rotating_frame.mapTo(earth.outputs.rotating_frame)   # Set the planet fixed frame
attx.params.R_planet(6378140.0)                                         # Set the ground station radius from the center of the Earth
attx.params.latitude_detic_rad(DEGREES_TO_RADIANS*40.0)               # Set the geometric latitude in radians
attx.params.longitude_rad(DEGREES_TO_RADIANS*-105.0)                    # Set the longitude in radians
attx.params.elevation_mask_rad(DEGREES_TO_RADIANS*10.0)                 # Set the elevation mask in radians

rolla = GroundStationModel(exc, "Rolla")                               # Create the rolla ground station and establish the ENU frame
rolla.params.spacecraft_frame.mapTo(sc.outputs.body)                    # Assign the spacecraft that we will be tracking
rolla.params.planet_rotating_frame.mapTo(earth.outputs.rotating_frame)  # Set the planet fixed frame
rolla.params.R_planet(6378140.0)                                        # Set the ground station radius from the center of the Earth
rolla.params.latitude_detic_rad(DEGREES_TO_RADIANS*37.95)            # Set the geometric latitude in radians
rolla.params.longitude_rad(DEGREES_TO_RADIANS*-91.78)                 # Set the longitude in radians
rolla.params.elevation_mask_rad(DEGREES_TO_RADIANS*10.0)                # Set the elevation mask in radians

# Create two communications data models for the spacecraft and ground stations
attx_comm = CommunicationsDataModel(exc, "attx_comm")                   # Create the communications data model for the ATTX ground station
connectSignals(attx.outputs.range, attx_comm.inputs.range)
connectSignals(attx.outputs.masked, attx_comm.inputs.masked)
attx_comm.params.frequency(2.23e9)                                      # Frequency in Hz
attx_comm.params.bit_rate(1e6)                                          # Bit rate in bits per second
attx_comm.params.power_TX(30)                                           # Transmit power in decibel-milliwatts (dBm)
attx_comm.params.gain_TX(6.5)                                           # Transmit antenna gain in (dBi)
attx_comm.params.loss_TX(4.0)                                           # Transmit system losses in dB
attx_comm.params.antenna_diameter(0.5)                                  # receive antenna diameter in meters
attx_comm.params.antenna_efficiency(60)                                 # receive antenna efficiency as a percentage (0 to 100)
attx_comm.params.noise_temperature(250)                                 # Noise temperature in Kelvin
attx_comm.params.loss_RX(4.0)                                           # receive system losses in dB
attx_comm.params.energy_per_bit_to_noise_ratio_required(3)              # Required energy per bit to noise ratio in dB for link budget closure

rolla_comm = CommunicationsDataModel(exc, "Rolla_comm")               # Create the communications data model for the Kaena Point ground station
connectSignals(rolla.outputs.range, rolla_comm.inputs.range)
connectSignals(rolla.outputs.masked, rolla_comm.inputs.masked)
rolla_comm.params.frequency(2.23e9)                                      # Frequency in Hz
rolla_comm.params.bit_rate(1e6)                                          # Bit rate in bits per second
rolla_comm.params.power_TX(30)                                           # Transmit power in decibel-milliwatts (dBm)
rolla_comm.params.gain_TX(6.5)                                           # Transmit antenna gain in (dBi)
rolla_comm.params.loss_TX(4.0)                                           # Transmit system losses in dB
rolla_comm.params.antenna_diameter(0.5)                                  # receive antenna diameter in meters
rolla_comm.params.antenna_efficiency(60)                                 # receive antenna efficiency as a percentage (0 to 100)
rolla_comm.params.noise_temperature(250)                                 # Noise temperature in Kelvin
rolla_comm.params.loss_RX(4.0)                                           # receive system losses in dB
rolla_comm.params.energy_per_bit_to_noise_ratio_required(3)              # Required energy per bit to noise ratio in dB for link budget closure

# Set parameters for logging
# Be sure to name output parameters in similar fashion to what follows:

# Anything relating to a ground station will have to have the ground station name postpended
# to the output parameter name like below for the analysis script to work properly

# Additionally, both time and tdb_time must be logged with the exact names below
# for the analysis script to work properly
states = CsvLogger(exc, "states.csv")
states.addParameter(exc.time().base_time, "time")
states.addParameter(exc.time().tdb_time, "tdb_time")
states.addParameter(attx.outputs.masked, "masked_attx")
states.addParameter(rolla.outputs.masked, "masked_rolla")
states.addParameter(attx.outputs.range, "range_attx")
states.addParameter(attx.outputs.range_rate, "range_rate_attx")
states.addParameter(rolla.outputs.range, "range_rolla")
states.addParameter(rolla.outputs.range_rate, "range_rate_rolla")
states.addParameter(attx_comm.outputs.free_space_path_loss, "FSPL_attx")
states.addParameter(attx_comm.outputs.eff_isotropic_radiated_pwr, "EISP_attx")
states.addParameter(attx_comm.outputs.carrier_to_noise_density_ratio, "C/N0_attx")
states.addParameter(attx_comm.outputs.energy_per_bit_to_noise_ratio, "Eb/N0_attx")
states.addParameter(attx_comm.outputs.link_margin, "link_margin_attx")
states.addParameter(rolla_comm.outputs.free_space_path_loss, "FSPL_rolla")
states.addParameter(rolla_comm.outputs.eff_isotropic_radiated_pwr, "EISP_rolla")
states.addParameter(rolla_comm.outputs.carrier_to_noise_density_ratio, "C/N0_rolla")
states.addParameter(rolla_comm.outputs.energy_per_bit_to_noise_ratio, "Eb/N0_rolla")
states.addParameter(rolla_comm.outputs.link_margin, "link_margin_rolla")
exc.logManager().addLog(states, 1)

# Enable visuals
exc.enableVisuals()                                 # Enable visuals for the simulation
exc.visualsModel().addFrame(sc.outputs.body())      # Add the spacecraft body frame to the visuals
exc.visualsModel().addGroundStation(attx)           # Add the ATTX ground station to the visuals
exc.visualsModel().addGroundStation(rolla)         # Add the rolla ground station to the visuals

# Run the simulation
exc.startup()
exc.run()