# ISC License
#
# Copyright (c) 2020, 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.
#
# Unit Test Script
# Module Name: Planet's Albedo
# Author: Demet Cilden-Guler
# Creation Date: May 28, 2020
#
import os
import numpy as np
import pytest
from Basilisk import __path__
from Basilisk.architecture import messaging
from Basilisk.simulation import albedo
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import macros
from Basilisk.utilities import orbitalMotion as om
from Basilisk.utilities import simIncludeGravBody
from Basilisk.utilities import unitTestSupport
from Basilisk.architecture import bskLogging
bskPath = __path__[0]
path = os.path.dirname(os.path.abspath(__file__))
# uncomment this line if this test has an expected failure, adjust message as needed
# @pytest.mark.xfail(True)
[docs]
@pytest.mark.parametrize("planetCase", ['earth', 'mars'])
@pytest.mark.parametrize("modelType", ['ALBEDO_AVG_IMPLICIT', 'ALBEDO_AVG_EXPLICIT', 'ALBEDO_DATA'])
@pytest.mark.parametrize("useEclipse", [True, False])
def test_unitAlbedo(show_plots, planetCase, modelType, useEclipse):
"""
**Validation Test Description**
This section describes the specific unit tests conducted on this module.
The test contains 4 tests and is located at ``test_albedo.py``.
The success criteria is to match the outputs with the generated truth.
Args:
planetCase (string): Defines which planet to use. Options include "earth" and "mars".
modelType (string): Defines which albedo model to use. Options include "ALBEDO_AVG_EXPLICIT", "ALBEDO_AVG_IMPLICIT" and "ALBEDO_DATA".
useEclipse (bool): Defines if the eclipse is considered for this parameterized unit test.
**Description of Variables Being Tested**
In this file, we are checking the values of the variable:
``albedoAtInstrument``
which are pulled from the log data to see if they match with the expected truth values.
"""
# each test method requires a single assert method to be called
[testResults, testMessage] = unitAlbedo(show_plots, planetCase, modelType, useEclipse)
assert testResults < 1, testMessage
def unitAlbedo(show_plots, planetCase, modelType, useEclipse):
__tracebackhide__ = True
testFailCount = 0
testMessages = []
testTaskName = "unitTestTask"
testProcessName = "unitTestProcess"
testTaskRate = macros.sec2nano(1.0)
# Create a simulation container
unitTestSim = SimulationBaseClass.SimBaseClass()
testProc = unitTestSim.CreateNewProcess(testProcessName)
testProc.addTask(unitTestSim.CreateNewTask(testTaskName, testTaskRate))
# create planet input message
planetInMsg = messaging.SpicePlanetStateMsg()
# Albedo A1
albModule = albedo.Albedo()
albModule.ModelTag = "Albedo_0"
if modelType == 'ALBEDO_DATA':
dataPath = os.path.abspath(bskPath + "/supportData/AlbedoData/")
if planetCase == 'earth':
fileName = "Earth_ALB_2018_CERES_All_10x10.csv"
else:
fileName = "Mars_ALB_TES_10x10.csv"
albModule.addPlanetandAlbedoDataModel(planetInMsg, dataPath, fileName)
else:
ALB_avg = 0.5
numLat = 200
numLon = 400
if modelType == 'ALBEDO_AVG_EXPLICIT':
albModule.addPlanetandAlbedoAverageModel(planetInMsg, ALB_avg, numLat, numLon)
else:
albModule.addPlanetandAlbedoAverageModel(planetInMsg)
if useEclipse:
albModule.eclipseCase = True
# Create dummy sun message
sunPositionMsg = messaging.SpicePlanetStateMsgPayload()
# Create dummy planet message
planetPositionMsg = messaging.SpicePlanetStateMsgPayload()
planetPositionMsg.PositionVector = [0., 0., 0.]
gravFactory = simIncludeGravBody.gravBodyFactory()
if planetCase == 'earth':
planet = gravFactory.createEarth()
sunPositionMsg.PositionVector = [-om.AU * 1000., 0.0, 0.0]
elif planetCase == 'mars':
planet = gravFactory.createMars()
sunPositionMsg.PositionVector = [-1.5 * om.AU * 1000., 0.0, 0.0]
planetPositionMsg.PlanetName = planetCase
planetPositionMsg.J20002Pfix = np.identity(3)
req = planet.radEquator
sunMessage = "sun_message"
# Create dummy spacecraft message
scStateMsg = messaging.SCStatesMsgPayload()
rSC = req + 6000 * 1000 # meters
alpha = 71. * macros.D2R
scStateMsg.r_BN_N = np.dot(rSC, [np.cos(alpha), np.sin(alpha), 0.0])
scStateMsg.sigma_BN = [0., 0., 0.]
# Albedo instrument configuration
config1 = albedo.instConfig_t()
config1.fov = 80. * macros.D2R
config1.nHat_B = np.array([-np.cos(alpha), -np.sin(alpha), 0.0])
config1.r_IB_B = np.array([0., 0., 0.])
albModule.addInstrumentConfig(config1)
sunInMsg = messaging.SpicePlanetStateMsg().write(sunPositionMsg)
albModule.sunPositionInMsg.subscribeTo(sunInMsg)
planetInMsg.write(planetPositionMsg)
scInMsg = messaging.SCStatesMsg().write(scStateMsg)
albModule.spacecraftStateInMsg.subscribeTo(scInMsg)
unitTestSim.AddModelToTask(testTaskName, albModule)
# setup logging
dataLog = albModule.albOutMsgs[0].recorder()
unitTestSim.AddModelToTask(testTaskName, dataLog)
# Initialize and run simulation one step at a time
unitTestSim.InitializeSimulation()
# Execute the simulation for one time step
unitTestSim.TotalSim.SingleStepProcesses()
# This pulls the actual data log from the simulation run.
dataAlb0 = dataLog.albedoAtInstrument
errTol = 1E-12
if planetCase == 'earth':
if modelType == 'ALBEDO_DATA':
if useEclipse:
truthAlb = 0.0022055492477917
else:
truthAlb = 0.0022055492477917
else:
if modelType == 'ALBEDO_AVG_EXPLICIT':
if useEclipse:
truthAlb = 0.0041742091531996
else:
truthAlb = 0.004174209177079
else:
if useEclipse:
truthAlb = 0.002421222716229847
else:
truthAlb = 0.002421222716229847
else:
if modelType == 'ALBEDO_DATA':
if useEclipse:
truthAlb = 0.0014001432717662
else:
truthAlb = 0.0014001432717662
else:
if modelType == 'ALBEDO_AVG_EXPLICIT':
if useEclipse:
truthAlb = 0.0035681407388827
else:
truthAlb = 0.0035681407390035
else:
if useEclipse:
truthAlb = 0.0011418311186365906
else:
truthAlb = 0.0011418311186365906
if not unitTestSupport.isDoubleEqual(dataAlb0[0], truthAlb, errTol):
testFailCount += 1
# print out success or failure message
if testFailCount == 0:
print("PASSED: " + albModule.ModelTag)
else:
print("Failed: " + albModule.ModelTag)
print("This test uses a relative accuracy value of " + str(errTol * 100) + " percent")
return [testFailCount, ''.join(testMessages)]
[docs]
def test_albedo_invalid_file(tmp_path):
"""Verify that Albedo model returns gracefully when file cannot be loaded.
Regression test for BSK-428 where model would segfault when invalid file
was specified.
.. note:: The model is not in a usable state if this initialization fails.
Ideally an exception would be thrown, but the SWIG infrastructure doesn't
appear to be setup to handle C++ exceptions, so we settle for printing a
message and not segfaulting.
"""
albModule = albedo.Albedo()
# silence expected error message
albModule.bskLogger.setLogLevel(bskLogging.BSK_SILENT)
gravFactory = simIncludeGravBody.gravBodyFactory()
gravFactory.createEarth()
planetPositionMsg = messaging.SpicePlanetStateMsgPayload()
planetPositionMsg.PlanetName = "earth"
planetInMsg = messaging.SpicePlanetStateMsg().write(planetPositionMsg)
sunPositionMsg = messaging.SpicePlanetStateMsgPayload()
sunInMsg = messaging.SpicePlanetStateMsg().write(sunPositionMsg)
albModule.sunPositionInMsg.subscribeTo(sunInMsg)
scStateMsg = messaging.SCStatesMsgPayload()
scInMsg = messaging.SCStatesMsg().write(scStateMsg)
albModule.spacecraftStateInMsg.subscribeTo(scInMsg)
albModule.addPlanetandAlbedoDataModel(planetInMsg, str(tmp_path), "does_not_exit.file")
# this call would previously segfault
albModule.Reset(0)
# the fact that we got here without segfaulting means the test
# passed
assert True
if __name__ == "__main__":
# unitAlbedo(False, 'earth', 'ALBEDO_AVG_EXPLICIT', True)
unitAlbedo(False, 'mars', 'ALBEDO_AVG_IMPLICIT', False)