Skip to content

Commit cd3871f

Browse files
committed
[#1387] Fix linkBudget antenna LOS frame transform
1 parent ffdae57 commit cd3871f

6 files changed

Lines changed: 53 additions & 4 deletions

File tree

docs/source/Support/bskKnownIssues.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ Version |release|
2727
for Basilisk and compatible plugins. If source builds fail with SWIG 4.4.0 or emit
2828
``builtin type swigvarlink has no __module__ attribute`` warnings, upgrade to SWIG 4.4.1 or a newer
2929
supported 4.x release.
30+
- BSK-1387: :ref:`linkBudget` could compute excessive pointing loss for non-identity antenna attitudes
31+
because the inertial line-of-sight vector was transformed using the antenna-to-inertial DCM. This is
32+
fixed in the current version.
3033

3134

3235
Version 2.10.0 (April 2, 2026)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fixed :ref:`linkBudget` pointing-loss frame handling so ``AntennaLogMsgPayload.sigma_AN`` antenna orientations are interpreted consistently with :ref:`simpleAntenna`.

src/simulation/communication/linkBudget/_UnitTest/test_linkBudget.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,51 @@ def test_linkBudget_pointing_loss():
12201220
assert testFailCount == 0, "\n".join(testMessages)
12211221

12221222

1223+
def test_linkBudget_pointing_loss_nontrivial_attitude_zero_error():
1224+
"""
1225+
Verify that a perfectly pointed, non-identity antenna attitude produces
1226+
negligible pointing loss.
1227+
1228+
This catches frame-convention regressions where the inertial LOS vector is
1229+
transformed with the antenna-to-inertial DCM instead of its transpose.
1230+
"""
1231+
frequency_Hz = 2.2e9 # [Hz]
1232+
directivity_dB = 20.0 # [dB]
1233+
k = 1.0 # [-]
1234+
pointing_loss_tolerance = 1e-10 # [dB]
1235+
1236+
pos1 = [0.0, 0.0, 0.0] # [m]
1237+
pos2 = [1000e3, 400e3, 700e3] # [m]
1238+
1239+
ant1_payload, _ = create_antenna_msg_payload(
1240+
name="Ant1",
1241+
state=ANTENNA_TX,
1242+
frequency=frequency_Hz,
1243+
directivity_dB=directivity_dB,
1244+
k=k,
1245+
position=pos1,
1246+
target_position=pos2,
1247+
)
1248+
ant2_payload, _ = create_antenna_msg_payload(
1249+
name="Ant2",
1250+
state=ANTENNA_RX,
1251+
frequency=frequency_Hz,
1252+
directivity_dB=directivity_dB,
1253+
k=k,
1254+
position=pos2,
1255+
target_position=pos1,
1256+
)
1257+
1258+
unitTestSim, linkBudgetModule, _ = setup_link_budget_sim(ant1_payload, ant2_payload)
1259+
run_simulation(unitTestSim)
1260+
1261+
L_point_sim = linkBudgetModule.getL_point()
1262+
assert abs(L_point_sim) < pointing_loss_tolerance, (
1263+
"Perfectly aligned non-identity antenna attitudes should have near-zero "
1264+
f"pointing loss, got {L_point_sim:.6e} dB"
1265+
)
1266+
1267+
12231268
def test_linkBudget_no_bandwidth_overlap():
12241269
"""
12251270
Test behavior when antennas have no overlapping bandwidth.

src/simulation/communication/linkBudget/linkBudget.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ Eigen::Vector2d LinkBudget::getPointingError(Eigen::Vector3d r_A1N_N, Eigen::Vec
283283
bskLogger.bskLog(BSK_WARNING, "LinkBudget.getPointingError: antenna positions are coincident; returning zero pointing errors.");
284284
return {0.0, 0.0};
285285
}
286-
// Calculate the unit vector from antenna 1 to antenna 2
287-
Eigen::Vector3d n_A2A1_A1 = dcm_NA1 * (r_A2A1_N / deltaNorm);
286+
// Calculate the unit vector from antenna 1 to antenna 2, decomposed in antenna 1 frame
287+
Eigen::Vector3d n_A2A1_A1 = dcm_NA1.transpose() * (r_A2A1_N / deltaNorm);
288288
// boresight vector of antenna 1 in {A1} frame
289289
double boresightComponent = n_A2A1_A1[2];
290290
double azimuthError = std::atan2(n_A2A1_A1[0], boresightComponent); // [rad] Azimuth pointing error

src/simulation/communication/linkBudget/linkBudget.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class LinkBudget: public SysModel {
187187
double calcCosSlantGround(Eigen::Vector3d r_AN_N, Eigen::Vector3d n_A_N, Eigen::Vector3d r_BN_N);
188188
void generateLookupTable(LinkBudgetTypes::GasType gasType, LookupTable* lookupTable);
189189
void precomputeAtmosphericAttenuationAtLayers(AttenuationLookupTable* tableAttenuation, double frequency);
190-
Eigen::Vector2d getPointingError(Eigen::Vector3d r_A1N_N, Eigen::Vector3d r_A2N_N, Eigen::MRPd sigma_NA1);
190+
Eigen::Vector2d getPointingError(Eigen::Vector3d r_A1N_N, Eigen::Vector3d r_A2N_N, Eigen::MRPd sigma_AN1);
191191
void calculatePointingLoss();
192192
void calculateFrequencyOffsetLoss();
193193
void calculateCNR();

src/simulation/communication/linkBudget/linkBudget.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ Pointing Loss
134134

135135
Figure 3: Illustration of pointing loss due to antenna misalignment (pointing error angle in elevation, :math:`\| \theta_\mathrm{el} \| > 0^\circ`)
136136

137-
The pointing error loss accounts for signal degradation when the antennas are not perfectly aligned. This loss is computed assuming a Gaussian antenna radiation pattern (consistent with the :ref:`simpleAntenna` module).
137+
The pointing error loss accounts for signal degradation when the antennas are not perfectly aligned. This loss is computed assuming a Gaussian antenna radiation pattern (consistent with the :ref:`simpleAntenna` module). The antenna orientation is read from ``AntennaLogMsgPayload.sigma_AN`` and the inertial line-of-sight vector is decomposed in the antenna frame, where the antenna boresight is the local +Z axis.
138138

139139
For each antenna, the pointing error is decomposed into azimuth and elevation components relative to the antenna boresight direction\:
140140

0 commit comments

Comments
 (0)