# ISC License
#
# Copyright (c) 2016, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import inspect
import math
import os
import matplotlib.pyplot as plt
import numpy
import pytest
#
# orb_elem_convert Unit Test
#
# Purpose: Test the precision of the orb_elem_convert module. Functionality
# is tested by comparing input/output data as well as calculated
# conversions.
# Author: Gabriel Chapel
# Creation Date: July 27, 2017
#
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
splitPath = path.split('simulation')
from Basilisk.utilities import SimulationBaseClass
from Basilisk.simulation import orbElemConvert
from Basilisk.utilities import macros
from Basilisk.utilities import macros as mc
from Basilisk.utilities import unitTestSupport
from Basilisk.architecture import messaging
from Basilisk.utilities.pythonVariableLogger import PythonVariableLogger
# Class in order to plot using data across the different parameterized scenarios
class DataStore:
def __init__(self):
self.Date = [] # replace these with appropriate containers for the data to be stored for plotting
self.MarsPosErr = []
self.EarthPosErr = []
self.SunPosErr = []
[docs]
@pytest.mark.parametrize("a, e, i, AN, AP, f, mu, name", [
# Inclined Elliptical Orbit Varying e
(10000000.0, 0.01, 33.3*mc.D2R, 48.2*mc.D2R, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 'IncEllip_e_1'),
(10000000.0, 0.10, 33.3*mc.D2R, 48.2*mc.D2R, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 0),
(10000000.0, 0.25, 33.3*mc.D2R, 48.2*mc.D2R, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 0),
(10000000.0, 0.50, 33.3*mc.D2R, 48.2*mc.D2R, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 0),
(10000000.0, 0.75, 33.3*mc.D2R, 48.2*mc.D2R, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 'IncEllip_e_2'),
# Inclined Elliptical Orbit Varying a
(10000000.0, 0.50, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncEllip_a_1'),
(100000.0, 0.50, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10000.0, 0.50, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(1000.0, 0.50, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(100.0, 0.50, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10.0, 0.50, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncEllip_a_2'),
# Equatorial Elliptical Orbit Varying e
(10000000.0, 0.01, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquEllip_e_1'),
(10000000.0, 0.10, 0.0, 0.0, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 0),
(10000000.0, 0.25, 0.0, 0.0, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 0),
(10000000.0, 0.50, 0.0, 0.0, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 0),
(10000000.0, 0.75, 0.0, 0.0, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15, 'EquEllip_e_2'),
# Equatorial Elliptical Orbit Varying a
(10000000.0, 0.50, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquEllip_a_1'), # For i=0 => AN=0
(100000.0, 0.50, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10000.0, 0.50, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(1000.0, 0.50, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(100.0, 0.50, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10.0, 0.50, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquEllip_a_2'),
# Inclined Circular Orbit
(10000000.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 'IncCirc_1'),
(1000000.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(100000.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10000.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(1000.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(100.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 'IncCirc_2'),
# Equatorial Circular Orbit
(10000000.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 'EquCirc_1'),
(1000000.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(100000.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10000.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(1000.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(100.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(10.0, 0.0, 0.0, 0.0, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 'EquCirc_2'),
# Inclined Parabolic Orbit
(-10.0, 1.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncPara_1'), # For input of -a,
(-100.0, 1.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0), # must have e= 1.0
(-1000.0, 1.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0), # or e >1.0
(-10000.0, 1.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncPara_2'),
# Equatorial Parabolic Orbit
(-10.0, 1.0, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquPara_1'), # For input of -a,
(-100.0, 1.0, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0), # must have e= 1.0
(-1000.0, 1.0, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0), # or e >1.0
(-10000.0, 1.0, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.0, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquPara_2'),
# Inclined Hyperbolic Orbit varying a
(-10.0, 1.3, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncHyp_a_1'),
(-100.0, 1.3, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-1000.0, 1.3, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-10000.0, 1.3, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.3, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncHyp_a_2'),
# Inclined Hyperbolic Orbit varying e
(-100000.0, 1.1, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncHyp_e_1'),
(-100000.0, 1.2, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.3, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.4, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.5, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'IncHyp_e_2'),
# Equatorial Hyperbolic Orbit varying a
(-10.0, 1.3, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquHyp_a_1'),
(-100.0, 1.3, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-1000.0, 1.3, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-10000.0, 1.3, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.3, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquHyp_a_2'),
# Equatorial Hyperbolic Orbit varying e
(-100000.0, 1.1, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquHyp_e_1'),
(-100000.0, 1.2, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.3, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.4, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 0),
(-100000.0, 1.5, 0.0, 0.0, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15, 'EquHyp_e_2'),
# # Approaching circular orbit
# (100000.0, 0.000001, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15),
# These don't work
# (10000000.0, 1.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 347.8 * mc.D2R, 85.3 * mc.D2R, 0.3986004415E+15), # or e >1.0
# (-10, 0.9, 33.3*mc.D2R, 48.2*mc.D2R, 347.8*mc.D2R, 85.3*mc.D2R, 0.3986004415E+15)
])
# provide a unique test method name, starting with test_
def test_orb_elem_convert(a, e, i, AN, AP, f, mu, name, DispPlot=False):
"""Module Unit Test"""
# each test method requires a single assert method to be called
[testResults, testMessage] = orbElem(a, e, i, AN, AP, f, mu, name, DispPlot)
assert testResults < 1, testMessage
# Run unit test
def orbElem(a, e, i, AN, AP, f, mu, name, DispPlot):
# Elem2RV
testFailCount1 = 0 # zero unit test result counter
testMessages = [] # create empty array to store test log messages
# Create a sim module as an empty container
unitTaskName = "unitTask" # arbitrary name (don't change)
unitProcessName = "TestProcess" # arbitrary name (don't change)
# Create a sim module as an empty container
TotalSim = SimulationBaseClass.SimBaseClass()
DynUnitTestProc = TotalSim.CreateNewProcess(unitProcessName)
# # create the dynamics task and specify the integration update time
testProcessRate = macros.sec2nano(1.0)
DynUnitTestProc.addTask(TotalSim.CreateNewTask(unitTaskName, testProcessRate))
# Initialize the modules that we are using.
orb_elemObject = orbElemConvert.OrbElemConvert()
orb_elemObject.ModelTag = "OrbElemConvertData"
# Add Model To Task
TotalSim.AddModelToTask(unitTaskName, orb_elemObject)
# Set element values
epsDiff = 0.0000001
orb_elemObject.mu = mu
###### ELEM2RV ######
orb_elemObjectLog = orb_elemObject.logger(["r_N", "v_N"])
TotalSim.AddModelToTask(unitTaskName, orb_elemObjectLog)
# Create and write messages
ElemMessage = messaging.ClassicElementsMsgPayload()
elemMsg = messaging.ClassicElementsMsg()
if e == 1.0:
ElemMessage.a = 0.0
ElemMessage.rPeriap = -a
else:
ElemMessage.a = a # meters
ElemMessage.e = e
ElemMessage.i = i
ElemMessage.Omega = AN
ElemMessage.omega = AP
ElemMessage.f = f
elemMsg.write(ElemMessage)
orb_elemObject.elemInMsg.subscribeTo(elemMsg)
# Log Message to test WriteOutputMessage()
dataLog = orb_elemObject.spiceStateOutMsg.recorder()
TotalSim.AddModelToTask(unitTaskName, dataLog)
# Execute simulation
TotalSim.ConfigureStopTime(int(1E9))
TotalSim.InitializeSimulation()
TotalSim.ExecuteSimulation()
# Get r and v from sim
vSim = orb_elemObjectLog.v_N[-1]
rSim = orb_elemObjectLog.r_N[-1]
# Get r and v from message
rMsgPlanet = dataLog.PositionVector[-1]
vMsgPlanet = dataLog.VelocityVector[-1]
rMsgPlanetDiff = numpy.subtract(rSim, rMsgPlanet)
for g in range(3):
if abs(rMsgPlanetDiff[g]) > 0:
testMessages.append(" FAILED: Planet Position Message, column " + str(g))
testFailCount1 += 1
vMsgPlanetDiff = numpy.subtract(vSim, vMsgPlanet)
for g in range(3):
if abs(vMsgPlanetDiff[g]) > 0:
testMessages.append(" FAILED: Planet Velocity Message, column " + str(g))
testFailCount1 += 1
# Calculation of elem2rv
if e == 1.0 and a > 0.0: # rectilinear elliptic orbit case
Ecc = f # f is treated as ecc.anomaly
r = a * (1 - e * math.cos(Ecc)) # orbit radius
v = math.sqrt(2 * mu / r - mu / a)
ir = numpy.zeros(3)
ir[0] = math.cos(AN) * math.cos(AP) - math.sin(AN) * math.sin(AP) * math.cos(i)
ir[1] = math.sin(AN) * math.cos(AP) + math.cos(AN) * math.sin(AP) * math.cos(i)
ir[2] = math.sin(AP) * math.sin(i)
rTruth = numpy.multiply(r, ir)
if math.sin(Ecc) > 0:
vTruth = numpy.multiply(-v, ir)
else:
vTruth = numpy.multiply(v, ir)
else:
if e == 1 and a < 0: # parabolic case
rp = -a # radius at periapses
p = 2 * rp # semi-latus rectum
a = 0.0
else: # elliptic and hyperbolic cases
p = a * (1 - e * e) # semi-latus rectum
r = p / (1 + e * math.cos(f)) # orbit radius
theta = AP + f # true latitude angle
h = math.sqrt(mu * p) # orbit ang.momentum mag.
rTruth = numpy.zeros(3)
rTruth[0] = r * (math.cos(AN) * math.cos(theta) - math.sin(AN) * math.sin(theta) * math.cos(i))
rTruth[1] = r * (math.sin(AN) * math.cos(theta) + math.cos(AN) * math.sin(theta) * math.cos(i))
rTruth[2] = r * (math.sin(theta) * math.sin(i))
vTruth = numpy.zeros(3)
vTruth[0] = -mu / h * (math.cos(AN) * (math.sin(theta) + e * math.sin(AP)) + math.sin(AN) * (math.cos(
theta) + e * math.cos(AP)) * math.cos(i))
vTruth[1] = -mu / h * (math.sin(AN) * (math.sin(theta) + e * math.sin(AP)) - math.cos(AN) * (math.cos(
theta) + e * math.cos(AP)) * math.cos(i))
vTruth[2] = -mu / h * (-(math.cos(theta) + e * math.cos(AP)) * math.sin(i))
# Position and Velocity Diff Checks
rDiff = numpy.subtract(rSim, rTruth)
vDiff = numpy.subtract(vSim, vTruth)
rDiffcsv = numpy.asarray(rDiff)
vDiffcsv = numpy.asarray(vDiff)
for g in range(3):
if abs(rDiff[g]) > epsDiff:
testMessages.append(" FAILED: Position Vector, column " + str(g))
testFailCount1 += 1
for g in range(3):
if abs(vDiff[g]) > epsDiff:
testMessages.append(" FAILED: Velocity Vector, column " + str(g))
testFailCount1 += 1
if name != 0:
if testFailCount1 == 0:
colorText = 'ForestGreen'
passFailMsg = "" # "Passed: " + name + "."
passedText = r'\textcolor{' + colorText + '}{' + "PASSED" + '}'
else:
colorText = 'Red'
passFailMsg = "Failed: " + name + "."
testMessages.append(passFailMsg)
testMessages.append(" | ")
passedText = r'\textcolor{' + colorText + '}{' + "FAILED" + '}'
# Write some snippets for AutoTex
snippetName = name + "PassedText1"
snippetContent = passedText
unitTestSupport.writeTeXSnippet(snippetName, snippetContent, path)
snippetName = name + "PassFailMsg1"
snippetContent = passFailMsg
unitTestSupport.writeTeXSnippet(snippetName, snippetContent, path)
###### RV2ELEM ######
# RV2Elem
testFailCount2 = 0 # zero unit test result counter
for g in range(2):
TotalSim = SimulationBaseClass.SimBaseClass()
DynUnitTestProc = TotalSim.CreateNewProcess(unitProcessName)
# # create the dynamics task and specify the integration update time
testProcessRate = macros.sec2nano(1.0)
DynUnitTestProc.addTask(TotalSim.CreateNewTask(unitTaskName, testProcessRate))
# Initialize the modules that we are using.
orb_elemObject = orbElemConvert.OrbElemConvert()
orb_elemObject.ModelTag = "OrbElemConvertData"
# Add Model To Task
TotalSim.AddModelToTask(unitTaskName, orb_elemObject)
# Log Variables
elemLog = PythonVariableLogger({
"a": lambda _: orb_elemObject.CurrentElem.a,
"e": lambda _: orb_elemObject.CurrentElem.e,
"i": lambda _: orb_elemObject.CurrentElem.i,
"Omega": lambda _: orb_elemObject.CurrentElem.Omega,
"omega": lambda _: orb_elemObject.CurrentElem.omega,
"f": lambda _: orb_elemObject.CurrentElem.f,
})
TotalSim.AddModelToTask(unitTaskName, elemLog)
orb_elemObjectLog = orb_elemObject.logger(["r_N", "v_N"])
TotalSim.AddModelToTask(unitTaskName, orb_elemObjectLog)
orb_elemObject.mu = mu
if g == 0:
CartMessage = messaging.SCStatesMsgPayload()
CartMessage.r_BN_N = rSim
CartMessage.v_BN_N = vSim
stateScMsg = messaging.SCStatesMsg().write(CartMessage)
orb_elemObject.scStateInMsg.subscribeTo(stateScMsg)
else:
CartMessage = messaging.SpicePlanetStateMsgPayload()
CartMessage.PositionVector = rSim
CartMessage.VelocityVector = vSim
stateSpMsg = messaging.SpicePlanetStateMsg().write(CartMessage)
orb_elemObject.spiceStateInMsg.subscribeTo(stateSpMsg)
dataElemLog = orb_elemObject.elemOutMsg.recorder()
TotalSim.AddModelToTask(unitTaskName, dataElemLog)
# Execute simulation
TotalSim.ConfigureStopTime(int(1E9))
TotalSim.InitializeSimulation()
TotalSim.ExecuteSimulation()
aOut = elemLog.a[-1]
eOut = elemLog.e[-1]
iOut = elemLog.i[-1]
ANOut = elemLog.Omega[-1]
APOut = elemLog.omega[-1]
fOut = elemLog.f[-1]
# Element Diff Check
ElemDiff = [(a - aOut), (e - eOut), (i - iOut), (AN - ANOut), (AP - APOut), (f - fOut)]
ElemDiffcsv = numpy.asarray(ElemDiff)
for g in range(6):
# check for angle roll over with 2*pi
if g > 2:
if abs(ElemDiff[g] - 2 * math.pi) < epsDiff:
ElemDiff[g] -= 2 * math.pi
elif abs(ElemDiff[g] + 2 * math.pi) < epsDiff:
ElemDiff[g] += 2 * math.pi
if abs(ElemDiff[g]) > epsDiff:
testMessages.append(" FAILED: Sim Orbital Element " + str(g))
testFailCount2 += 1
aMsg = dataElemLog.a[-1]
eMsg = dataElemLog.e[-1]
iMsg = dataElemLog.i[-1]
ANMsg = dataElemLog.Omega[-1]
APMsg = dataElemLog.omega[-1]
fMsg = dataElemLog.f[-1]
ElemMsgDiff = [(aOut - aMsg), (eOut - eMsg), (iOut - iMsg), (ANOut - ANMsg), (APOut - APMsg), (fOut - fMsg)]
for g in range(6):
# check for angle roll over with 2*pi
if g > 2:
if abs(ElemDiff[g] - 2 * math.pi) < epsDiff:
ElemDiff[g] -= 2 * math.pi
elif abs(ElemDiff[g] + 2 * math.pi) < epsDiff:
ElemDiff[g] += 2 * math.pi
if abs(ElemMsgDiff[g]) > 0:
testMessages.append(" FAILED: Orbital Element Message " + str(g))
testFailCount2 += 1
######### Calculate rv2elem #########
# Calculate the specific angular momentum and its magnitude
epsConv = 0.0000000001
hVec = numpy.cross(rTruth, vTruth)
h = numpy.linalg.norm(hVec)
p = h * h / mu
# Calculate the line of nodes
v3 = numpy.array([0.0, 0.0, 1.0])
nVec = numpy.cross(v3, hVec)
n = numpy.linalg.norm(nVec)
# Orbit eccentricity and energy
r = numpy.linalg.norm(rTruth)
v = numpy.linalg.norm(vTruth)
eVec = numpy.multiply(v * v / mu - 1.0 / r, rTruth)
v3 = numpy.multiply(numpy.dot(rTruth, vTruth) / mu, vTruth)
eVec = numpy.subtract(eVec, v3)
eO = numpy.linalg.norm(eVec)
rmag = r
rPeriap = p / (1.0 + eO)
# compute semi - major axis
alpha = 2.0 / r - v * v / mu
if (math.fabs(alpha) > epsConv): # elliptic or hyperbolic case
aO = 1.0 / alpha
rApoap = p / (1.0 - eO)
else: # parabolic case
rp = p / 2.0
aO = 0.0 # a is not defined for parabola, so -rp is returned instead
rApoap = 0.0
# Calculate the inclination
iO = math.acos(hVec[2] / h)
# Calculate AP, AN, and True anomaly
if eO >= 1e-11 and iO >= 1e-11:
# Case 1: Non - circular, inclined orbit
Omega = math.acos(nVec[0] / n)
if (nVec[1] < 0.0):
Omega = 2.0 * math.pi - Omega
omega = math.acos(numpy.dot(nVec, eVec) / n / eO)
if eVec[2] < 0.0:
omega = 2.0 * math.pi - omega
fO = math.acos(numpy.dot(eVec, rTruth) / eO / r)
if numpy.dot(rTruth, vTruth) < 0.0:
fO = 2.0 * math.pi - fO
elif eO >= 1e-11 and iO < 1e-11:
# Case 2: Non - circular, equatorial orbit
# Equatorial orbit has no ascending node
Omega = 0.0
# True longitude of periapsis, omegatilde_true
omega = math.acos(eVec[0] / eO)
if eVec[1] < 0.0:
omega = 2.0 * math.pi - omega
fO = math.acos(numpy.dot(eVec, rTruth) / eO / r)
if numpy.dot(rTruth, vTruth) < 0.0:
fO = 2.0 * math.pi - fO
elif eO < 1e-11 and iO >= 1e-11:
# Case 3: Circular, inclined orbit
Omega = math.acos(nVec[0] / n)
if (nVec[1] < 0.0):
Omega = 2.0 * math.pi - Omega
omega = 0.0
# Argument of latitude, u = omega + f * /
fO = math.acos(numpy.dot(nVec, rTruth) / n / r)
if rTruth[2] < 0.0:
fO = 2.0 * math.pi - fO
elif eO < 1e-11 and iO < 1e-11:
# Case 4: Circular, equatorial orbit
Omega = 0.0
omega = 0.0
# True longitude, lambda_true
fO = math.acos(rTruth[0] / r)
if rTruth[1] < 0:
fO = 2.0 * math.pi - fO
else:
print("Error: rv2elem couldn't identify orbit type.\n")
if (eO >= 1.0 and math.fabs(fO) > math.pi):
twopiSigned = math.copysign(2.0 * math.pi, fO)
fO -= twopiSigned
# Element Diff Check
ElemCalcDiff = [(aO - aOut), (eO - eOut), (iO - iOut), (Omega - ANOut), (omega - APOut), (fOut - fOut)]
ElemCalcDiffcsv = numpy.asarray(ElemCalcDiff)
for g in range(6):
# check for angle roll over with 2*pi
if g > 2:
if abs(ElemCalcDiff[g] - 2 * math.pi) < epsDiff:
ElemCalcDiff[g] -= 2 * math.pi
elif abs(ElemCalcDiff[g] + 2 * math.pi) < epsDiff:
ElemCalcDiff[g] += 2 * math.pi
if abs(ElemCalcDiff[g]) > epsDiff:
testMessages.append(" FAILED: Calculated Orbital Element " + str() + str(g))
testFailCount2 += 1
# create plot
# txt = 'e = ' + str(e) + ' and a = ' + str(a) + 'km'
fact = (len(str(abs(a)))-3.0)
plt.figure(1,figsize=(7, 5), dpi=80, facecolor='w', edgecolor='k')
plt.clf()
# fig1.text(.5, .05, txt, ha='center')
ax1 = plt.subplot(211)
ax1.cla()
index = numpy.arange(3)
bar_width = 0.35
opacity = 0.8
rects1 = ax1.bar(index, rSim, bar_width, alpha=opacity, color='b', label='Simulated Position')
rects2 = ax1.bar(index + bar_width, rTruth, bar_width, alpha=opacity, color='g', label='Calculated Position')
ax1.spines['left'].set_position('zero')
ax1.spines['right'].set_color('none')
ax1.spines['bottom'].set_position('zero')
ax1.spines['top'].set_color('none')
for xtick in ax1.get_xticklabels():
xtick.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.5))
ax1.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('left')
plt.ylabel('Position (m)')
plt.xticks(index + bar_width, ('X', 'Y', 'Z'))
plt.legend(loc='lower right')
ax2 = plt.subplot(212)
ax2.cla()
rects1 = ax2.bar(index, vSim, bar_width, alpha=opacity, color='b', label='Simulated Velocity')
rects2 = ax2.bar(index + bar_width, vTruth, bar_width, alpha=opacity, color='g', label='Calculated Velocity')
ax2.spines['left'].set_position('zero')
ax2.spines['right'].set_color('none')
ax2.spines['bottom'].set_position('zero')
ax2.spines['top'].set_color('none')
for xtick in ax2.get_xticklabels():
xtick.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.5))
ax2.xaxis.set_ticks_position('bottom')
ax2.yaxis.set_ticks_position('left')
plt.ylabel('Velocity (m/s)')
plt.xticks(index + bar_width, ('X', 'Y', 'Z'))
plt.legend(loc='lower right')
if name != 0:
unitTestSupport.writeFigureLaTeX(name, "$e = " + str(e) + "$ and $a = 10^" + str(int(fact)) + "$km",
plt, 'height=0.7\\textwidth, keepaspectratio', path)
if testFailCount2 == 0:
colorText = 'ForestGreen'
passFailMsg = "" # "Passed: " + name + "."
passedText = r'\textcolor{' + colorText + '}{' + "PASSED" + '}'
else:
colorText = 'Red'
passFailMsg = "Failed: " + name + "."
testMessages.append(passFailMsg)
testMessages.append(" | ")
passedText = r'\textcolor{' + colorText + '}{' + "FAILED" + '}'
# Write some snippets for AutoTex
snippetName = name + "PassedText2"
snippetContent = passedText
unitTestSupport.writeTeXSnippet(snippetName, snippetContent, path)
snippetName = name + "PassFailMsg2"
snippetContent = passFailMsg
unitTestSupport.writeTeXSnippet(snippetName, snippetContent, path)
if DispPlot:
plt.show()
plt.close()
testFailCount = testFailCount1+testFailCount2
if testFailCount:
print("Failed")
print(testMessages)
else:
print("PASSED")
return [testFailCount, ''.join(testMessages)]
if __name__ == "__main__":
orbElem(10000000.0, 0.0, 33.3 * mc.D2R, 48.2 * mc.D2R, 0.0, 85.3 * mc.D2R, 0.3986004415E+15, 0, True
)