#
# 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.
#
#
# Unit Test Script
# Module Name: Magnetometer - TAM
# Author: Demet Cilden-Guler
# Creation Date: September 25, 2019
#
import inspect
import os
import numpy as np
import pytest
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
bskPath = path.split('src')[0]
# Import all of the modules that we are going to be called in this simulation
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import unitTestSupport # general support file with common unit test functions
from Basilisk.simulation import magnetometer
from Basilisk.architecture import messaging
from Basilisk.utilities import macros
from Basilisk.utilities import RigidBodyKinematics as rbk
[docs]
@pytest.mark.parametrize("useNoiseStd, errTol", [(False, 1e-10), (True, 1e-2)])
@pytest.mark.parametrize("useBias", [True, False])
@pytest.mark.parametrize("useMinOut, useMaxOut", [(True, True), (False, False)])
@pytest.mark.parametrize("useScaleFactor", [True, False])
# update "module" in this function name to reflect the module name
def test_module(show_plots, useNoiseStd, useBias, useMinOut, useMaxOut, useScaleFactor, errTol):
"""
**Validation Test Description**
This section describes the specific unit tests conducted on this module.
The test contains 16 tests and is located at ``test_magnetometer.py``.
The success criteria is to match the outputs with the generated truth.
Args:
useNoiseStd (string): Defines if the standard deviation of the magnetometer measurements is used for this
parameterized unit test
useBias (string): Defines if the bias on the magnetometer measurements is used for this parameterized unit test
useMinOut (string): Defines if the minimum bound for the measurement saturation is used for this
parameterized unit test
useMaxOut (string): Defines if the maximum bound for the measurement saturation is used for this
parameterized unit test
useScaleFactor (string): Defines if the scaling on the measurement is used for this parameterized unit test
errTol (double): Defines the error tolerance for this parameterized unit test
**Description of Variables Being Tested**
In this file, we are checking the values of the variable:
``tamData[3]``
which is 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] = run(show_plots, useNoiseStd, useBias, useMinOut, useMaxOut, useScaleFactor, errTol)
assert testResults < 1, testMessage
def run(show_plots, useNoiseStd, useBias, useMinOut, useMaxOut, useScaleFactor, errTol):
testFailCount = 0 # zero unit test result counter
testMessages = [] # create empty array to store test log messages
unitTaskName = "unitTask" # arbitrary name (don't change)
unitProcessName = "TestProcess" # arbitrary name (don't change)
# Create a sim module as an empty container
unitTestSim = SimulationBaseClass.SimBaseClass()
# Create test thread
testProcessRate_s = 0.01
testProcessRate = macros.sec2nano(testProcessRate_s) # update process rate update time
testProc = unitTestSim.CreateNewProcess(unitProcessName)
testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate))
# Construct algorithm and associated C++ container
testModule = magnetometer.Magnetometer()
testModule.ModelTag = "TAM_sensor"
NoiseStd = [3e-9, 3e-9, 3e-9] # Tesla
bias = [1e-6, 1e-6, 1e-5] # Tesla
minOut = -1e-4 # Tesla
maxOut = 1e-4 # Tesla
if useNoiseStd:
testModule.senNoiseStd = NoiseStd
if useBias:
testModule.senBias = bias
if useScaleFactor:
testModule.scaleFactor = 2
if useMinOut & useMaxOut:
testModule.minOutput = minOut
testModule.maxOutput = maxOut
# Add module to the task
unitTestSim.AddModelToTask(unitTaskName, testModule)
# Set-up fake magnetic field
magFieldMsg = messaging.MagneticFieldMsgPayload()
trueMagField = [1e-5, 2e-5, 1.5e-5] # [T] true magnetic field outputs in inertial frame
magFieldMsg.magField_N = trueMagField
magMsg = messaging.MagneticFieldMsg().write(magFieldMsg)
testModule.magInMsg.subscribeTo(magMsg)
# Set-up fake attitude
satelliteStateMsg = messaging.SCStatesMsgPayload()
angles = np.linspace(0., 2 * np.pi, 59000)
sigmas = np.zeros(len(angles))
for i in range(len(sigmas)): # convert rotation angle about 3rd axis to MRP
sigmas[i] = np.tan(angles[i] / 4.) # This is iterated through in the execution for loop
satelliteStateMsg.sigma_BN = [0.3, 0.2, sigmas[i]]
scMsg = messaging.SCStatesMsg().write(satelliteStateMsg)
testModule.stateInMsg.subscribeTo(scMsg)
dcm_BN = rbk.MRP2C(satelliteStateMsg.sigma_BN)
# Sensor set-up
yaw = 0.7854 # [rad]
pitch = 1.0 # [rad]
roll = 0.1 # [rad]
dcm_SB_py = rbk.euler3212C([yaw, pitch, roll]) # for checking the dcm_SB
dcm_SB = testModule.setBodyToSensorDCM(yaw, pitch, roll)
dcm_SN = np.dot(dcm_SB, dcm_BN)
trueTam_S = np.dot(dcm_SN, trueMagField)
if useBias:
trueTam_S += bias # Tesla
if useScaleFactor:
trueTam_S *= 2
for i in range(len(trueTam_S)):
if useMinOut & useMaxOut:
if trueTam_S[i] < minOut:
trueTam_S[i] = minOut
if trueTam_S[i] > maxOut:
trueTam_S[i] = maxOut
# Setup logging on the test module output message so that we get all the writes to it
dataLog = testModule.tamDataOutMsg.recorder()
unitTestSim.AddModelToTask(unitTaskName, dataLog)
# Need to call the self-init and cross-init methods
unitTestSim.InitializeSimulation()
unitTestSim.TotalSim.SingleStepProcesses()
# This pulls the actual data log from the simulation run.
tamData = dataLog.tam_S
print(tamData)
print(trueTam_S)
if not unitTestSupport.isArrayEqualRelative(tamData[0], trueTam_S, 3, errTol):
testFailCount += 1
# print out success or failure message
if testFailCount == 0:
print("PASSED: " + testModule.ModelTag)
else:
print("Failed: " + testModule.ModelTag)
print("This test uses a relative accuracy value of " + str(errTol*100) + " percent")
return [testFailCount, ''.join(testMessages)]
#
# This statement below ensures that the unitTestScript can be run as a
# stand-along python script
#
if __name__ == "__main__":
test_module( # update "module" in function name
False, # show_plots
True, # useNoiseStd
True, # useBias
True, # useMinOut
True, # useMaxOut
True, # useScaleFactor
1e-2 # errTol
)