Source code for test_orb_elem_convert


# 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 )