#
#  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 pytest
import os, inspect
import numpy as np
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.simulation import simMessages
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()
    # terminateSimulation() is needed if multiple unit test scripts are run
    # that run a simulation for the test. This creates a fresh and
    # consistent simulation environment for each test run.
    # 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"
    testModule.tamDataOutMsgName = "TAM_output"
    NoiseStd = 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
    testModule.magIntMsgName = "True magnetic field"
    magFieldMsg = simMessages.MagneticFieldSimMsg()
    trueMagField = [1e-5, 2e-5, 1.5e-5]  # [T] true magnetic field outputs in inertial frame
    magFieldMsg.magField_N = trueMagField
    unitTestSupport.setMessage(unitTestSim.TotalSim,
                               unitProcessName,
                               testModule.magIntMsgName,
                               magFieldMsg)
    # Set-up fake attitude
    satelliteStateMsg = simMessages.SCPlusStatesSimMsg()
    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]]
    unitTestSupport.setMessage(unitTestSim.TotalSim,
                               unitProcessName,
                               testModule.stateIntMsgName,
                               satelliteStateMsg)
    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
    unitTestSim.TotalSim.logThisMessage(testModule.tamDataOutMsgName, testProcessRate)
    # 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 = unitTestSim.pullMessageLogData(testModule.tamDataOutMsgName + ".OutputData", list(range(3)))
    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
               )