#
# ISC License
#
# Copyright (c) 2021, Autonomous Vehicle Systems Lab, University of Colorado 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.
#
#
import numpy as np
from Basilisk.architecture import messaging
from Basilisk.fswAlgorithms import forceTorqueThrForceMapping
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import fswSetupThrusters
from Basilisk.utilities import macros
from Basilisk.utilities import unitTestSupport
[docs]
def test_forceTorqueThrForceMapping1():
r"""
**Test Description**
This pytest ensures that the forceTorqueThrForce module can compute a valid solution for cases where:
1. There is a direction where no thrusters point - ensures matrix invertibility is handled
"""
# Test 1 - No thrusters pointing in one direction, CoM offset
rcsLocationData = [[-0.86360, -0.82550, 1.79070],
[-0.82550, -0.86360, 1.79070],
[0.82550, 0.86360, 1.79070],
[0.86360, 0.82550, 1.79070],
[-0.86360, -0.82550, -1.79070],
[-0.82550, -0.86360, -1.79070],
[0.82550, 0.86360, -1.79070],
[0.86360, 0.82550, -1.79070]]
rcsDirectionData = [[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0]]
requested_torque = [0.4, 0.2, 0.4]
requested_force = [0.9, 1.1, 0.]
CoM_B = [0.1, 0.1, 0.1]
truth = np.array([[0.7082, 0.5500, 0.0810, 0.1772, 0.6272, 0.6310, 0., 0.2582]])
[testResults, testMessage] = forceTorqueThrForceMappingTestFunction(rcsLocationData, rcsDirectionData,
requested_torque, requested_force, CoM_B,
truth, True)
assert testResults < 1, testMessage
[docs]
def test_forceTorqueThrForceMapping2():
r"""
**Test Description**
This pytest ensures that the forceTorqueThrForce module can compute a valid solution for the case
where there is zero requested torque in a connected input message, but a requested non-zero force
"""
# Test 1 - No thrusters pointing in one direction, CoM offset
rcsLocationData = [[-0.86360, -0.82550, 1.79070],
[-0.82550, -0.86360, 1.79070],
[0.82550, 0.86360, 1.79070],
[0.86360, 0.82550, 1.79070],
[-0.86360, -0.82550, -1.79070],
[-0.82550, -0.86360, -1.79070],
[0.82550, 0.86360, -1.79070],
[0.86360, 0.82550, -1.79070]]
rcsDirectionData = [[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0]]
requested_force = [0.9, 1.1, 0.]
CoM_B = [0.1, 0.1, 0.1]
requested_torque = [0.0, 0.0, 0.0]
truth = np.array([[0.5340, 0.5807, 0., 0.0588, 0.5088, 0.5500, 0.0307, 0.0840]])
[testResults, testMessage] = forceTorqueThrForceMappingTestFunction(rcsLocationData, rcsDirectionData,
requested_torque, requested_force, CoM_B,
truth, True)
assert testResults < 1, testMessage
[docs]
def test_forceTorqueThrForceMapping3():
r"""
**Test Description**
This pytest ensures that the forceTorqueThrForce module can compute a valid solution for the case
where there is no torque input message, but a requested non-zero force
"""
# Test 1 - No thrusters pointing in one direction, CoM offset
rcsLocationData = [[-0.86360, -0.82550, 1.79070],
[-0.82550, -0.86360, 1.79070],
[0.82550, 0.86360, 1.79070],
[0.86360, 0.82550, 1.79070],
[-0.86360, -0.82550, -1.79070],
[-0.82550, -0.86360, -1.79070],
[0.82550, 0.86360, -1.79070],
[0.86360, 0.82550, -1.79070]]
rcsDirectionData = [[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0]]
requested_force = [0.9, 1.1, 0.]
CoM_B = [0.1, 0.1, 0.1]
requested_torque = [0.0, 0.0, 0.0]
truth = np.array([[0.5340, 0.5807, 0., 0.0588, 0.5088, 0.5500, 0.0307, 0.0840]])
[testResults, testMessage] = forceTorqueThrForceMappingTestFunction(rcsLocationData, rcsDirectionData,
requested_torque, requested_force, CoM_B,
truth, False)
assert testResults < 1, testMessage
[docs]
def test_forceTorqueThrForceMapping4():
r"""
**Test Description**
This pytest ensures that the forceTorqueThrForce module can compute a valid solution for the case where
Thrusters point in each direction
"""
rcsLocationData = [[-1, -1, 1],
[-1, -1, 1],
[-1, -1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, -1],
[1, 1, -1],
[1, 1, -1],
[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1]]
rcsDirectionData = [[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, -1.0],
[0.0, 0.0, -1.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]]
CoM_B = [0.1, 0.1, 0.1]
requested_torque = [0.0, 0.0, 0.0]
requested_force = [0.9, 1.1, 1.]
truth = np.array([[0.5050, 0.5550, 0.0300, 0.0300, 0., 0.0600, 0.0050, 0.0550, 0.5300, 0.5100, 0.5500, 0.5300]])
[testResults, testMessage] = forceTorqueThrForceMappingTestFunction(rcsLocationData, rcsDirectionData,
requested_torque, requested_force, CoM_B,
truth, True)
assert testResults < 1, testMessage
[docs]
def forceTorqueThrForceMappingTestFunction(rcsLocation, rcsDirection, requested_torque, requested_force, CoM_B,
truth, torqueInMsgFlag):
"""Test method"""
testFailCount = 0
testMessages = []
unitTaskName = "unitTask"
unitProcessName = "TestProcess"
unitTestSim = SimulationBaseClass.SimBaseClass()
testProcessRate = macros.sec2nano(0.5)
testProc = unitTestSim.CreateNewProcess(unitProcessName)
testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate))
# setup module to be tested
module = forceTorqueThrForceMapping.forceTorqueThrForceMapping()
module.ModelTag = "forceTorqueThrForceMappingTag"
unitTestSim.AddModelToTask(unitTaskName, module)
# Configure blank module input messages
cmdTorqueInMsgData = messaging.CmdTorqueBodyMsgPayload()
cmdTorqueInMsgData.torqueRequestBody = requested_torque
cmdTorqueInMsg = messaging.CmdTorqueBodyMsg().write(cmdTorqueInMsgData)
cmdForceInMsgData = messaging.CmdForceBodyMsgPayload()
cmdForceInMsgData.forceRequestBody = requested_force
cmdForceInMsg = messaging.CmdForceBodyMsg().write(cmdForceInMsgData)
numThrusters = len(rcsLocation)
maxThrust = 3.0 # N
MAX_EFF_CNT = messaging.MAX_EFF_CNT
rcsLocationData = np.zeros((MAX_EFF_CNT, 3))
rcsDirectionData = np.zeros((MAX_EFF_CNT, 3))
rcsLocationData[0:len(rcsLocation)] = rcsLocation
rcsDirectionData[0:len(rcsLocation)] = rcsDirection
fswSetupThrusters.clearSetup()
for i in range(numThrusters):
fswSetupThrusters.create(rcsLocationData[i], rcsDirectionData[i], maxThrust)
thrConfigInMsg = fswSetupThrusters.writeConfigMessage()
vehConfigInMsgData = messaging.VehicleConfigMsgPayload()
vehConfigInMsgData.CoM_B = CoM_B
vehConfigInMsg = messaging.VehicleConfigMsg().write(vehConfigInMsgData)
# subscribe input messages to module
if torqueInMsgFlag:
module.cmdTorqueInMsg.subscribeTo(cmdTorqueInMsg)
module.cmdForceInMsg.subscribeTo(cmdForceInMsg)
module.thrConfigInMsg.subscribeTo(thrConfigInMsg)
module.vehConfigInMsg.subscribeTo(vehConfigInMsg)
unitTestSim.InitializeSimulation()
unitTestSim.ConfigureStopTime(macros.sec2nano(0.5))
unitTestSim.ExecuteSimulation()
testFailCount, testMessages = unitTestSupport.compareArray(truth, np.array([module.thrForceCmdOutMsg.read().thrForce[0:len(rcsLocation)]]), 1e-3,
"CompareForces", testFailCount, testMessages)
if testFailCount == 0:
print("PASSED: " + module.ModelTag)
else:
print(testMessages)
return [testFailCount, "".join(testMessages)]
if __name__ == "__main__":
test_forceTorqueThrForceMapping1()