#
#  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:        meanOEFeedback
#   Author:             Hirotaka Kondo
#   Creation Date:      March 27, 2020
#
import pytest
# 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.utilities import orbitalMotion
from Basilisk.fswAlgorithms.meanOEFeedback import meanOEFeedback  # import the module that is to be tested
from Basilisk.utilities import macros
# 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
# @pytest.mark.xfail(conditionstring)
# provide a unique test method name, starting with test_
[docs]@pytest.mark.parametrize("useClassicElem", [True, False])
@pytest.mark.parametrize("accuracy", [1e-9])
def test_meanOEFeedback(show_plots, useClassicElem, accuracy):
    """Module Unit Test"""
    # each test method requires a single assert method to be called
    [testResults, testMessage] = meanOEFeedbackTestFunction(show_plots, useClassicElem, accuracy)
    assert testResults < 1, testMessage 
def meanOEFeedbackTestFunction(show_plots, useClassicElem, accuracy):
    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 meanOEFeedback as an empty container
    unitTestSim = SimulationBaseClass.SimBaseClass()
    # Create test thread
    testProcessRate = macros.sec2nano(0.1)  # process rate
    testProc = unitTestSim.CreateNewProcess(unitProcessName)  # create new process
    testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate))  # create new task
    # Construct algorithm and associated C++ container
    moduleConfig = meanOEFeedback.meanOEFeedbackConfig()  # update with current values
    moduleWrap = unitTestSim.setModelDataWrap(moduleConfig)
    moduleWrap.ModelTag = "meanOEFeedback"  # update python name of test meanOEFeedback
    moduleConfig.chiefTransInMsgName = "chiefInputMsg"
    moduleConfig.deputyTransInMsgName = "deputyInputMsg"
    moduleConfig.forceOutMsgName = "forceOutputMsg"
    moduleConfig.targetDiffOeMean = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
    moduleConfig.mu = orbitalMotion.MU_EARTH * 1e9  # [m^3/s^2]
    moduleConfig.req = orbitalMotion.REQ_EARTH * 1e3  # [m]
    moduleConfig.J2 = orbitalMotion.J2_EARTH      # []
    moduleConfig.K = [1e7, 0.0, 0.0, 0.0, 0.0, 0.0,
                      0.0, 1e7, 0.0, 0.0, 0.0, 0.0,
                      0.0, 0.0, 1e7, 0.0, 0.0, 0.0,
                      0.0, 0.0, 0.0, 1e7, 0.0, 0.0,
                      0.0, 0.0, 0.0, 0.0, 1e7, 0.0,
                      0.0, 0.0, 0.0, 0.0, 0.0, 1e7]
    if(useClassicElem):
        moduleConfig.oeType = 0  # 0: classic
    else:
        moduleConfig.oeType = 1  # 1: equinoctial
    # Add test meanOEFeedback to runtime call list
    unitTestSim.AddModelToTask(unitTaskName, moduleWrap, moduleConfig)
    # Create input message and size it because the regular creator of that message
    # is not part of the test.
    #
    # Chief Navigation Message
    #
    oe = orbitalMotion.ClassicElements()
    oe.a = 20000e3  # [m]
    oe.e = 0.1
    oe.i = 0.2
    oe.Omega = 0.3
    oe.omega = 0.4
    oe.f = 0.5
    (r_BN_N, v_BN_N) = orbitalMotion.elem2rv(orbitalMotion.MU_EARTH*1e9, oe)
    chiefNavStateOutData = meanOEFeedback.NavTransIntMsg()  # Create a structure for the input message
    chiefNavStateOutData.timeTag = 0
    chiefNavStateOutData.r_BN_N = r_BN_N
    chiefNavStateOutData.v_BN_N = v_BN_N
    chiefNavStateOutData.vehAccumDV = [0, 0, 0]
    unitTestSupport.setMessage(unitTestSim.TotalSim, unitProcessName,
                               moduleConfig.chiefTransInMsgName,
                               chiefNavStateOutData)
    #
    # Deputy Navigation Message
    #
    oe2 = orbitalMotion.ClassicElements()
    oe2.a = (1 + 0.0006) * 7000e3  # [m]
    oe2.e = 0.2 + 0.0005
    oe2.i = 0.0 + 0.0004
    oe2.Omega = 0.0 + 0.0003
    oe2.omega = 0.0 + 0.0002
    oe2.f = 0.0001
    (r_BN_N2, v_BN_N2) = orbitalMotion.elem2rv(orbitalMotion.MU_EARTH*1e9, oe2)
    deputyNavStateOutData = meanOEFeedback.NavTransIntMsg()  # Create a structure for the input message
    deputyNavStateOutData.timeTag = 0
    deputyNavStateOutData.r_BN_N = r_BN_N2
    deputyNavStateOutData.v_BN_N = v_BN_N2
    deputyNavStateOutData.vehAccumDV = [0, 0, 0]
    unitTestSupport.setMessage(unitTestSim.TotalSim, unitProcessName,
                               moduleConfig.deputyTransInMsgName,
                               deputyNavStateOutData)
    # Setup logging on the test meanOEFeedback output message so that we get all the writes to it
    unitTestSim.TotalSim.logThisMessage(moduleConfig.forceOutMsgName, testProcessRate)
    # Need to call the self-init and cross-init methods
    unitTestSim.InitializeSimulation()
    # Set the simulation time.
    # NOTE: the total simulation time may be longer than this value. The
    # simulation is stopped at the next logging event on or after the
    # simulation end time.
    unitTestSim.ConfigureStopTime(testProcessRate)  # seconds to stop simulation
    # Begin the simulation time run set above
    unitTestSim.ExecuteSimulation()
    # This pulls the actual data log from the simulation run.
    # Note that range(3) will provide [0, 1, 2]  Those are the elements you get from the vector (all of them)
    forceOutput = unitTestSim.pullMessageLogData(
        moduleConfig.forceOutMsgName + ".forceRequestInertial", list(range(3)))
    # set the filtered output truth states
    if useClassicElem:
        trueVector = [[-849.57347406544340628897771239280701,
                       1849.77641265032843875815160572528839,
                       136.07817734479317550722043961286545]]
    else:
        trueVector = [[-1655.37188207880308254971168935298920,
                       1788.61776379042521512019447982311249,
                       52.54814237453938119415397522971034]]
    # compare the meanOEFeedback results to the truth values
    for i in range(0, len(trueVector)):
        # check a vector values
        if not unitTestSupport.isArrayEqual(forceOutput[i], trueVector[i], 3, accuracy):
            testFailCount += 1
            testMessages.append("FAILED: " + moduleWrap.ModelTag + " Module failed "
                                + moduleConfig.forceOutMsgName
                                + ".forceRequestInertial" + " unit test at t="
                                + str(forceOutput[i, 0]*macros.NANO2SEC) + "sec\n")
    #   print out success message if no error were found
    if testFailCount == 0:
        print("PASSED: " + moduleWrap.ModelTag)
        print("This test uses an accuracy value of " + str(accuracy))
    # 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 unitTestScript can be run as a
# stand-along python script
#
if __name__ == "__main__":
    test_meanOEFeedback(
        False,  # show_plots
        True,  # useClassicElem
        1e-9    # accuracy
    )