Skip to content

Commit 9ee8bca

Browse files
committed
[edit] adding unit and integrated testing
1 parent 4429ee3 commit 9ee8bca

File tree

4 files changed

+162
-6
lines changed

4 files changed

+162
-6
lines changed

examples/scenarioConstrainedDynamics.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
# Basilisk imports
6969
from Basilisk.architecture import messaging
7070
from Basilisk.utilities import (SimulationBaseClass, orbitalMotion, macros, RigidBodyKinematics)
71-
from Basilisk.simulation import (spacecraft, constraintDynamicEffector, gravityEffector, svIntegrators)
71+
from Basilisk.simulation import (spacecraft, constraintDynamicEffector, gravityEffector, svIntegrators, linearTranslationNDOFStateEffector, prescribedLinearTranslation)
7272
import matplotlib.pyplot as plt
7373

7474
# Utility imports
@@ -120,6 +120,9 @@ def run(show_plots, env):
120120
scObject2.hub.r_BcB_B = [[0.0], [0.0], [1.0]]
121121
scObject2.hub.IHubPntBc_B = [[600.0, 0.0, 0.0], [0.0, 600.0, 0.0], [0.0, 0.0, 600.0]]
122122

123+
# Define the spacecraft's properties
124+
scGeometry = geometryClass()
125+
123126
# Add Earth gravity to the simulation if requested
124127
if env == 'Gravity':
125128
earthGravBody = gravityEffector.GravBodyData()
@@ -174,6 +177,58 @@ def run(show_plots, env):
174177
scObject2.hub.v_CN_NInit = rDot_B2N_N_0
175178
scObject2.hub.omega_BN_BInit = omega_B2N_B2_0
176179

180+
# Set up translating body
181+
translatingBodyEffector = linearTranslationNDOFStateEffector.linearTranslationNDOFStateEffector()
182+
translatingBodyEffector.ModelTag = "translatingBodyEffector"
183+
scObject1.addStateEffector(translatingBodyEffector)
184+
scSim.AddModelToTask(simTaskName, translatingBodyEffector)
185+
186+
translatingBody1 = linearTranslationNDOFStateEffector.translatingBody()
187+
translatingBody1.setMass(100)
188+
translatingBody1.setIPntFc_F([[translatingBody1.getMass() / 12 * (3 * (scGeometry.diameterArm / 2) ** 2 + scGeometry.heightArm ** 2), 0.0, 0.0],
189+
[0.0, translatingBody1.getMass() / 12 * (scGeometry.diameterArm / 2) ** 2, 0.0],
190+
[0.0, 0.0, translatingBody1.getMass() / 12 * (3 * (scGeometry.diameterArm / 2) ** 2 + scGeometry.heightArm ** 2)]])
191+
translatingBody1.setDCM_FP([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
192+
translatingBody1.setR_FcF_F([[0.0], [scGeometry.heightArm / 2], [0.0]])
193+
translatingBody1.setR_F0P_P([[0], [scGeometry.lengthHub / 2], [0]])
194+
translatingBody1.setFHat_P([[0], [1], [0]])
195+
translatingBody1.setRhoInit(0.0)
196+
translatingBody1.setRhoDotInit(0.0)
197+
translatingBody1.setC(400.0)
198+
translatingBody1.setK(100.0)
199+
translatingBodyEffector.addTranslatingBody(translatingBody1)
200+
201+
translatingBody2 = linearTranslationNDOFStateEffector.translatingBody()
202+
translatingBody2.setMass(100)
203+
translatingBody2.setIPntFc_F([[translatingBody2.getMass() / 12 * (3 * (scGeometry.diameterArm / 2) ** 2 + scGeometry.heightArm ** 2), 0.0, 0.0],
204+
[0.0, translatingBody2.getMass() / 12 * (scGeometry.diameterArm / 2) ** 2, 0.0],
205+
[0.0, 0.0, translatingBody2.getMass() / 12 * (
206+
3 * (scGeometry.diameterArm / 2) ** 2 + scGeometry.heightArm ** 2)]])
207+
translatingBody2.setDCM_FP([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
208+
translatingBody2.setR_FcF_F([[0.0], [scGeometry.heightArm / 2], [0.0]])
209+
translatingBody2.setR_F0P_P([[0], [0], [0]])
210+
translatingBody2.setFHat_P([[0], [1], [0]])
211+
translatingBody2.setRhoInit(0.0)
212+
translatingBody2.setRhoDotInit(0.0)
213+
translatingBody2.setC(400.0)
214+
translatingBody2.setK(100.0)
215+
translatingBodyEffector.addTranslatingBody(translatingBody2)
216+
217+
profiler2 = prescribedLinearTranslation.PrescribedLinearTranslation()
218+
profiler2.ModelTag = "profiler"
219+
profiler2.setTransAccelMax(0.0005)
220+
profiler2.setTransPosInit(translatingBody2.getRhoInit())
221+
profiler2.setSmoothingDuration(10)
222+
scSim.AddModelToTask(simTaskName, profiler2)
223+
translatingBodyEffector.translatingBodyRefInMsgs[1].subscribeTo(profiler2.linearTranslationRigidBodyOutMsg)
224+
225+
translatingRigidBodyMsgData = messaging.LinearTranslationRigidBodyMsgPayload()
226+
translatingRigidBodyMsgData.rho = scGeometry.heightArm # [m]
227+
translatingRigidBodyMsgData.rhoDot = 0 # [m/s]
228+
translatingRigidBodyMsg2 = messaging.LinearTranslationRigidBodyMsg().write(translatingRigidBodyMsgData)
229+
translatingRigidBodyMsg2.this.disown()
230+
profiler2.linearTranslationRigidBodyInMsg.subscribeTo(translatingRigidBodyMsg2)
231+
177232
# Create the constraint effector module
178233
constraintEffector = constraintDynamicEffector.ConstraintDynamicEffector()
179234
# Set up the constraint effector physical parameters
@@ -185,7 +240,7 @@ def run(show_plots, env):
185240
constraintEffector.ModelTag = "constraintEffector"
186241

187242
# Add the constraint to both spacecraft
188-
scObject1.addDynamicEffector(constraintEffector)
243+
translatingBody2.addDynamicEffector(constraintEffector)
189244
scObject2.addDynamicEffector(constraintEffector)
190245

191246
# Add the modules to runtime call list
@@ -265,6 +320,15 @@ def run(show_plots, env):
265320

266321
return figureList
267322

323+
class geometryClass:
324+
massHub = 400
325+
lengthHub = 3
326+
widthHub = 3
327+
heightHub = 6
328+
massArm = 50
329+
heightArm = 3
330+
diameterArm = 0.6
331+
268332
if __name__ == "__main__":
269333
run(
270334
True, # show_plots: True or False

examples/scenarioExtendingBoom.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def run(show_plots):
260260
["arm3", translatingBodyEffector.translatingBodyConfigLogOutMsgs[2]],
261261
["arm4", translatingBodyEffector.translatingBodyConfigLogOutMsgs[3]]]
262262
viz = vizSupport.enableUnityVisualization(scSim, dynTaskName, scBodyList
263-
# , saveFile=fileName
263+
, saveFile=fileName
264264
)
265265
vizSupport.createCustomModel(viz
266266
, simBodiesToModify=[scObject.ModelTag]

src/simulation/dynamics/Thrusters/thrusterDynamicEffector/_UnitTest/test_thruster_dynamics_attached_body.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ def thrusterEffectorAllTests(show_plots):
4646
# provide a unique test method name, starting with test_
4747
def test_unitThrusters(show_plots, long_angle, lat_angle, location, rate):
4848
r"""
49-
This unit test checks the functionality of attaching a dynamic thruster to a body other than the hub. Although the
50-
attached body is fixed with respect to the hub, the point where the thruster is attached now has an additional
51-
offset and a different orientation.
49+
This unit test checks the functionality of attaching a dynamic thruster to a body other than the hub using the
50+
messaging system. Although the attached body is fixed with respect to the hub, the point where the thruster is
51+
attached now has an additional offset and a different orientation.
5252
5353
The unit test sets up the thruster as normal, but then converts the direction and location to take into account the
5454
attached body for testing purposes. The thruster is set to fire for the first half of the simulation, and then turn
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ISC License
2+
#
3+
# Copyright (c) 2024, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
4+
#
5+
# Permission to use, copy, modify, and/or distribute this software for any
6+
# purpose with or without fee is hereby granted, provided that the above
7+
# copyright notice and this permission notice appear in all copies.
8+
#
9+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16+
17+
18+
#
19+
# Unit Test Script
20+
# Module Name: spinningBody2TwoDOF
21+
# Author: Andrew Morell
22+
# Creation Date: December 18, 2024
23+
#
24+
25+
import inspect
26+
import os
27+
import pytest
28+
import numpy
29+
import matplotlib.pyplot as plt
30+
31+
filename = inspect.getframeinfo(inspect.currentframe()).filename
32+
path = os.path.dirname(os.path.abspath(filename))
33+
splitPath = path.split('simulation')
34+
35+
from Basilisk.utilities import SimulationBaseClass, unitTestSupport, macros
36+
from Basilisk.simulation import spacecraft, spinningBodyTwoDOFStateEffector, gravityEffector
37+
from Basilisk.architecture import messaging
38+
39+
40+
# uncomment this line is this test is to be skipped in the global unit test run, adjust message as needed
41+
# @pytest.mark.skipif(conditionstring)
42+
# uncomment this line if this test has an expected failure, adjust message as needed
43+
# @pytest.mark.xfail() # need to update how the RW states are defined
44+
# provide a unique test method name, starting with test_
45+
46+
@pytest.mark.parametrize("cmdTorque1, lock1, theta1Ref, cmdTorque2, lock2, theta2Ref", [
47+
(0.0, False, 0.0, 0.0, False, 0.0)
48+
, (0.0, True, 0.0, 0.0, False, 0.0)
49+
, (0.0, False, 0.0, 0.0, True, 0.0)
50+
, (0.0, True, 0.0, 0.0, True, 0.0)
51+
, (1.0, False, 0.0, -2.0, False, 0.0)
52+
, (0.0, False, 10.0 * macros.D2R, 0.0, False, -5.0 * macros.D2R)
53+
, (0.0, False, -5.0 * macros.D2R, 0.0, False, 10.0 * macros.D2R)
54+
])
55+
def test_spinningBody(show_plots, cmdTorque1, lock1, theta1Ref, cmdTorque2, lock2, theta2Ref):
56+
r"""
57+
**Validation Test Description**
58+
This integrated test checks the functionality of attaching a dynamic effector onto a spinningBodyTwoDOF such that
59+
spinningBodyTwoDOF is the dynamical parent rather than the hub. Note that this can also be done in some cases using
60+
the messaging system (see test_thruster_dynamics_attached_body.py) but is done here using the dynParamManager to
61+
achieve time varying motion with respect to the hub, two-way dynamical coupling, and sub-integration timestep
62+
resolution.
63+
64+
The integrated test sets up an extForceTorque dynamic effector as normal, but then converts the direction and
65+
location to take into account being in the spinningBodyTwoDOF local body. The extForceTorque effector is set to
66+
apply a force and torque for the first half of the simulation, and then turn off.
67+
68+
69+
70+
71+
As with the other tests, the expected forces and torques are compared with the values from the module to check that
72+
everything matches accordingly.
73+
74+
This unit test sets up a spacecraft with a single-axis rotating rigid body attached to a rigid hub. The spinning
75+
body's center of mass is off-center from the spinning axis and the position of the axis is arbitrary. The scenario
76+
includes gravity acting on both the spacecraft and the effector.
77+
78+
**Description of Variables Being Tested**
79+
80+
In this file we are checking the principles of conservation of energy and angular momentum. Both the orbital and
81+
rotational energy and angular momentum must be maintained when conservative forces like gravity are present.
82+
Therefore, the values of the variables
83+
84+
- ``finalOrbAngMom``
85+
- ``finalOrbEnergy``
86+
- ``finalRotAngMom``
87+
- ``finalRotEnergy``
88+
89+
against their initial values.
90+
"""
91+
[testResults, testMessage] = spinningBody(show_plots, cmdTorque1, lock1, theta1Ref, cmdTorque2, lock2, theta2Ref)
92+
assert testResults < 1, testMessage

0 commit comments

Comments
 (0)