Skip to content

Commit 0392057

Browse files
authored
Merge pull request #333 from lasp/feature/1134-protective-logic-flyby
Feature/1134 protective logic flyby
2 parents 150e7c6 + 94fa0d1 commit 0392057

File tree

4 files changed

+278
-196
lines changed

4 files changed

+278
-196
lines changed

src/fswAlgorithms/attGuidance/flybyPoint/_UnitTest/test_flybyPoint.py

+109-141
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,21 @@
3030
from Basilisk import __path__
3131
from Basilisk.architecture import messaging
3232
from Basilisk.fswAlgorithms import flybyPoint
33-
from Basilisk.utilities import SimulationBaseClass, macros, unitTestSupport
3433
from Basilisk.utilities import RigidBodyKinematics as rbk
34+
from Basilisk.utilities import SimulationBaseClass, macros, unitTestSupport
3535

3636
bskPath = __path__[0]
3737
fileName = os.path.basename(os.path.splitext(__file__)[0])
3838

3939

40-
@pytest.mark.parametrize("initPos", [[-5e7, 7.5e6, 5e5]]) # m - r_CN_N
41-
@pytest.mark.parametrize("initVel", [[2e4, 0, 0]]) # m/s - v_CN_N
42-
@pytest.mark.parametrize("dTsim", [10, 100]) # s
43-
@pytest.mark.parametrize("dTfilter", [0, 60, 600]) # s
44-
@pytest.mark.parametrize("signOrbitNormal", [1, -1])
45-
@pytest.mark.parametrize("accuracy", [1e-12])
46-
def test_flybyPoint(show_plots, initPos, initVel, dTsim, dTfilter, signOrbitNormal, accuracy):
40+
@pytest.mark.parametrize("initial_position", [[-5e7, 7.5e6, 5e5]]) # m - r_CN_N
41+
@pytest.mark.parametrize("initial_velocity", [[2e4, 0, 0]]) # m/s - v_CN_N
42+
@pytest.mark.parametrize("filter_dt", [1, 60]) # s
43+
@pytest.mark.parametrize("orbit_normal_sign", [1, -1])
44+
@pytest.mark.parametrize("max_rate", [0, 0.01])
45+
@pytest.mark.parametrize("max_acceleration", [0, 1E-7])
46+
def test_flybyPoint(show_plots, initial_position, initial_velocity, filter_dt, orbit_normal_sign, max_rate,
47+
max_acceleration):
4748
r"""
4849
**Validation Test Description**
4950
@@ -59,219 +60,186 @@ def test_flybyPoint(show_plots, initPos, initVel, dTsim, dTfilter, signOrbitNorm
5960
Correctness is tested assessing whether the computed hill frame moves according to the motion of the spacecraft.
6061
6162
Args:
62-
initPos[3] (m): initial position of the spacecraft w.r.t. the body/origin
63-
initVel[3] (m): initial velocity of the spacecraft w.r.t. the body/origin
64-
dTsim (s): simulation time step
65-
dTfilter (s): time between two consecutive reads of the input message
66-
signOrbitNormal (-): sign of the reference frame "out of plane" vector (orbit normal or anti orbit normal)
67-
accuracy: tolerance on the result.
63+
initial_position[3] (m): initial position of the spacecraft w.r.t. the body/origin
64+
initial_velocity[3] (m): initial velocity of the spacecraft w.r.t. the body/origin
65+
filter_dt (s): time between two consecutive reads of the input message
66+
orbit_normal_sign (-): sign of the reference frame "out of plane" vector (orbit normal or anti orbit normal)
6867
6968
**Description of Variables Being Tested**
7069
71-
The referene attitude :math:`\sigma_\mathcal{R/N}`, reference angular rates :math:`\omega_\mathcal{R/N}` and
70+
The reference attitude :math:`\sigma_\mathcal{R/N}`, reference angular rates :math:`\omega_\mathcal{R/N}` and
7271
angular accelerations :math:`\dot{\omega}_\mathcal{R/N}` are tested. These are compared to the analytical results
7372
expected from the rectilinear motion described in the documentation of :ref:`flybyPoint`.
7473
The reference attitude is mapped to the corresponding reference frame, and each axis of the reference frame is
7574
tested for correctness. The angular rate and acceleration vectors are tested against the analytical result,
7675
expressed in R-frame coordinates.
7776
"""
7877
# each test method requires a single assert method to be called
79-
flybyPointTestFunction(show_plots, initPos, initVel, dTsim, dTfilter, signOrbitNormal, accuracy)
80-
81-
82-
def flybyPointTestFunction(show_plots, initPos, initVel, dTsim, dTfilter, signOrbitNormal, accuracy):
83-
testFailCount = 0 # zero unit test result counter
84-
testMessages = [] # create empty array to store test log messages
85-
unitTaskName = "unitTask" # arbitrary name (don't change)
86-
unitProcessName = "TestProcess" # arbitrary name (don't change)
87-
88-
# Create a sim module as an empty container
89-
unitTestSim = SimulationBaseClass.SimBaseClass()
90-
91-
# Create test thread
92-
testProcessRate = macros.sec2nano(dTsim) # update process rate update time
93-
testProc = unitTestSim.CreateNewProcess(unitProcessName)
94-
testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate))
95-
96-
# Create simulation variable names
97-
unitTaskName = "unitTask"
98-
unitProcessName = "unitProcess"
78+
flybyPointTestFunction(show_plots, initial_position, initial_velocity, filter_dt, orbit_normal_sign,
79+
max_rate, max_acceleration)
9980

100-
# Create a sim module as an empty container
101-
unitTestSim = SimulationBaseClass.SimBaseClass()
10281

103-
# create the simulation process
104-
testProcess = unitTestSim.CreateNewProcess(unitProcessName)
105-
106-
# create the dynamics task and specify the integration update time
107-
simulationTimeStep = macros.sec2nano(dTsim)
108-
testProcess.addTask(unitTestSim.CreateNewTask(unitTaskName, simulationTimeStep))
82+
def flybyPointTestFunction(show_plots, initial_position, initial_velocity, filter_dt, orbit_normal_sign,
83+
max_rate, max_acceleration):
84+
# setup simulation environment
85+
sim_dt = 10
86+
unit_test_sim = SimulationBaseClass.SimBaseClass()
87+
process_rate = macros.sec2nano(sim_dt)
88+
test_process = unit_test_sim.CreateNewProcess("unit_process")
89+
test_process.addTask(unit_test_sim.CreateNewTask("unit_task", process_rate))
10990

11091
# setup flybyPoint guidance module
111-
flybyGuid = flybyPoint.FlybyPoint()
112-
flybyGuid.ModelTag = "flybyPoint"
113-
flybyGuid.setTimeBetweenFilterData(dTfilter)
114-
flybyGuid.setToleranceForCollinearity(1E-5)
115-
flybyGuid.setSignOfOrbitNormalFrameVector(signOrbitNormal)
116-
unitTestSim.AddModelToTask(unitTaskName, flybyGuid)
117-
118-
inputData = messaging.NavTransMsgPayload()
119-
inputData.v_BN_N = np.array(initVel)
120-
filterInMsg = messaging.NavTransMsg()
121-
flybyGuid.filterInMsg.subscribeTo(filterInMsg)
122-
123-
#
124-
# Setup data logging before the simulation is initialized
125-
#
126-
attRefLog = flybyGuid.attRefOutMsg.recorder()
127-
unitTestSim.AddModelToTask(unitTaskName, attRefLog)
128-
129-
unitTestSim.InitializeSimulation()
130-
dataPos = []
131-
dataVel = []
132-
for i in range(round(9*600/dTsim)):
133-
dataPos.append(np.array(initPos) + np.array(initVel) * (i * dTsim))
134-
dataVel.append(np.array(initVel))
135-
inputData.timeTag = macros.sec2nano(i * dTsim)
136-
inputData.r_BN_N = dataPos[i]
137-
filterInMsg.write(inputData, unitTestSim.TotalSim.getCurrentNanos())
138-
unitTestSim.ConfigureStopTime(macros.sec2nano((i + 1) * dTsim) - 1)
139-
unitTestSim.ExecuteSimulation()
140-
141-
# retrieve the logged data
142-
refAtt = attRefLog.sigma_RN
143-
refRate = attRefLog.omega_RN_N
144-
refAcc = attRefLog.domega_RN_N
145-
timeData = attRefLog.times() * macros.NANO2MIN
92+
flyby_guidance = flybyPoint.FlybyPoint()
93+
flyby_guidance.ModelTag = "flybyPoint"
94+
flyby_guidance.setTimeBetweenFilterData(filter_dt)
95+
flyby_guidance.setToleranceForCollinearity(1E-5)
96+
flyby_guidance.setSignOfOrbitNormalFrameVector(orbit_normal_sign)
97+
flyby_guidance.setMaximumRateThreshold(max_rate)
98+
flyby_guidance.setMaximumAccelerationThreshold(max_acceleration)
99+
unit_test_sim.AddModelToTask("unit_task", flyby_guidance)
100+
101+
input_data = messaging.NavTransMsgPayload()
102+
input_data.v_BN_N = np.array(initial_velocity)
103+
filter_msg = messaging.NavTransMsg()
104+
flyby_guidance.filterInMsg.subscribeTo(filter_msg)
105+
106+
# Setup data logging before the simulation is initialized
107+
attitude_reference_log = flyby_guidance.attRefOutMsg.recorder()
108+
unit_test_sim.AddModelToTask("unit_task", attitude_reference_log)
109+
110+
unit_test_sim.InitializeSimulation()
111+
position_data = []
112+
velocity_data = []
113+
for i in range(round(9 * 600 / sim_dt)):
114+
position_data.append(np.array(initial_position) + np.array(initial_velocity) * (i * sim_dt))
115+
velocity_data.append(np.array(initial_velocity))
116+
input_data.timeTag = macros.sec2nano(i * sim_dt)
117+
input_data.r_BN_N = position_data[i]
118+
filter_msg.write(input_data, unit_test_sim.TotalSim.getCurrentNanos())
119+
unit_test_sim.ConfigureStopTime(macros.sec2nano((i + 1) * sim_dt) - 1)
120+
unit_test_sim.ExecuteSimulation()
121+
122+
# retrieve the logged data
123+
reference_attitude = attitude_reference_log.sigma_RN
124+
reference_rate = attitude_reference_log.omega_RN_N
125+
reference_acceleration = attitude_reference_log.domega_RN_N
126+
time_data = attitude_reference_log.times() * macros.NANO2MIN
146127

147128
ur_output = []
148129
ut_output = []
149130
uh_output = []
150-
for i in range(len(refAtt)):
151-
RN = rbk.MRP2C(refAtt[i])
131+
for i in range(len(reference_attitude)):
132+
RN = rbk.MRP2C(reference_attitude[i])
152133
ur_output.append(np.matmul(RN.transpose(), [1, 0, 0]))
153134
ut_output.append(np.matmul(RN.transpose(), [0, 1, 0]))
154135
uh_output.append(np.matmul(RN.transpose(), [0, 0, 1]))
155136

156-
ur = initPos / np.linalg.norm(initPos)
157-
uh = np.cross(initPos, initVel) / np.linalg.norm(np.cross(initPos, initVel))
137+
ur = initial_position / np.linalg.norm(initial_position)
138+
uh = np.cross(initial_position, initial_velocity) / np.linalg.norm(np.cross(initial_position, initial_velocity))
158139
ut = np.cross(uh, ur)
159-
f0 = np.linalg.norm(initVel) / np.linalg.norm(initPos)
160-
gamma0 = np.arctan(np.dot(initVel, ur) / np.dot(initVel, ut))
140+
f0 = np.linalg.norm(initial_velocity) / np.linalg.norm(initial_position)
141+
gamma0 = np.arctan(np.dot(initial_velocity, ur) / np.dot(initial_velocity, ut))
161142

162-
for i in range(len(refAtt)):
163-
# check a vector values
164-
ur = dataPos[i] / np.linalg.norm(dataPos[i])
165-
uh = np.cross(dataPos[i], dataVel[i]) / np.linalg.norm(np.cross(dataPos[i], dataVel[i]))
143+
for i in range(len(reference_attitude)):
144+
ur = position_data[i] / np.linalg.norm(position_data[i])
145+
uh = np.cross(position_data[i], velocity_data[i]) / np.linalg.norm(np.cross(position_data[i], velocity_data[i]))
166146
ut = np.cross(uh, ur)
167-
dt = timeData[i]*60
168-
den = ((f0*dt)**2 + 2*f0*np.sin(gamma0)*dt + 1)
147+
dt = time_data[i] * 60
148+
den = ((f0 * dt) ** 2 + 2 * f0 * np.sin(gamma0) * dt + 1)
169149
omega = uh * f0 * np.cos(gamma0) / den
170-
omegaDot = uh * (-2*f0*f0 * np.cos(gamma0)) * (f0*dt + np.sin(gamma0)) / den / den
171-
if signOrbitNormal == -1:
150+
omegaDot = uh * (-2 * f0 * f0 * np.cos(gamma0)) * (f0 * dt + np.sin(gamma0)) / den / den
151+
if orbit_normal_sign == -1:
172152
ut = np.cross(ur, uh)
173153
uh = np.cross(ur, ut)
174154

175155
# test correctness of frame, angular rate and acceleration
176-
np.testing.assert_allclose(ur_output[i], ur, rtol=0, atol=accuracy, verbose=True)
177-
np.testing.assert_allclose(ut_output[i], ut, rtol=0, atol=accuracy, verbose=True)
178-
np.testing.assert_allclose(uh_output[i], uh, rtol=0, atol=accuracy, verbose=True)
179-
np.testing.assert_allclose(refRate[i], omega, rtol=0, atol=accuracy, verbose=True)
180-
np.testing.assert_allclose(refAcc[i], omegaDot, rtol=0, atol=accuracy, verbose=True)
181-
182-
# plot the results
183-
plt.close("all") # clears out plots from earlier test runs
184-
dataPos = np.array(dataPos)
185-
dataVel = np.array(dataVel)
186-
plot_position(timeData, dataPos)
187-
plot_velocity(timeData, dataVel)
188-
plot_ref_attitude(timeData, refAtt)
189-
plot_ref_rates(timeData, refRate)
190-
plot_ref_accelerations(timeData, refAcc)
156+
np.testing.assert_allclose(ur_output[i], ur, rtol=0, atol=1E-12, verbose=True)
157+
np.testing.assert_allclose(ut_output[i], ut, rtol=0, atol=1E-12, verbose=True)
158+
np.testing.assert_allclose(uh_output[i], uh, rtol=0, atol=1E-12, verbose=True)
159+
np.testing.assert_allclose(reference_rate[i], omega, rtol=0, atol=1E-12, verbose=True)
160+
np.testing.assert_allclose(reference_acceleration[i], omegaDot, rtol=0, atol=1E-12, verbose=True)
161+
162+
plt.close("all")
163+
position_data = np.array(position_data)
164+
velocity_data = np.array(velocity_data)
165+
plot_position(time_data, position_data)
166+
plot_velocity(time_data, velocity_data)
167+
plot_ref_attitude(time_data, reference_attitude)
168+
plot_ref_rates(time_data, reference_rate)
169+
plot_ref_accelerations(time_data, reference_acceleration)
191170

192171
if show_plots:
193172
plt.show()
194173

195-
# close the plots being saved off to avoid over-writing old and new figures
196174
plt.close("all")
197175

198-
# each test method requires a single assert method to be called
199-
# this check below just makes sure no sub-test failures were found
200-
# return [testFailCount, ''.join(testMessages)]
201-
return
202176

203-
204-
def plot_position(timeData, dataPos):
177+
def plot_position(time_data, position_data):
205178
"""Plot the attitude errors."""
206179
plt.figure(1)
207180
for idx in range(3):
208-
plt.plot(timeData, dataPos[:, idx],
181+
plt.plot(time_data, position_data[:, idx],
209182
color=unitTestSupport.getLineColor(idx, 3),
210-
label=r'$r_{BN,' + str(idx+1) + '}$')
183+
label=r'$r_{BN,' + str(idx + 1) + '}$')
211184
plt.legend(loc='lower right')
212185
plt.xlabel('Time [min]')
213186
plt.ylabel(r'Inertial Position [m]')
214187

215188

216-
def plot_velocity(timeData, dataVel):
189+
def plot_velocity(time_data, velocity_data):
217190
"""Plot the attitude errors."""
218191
plt.figure(2)
219192
for idx in range(3):
220-
plt.plot(timeData, dataVel[:, idx],
193+
plt.plot(time_data, velocity_data[:, idx],
221194
color=unitTestSupport.getLineColor(idx, 3),
222-
label=r'$v_{BN,' + str(idx+1) + '}$')
195+
label=r'$v_{BN,' + str(idx + 1) + '}$')
223196
plt.legend(loc='lower right')
224197
plt.xlabel('Time [min]')
225198
plt.ylabel(r'Inertial Velocity [m/s]')
226199

227200

228-
def plot_ref_attitude(timeData, sigma_RN):
201+
def plot_ref_attitude(time_data, sigma_RN):
229202
"""Plot the attitude errors."""
230203
plt.figure(3)
231204
for idx in range(3):
232-
plt.plot(timeData, sigma_RN[:, idx],
205+
plt.plot(time_data, sigma_RN[:, idx],
233206
color=unitTestSupport.getLineColor(idx, 3),
234-
label=r'$\sigma_{RN,' + str(idx+1) + '}$')
207+
label=r'$\sigma_{RN,' + str(idx + 1) + '}$')
235208
plt.legend(loc='lower right')
236209
plt.xlabel('Time [min]')
237210
plt.ylabel(r'Reference attitude')
238211

239212

240-
def plot_ref_rates(timeData, omega_RN):
213+
def plot_ref_rates(time_data, omega_RN):
241214
"""Plot the attitude errors."""
242215
plt.figure(4)
243216
for idx in range(3):
244-
plt.plot(timeData, omega_RN[:, idx],
217+
plt.plot(time_data, omega_RN[:, idx],
245218
color=unitTestSupport.getLineColor(idx, 3),
246-
label=r'$\omega_{RN,' + str(idx+1) + '}$')
219+
label=r'$\omega_{RN,' + str(idx + 1) + '}$')
247220
plt.legend(loc='lower right')
248221
plt.xlabel('Time [min]')
249222
plt.ylabel(r'Reference rates')
250223

251224

252-
def plot_ref_accelerations(timeData, omegaDot_RN):
225+
def plot_ref_accelerations(time_data, omegaDot_RN):
253226
"""Plot the attitude errors."""
254227
plt.figure(5)
255228
for idx in range(3):
256-
plt.plot(timeData, omegaDot_RN[:, idx],
229+
plt.plot(time_data, omegaDot_RN[:, idx],
257230
color=unitTestSupport.getLineColor(idx, 3),
258-
label=r'$\dot{\omega}_{RN,' + str(idx+1) + '}$')
231+
label=r'$\dot{\omega}_{RN,' + str(idx + 1) + '}$')
259232
plt.legend(loc='lower right')
260233
plt.xlabel('Time [min]')
261234
plt.ylabel(r'Reference accelerations')
262235

263236

264-
#
265-
# This statement below ensures that the unit test scrip can be run as a
266-
# stand-along python script
267-
#
268237
if __name__ == "__main__":
269-
test_flybyPoint(
270-
True, # show_plots
271-
[-5e7, 7.5e6, 5e5], # initPos
272-
[2e4, 0, 0], # initVel
273-
100, # dTsim
274-
600, # dTfilter
275-
1, # sign Orbit Normal
276-
1e-12 # accuracy
277-
)
238+
test_flybyPoint(True, # show_plots
239+
[-5e7, 7.5e6, 5e5], # initial_position
240+
[2e4, 0, 0], # initial_velocity
241+
1, # filter_dt
242+
1, # sign Orbit Normal
243+
0.01, # max rate
244+
0 # max acceleration
245+
)

0 commit comments

Comments
 (0)