# 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.
#
# Integrated Unit Test Script
# Purpose: Run a test of the star tracker module
# Author: John Alcorn
# Creation Date: October 12, 2016
#
import pytest
import numpy as np
import six
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import unitTestSupport # general support file with common unit test functions
from Basilisk.utilities import macros
from Basilisk.utilities import RigidBodyKinematics as rbk
from Basilisk.architecture import messaging
from Basilisk.simulation import starTracker
# methods
def listStack(vec,simStopTime,unitProcRate):
# returns a list duplicated the number of times needed to be consistent with module output
return [vec] * int(simStopTime/(float(unitProcRate)/float(macros.sec2nano(1))))
def setRandomWalk(self, senNoiseStd = 0.0, errorBounds = [[1e6],[1e6],[1e6]]):
# sets the module random walk variables
PMatrix = [[senNoiseStd, 0., 0.], [0., senNoiseStd, 0.], [0., 0., senNoiseStd]]
self.PMatrix = PMatrix
self.walkBounds = errorBounds
# uncomment this line is this test is to be skipped in the global unit test run, adjust message as needed
# @pytest.mark.skipif(conditionstring)
# uncomment this line if this test has an expected failure, adjust message as needed
# The following 'parametrize' function decorator provides the parameters and expected results for each
# of the multiple test runs for this test.
[docs]@pytest.mark.parametrize("useFlag, testCase", [
(False,'basic'),
(False,'noise'),
(False,'walk bounds')
])
# provide a unique test method name, starting with test_
def test_unitSimStarTracker(show_plots, useFlag, testCase):
"""Module Unit Test"""
# each test method requires a single assert method to be called
[testResults, testMessage] = unitSimStarTracker(show_plots, useFlag, testCase)
assert testResults < 1, testMessage
def unitSimStarTracker(show_plots, useFlag, testCase):
testFail = False
testFailCount = 0 # zero unit test result counter
testMessages = [] # create empty array to store test log messages
unitTaskName = "unitTask" # arbitrary name (don't change)
unitProcName = "TestProcess" # arbitrary name (don't change)
# initialize SimulationBaseClass
unitSim = SimulationBaseClass.SimBaseClass()
# create the task and specify the integration update time
unitProcRate = macros.sec2nano(0.1)
unitProcRate_s = macros.NANO2SEC*unitProcRate
unitProc = unitSim.CreateNewProcess(unitProcName)
unitProc.addTask(unitSim.CreateNewTask(unitTaskName, unitProcRate))
# configure module
StarTracker = starTracker.StarTracker()
StarTracker.ModelTag = "StarTracker"
setRandomWalk(StarTracker)
# configure module input message
OutputStateData = messaging.SCStatesMsgPayload()
OutputStateData.r_BN_N = [0,0,0]
OutputStateData.v_BN_N = [0,0,0]
OutputStateData.sigma_BN = [0,0,0]
OutputStateData.omega_BN_B = [0,0,0]
OutputStateData.TotalAccumDVBdy = [0,0,0]
OutputStateData.MRPSwitchCount = 0
trueVector = dict()
print(testCase)
if testCase == 'basic':
# this test verifies basic input and output
simStopTime = 0.5
sigma = np.array([-0.390614710591786, -0.503642740963740, 0.462959869561285])
OutputStateData.sigma_BN = sigma
trueVector['qInrtl2Case'] = listStack(rbk.MRP2EP(sigma),simStopTime,unitProcRate)
trueVector['timeTag'] = np.arange(0,0+simStopTime*1E9,unitProcRate_s*1E9)
elif testCase == 'noise':
simStopTime = 1000.
noiseStd = 0.1
stdCorrectionFactor = 1.5 # this needs to be used because of the Gauss Markov module. need to fix the GM module
setRandomWalk(StarTracker, noiseStd*stdCorrectionFactor, [[1.0e-13],[1.0e-13],[1.0e-13]])
sigma = np.array([0,0,0])
OutputStateData.sigma_BN = sigma
trueVector['qInrtl2Case'] = [noiseStd] * 3
trueVector['timeTag'] = np.arange(0,0+simStopTime*1E9,unitProcRate_s*1E9)
elif testCase == 'walk bounds':
# this test checks the walk bounds of random walk
simStopTime = 1000.
noiseStd = 0.01
stdCorrectionFactor = 1.5 # this needs to be used because of the Gauss Markov module. need to fix the GM module
walkBound = 0.1
setRandomWalk(StarTracker, noiseStd*stdCorrectionFactor, [[walkBound],[walkBound],[walkBound]])
sigma = np.array([0,0,0])
OutputStateData.sigma_BN = sigma
trueVector['qInrtl2Case'] = [walkBound + noiseStd*3] * 3
trueVector['timeTag'] = np.arange(0,0+simStopTime*1E9,unitProcRate_s*1E9)
else:
raise Exception('invalid test case')
# add module to the task
unitSim.AddModelToTask(unitTaskName, StarTracker)
# log module output message
dataLog = StarTracker.sensorOutMsg.recorder()
unitSim.AddModelToTask(unitTaskName, dataLog)
# configure spacecraft state message
scMsg = messaging.SCStatesMsg().write(OutputStateData)
StarTracker.scStateInMsg.subscribeTo(scMsg)
unitSim.InitializeSimulation()
unitSim.ConfigureStopTime(macros.sec2nano(simStopTime))
unitSim.ExecuteSimulation()
# pull message log data and assemble into dict
moduleOutput = dataLog.qInrtl2Case
# convert quaternion output to prv
moduleOutput2 = np.zeros([int(simStopTime/unitProcRate_s)+1, 3])
for i in range(0, int(simStopTime/unitProcRate_s)+1):
moduleOutput2[i] = rbk.EP2PRV(moduleOutput[i])
if not 'accuracy' in vars():
accuracy = 1e-6
if testCase == 'noise':
for i in range(0,3):
if np.abs(np.mean(moduleOutput2[:,i])) > 0.01 \
or np.abs(np.std(moduleOutput2[:,i]) - trueVector['qInrtl2Case'][i]) > 0.01 :
testFail = True
break
elif testCase == 'walk bounds':
for i in range(0,3):
print(np.max(np.abs(np.asarray(moduleOutput2[i]))))
if np.max(np.abs(np.asarray(moduleOutput2[i]))) > trueVector['qInrtl2Case'][i]:
testFail = True
break
else:
for i in range(0,len(trueVector['qInrtl2Case'])):
if not unitTestSupport.isArrayEqual(moduleOutput[i], trueVector['qInrtl2Case'][i], 3, accuracy):
testFail = True
break
if testFail:
testFailCount += 1
testMessages.append("FAILED: " + StarTracker.ModelTag + " Module failed unit test")
np.set_printoptions(precision=16)
# print out success message if no error were found
if testFailCount == 0:
print("PASSED ")
else:
print(testMessages)
# each test method requires a single assert method to be called
# this check below just makes sure no sub-test failures were found
return [testFailCount, ''.join(testMessages)]
# This statement below ensures that the unit test script can be run as a
# stand-along python script
if __name__ == "__main__":
test_unitSimStarTracker(
False, # show_plots
False, # useFlag
'walk bounds' # testCase
)