Skip to content

Commit c19495d

Browse files
committed
Fold latitude angles correctly across complete turns
1 parent 2de7801 commit c19495d

2 files changed

Lines changed: 28 additions & 7 deletions

File tree

‎spatialmath/base/vectors.py‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -567,13 +567,10 @@ def wrap_mpi2_pi2(theta: ArrayLike) -> Union[float, NDArray]:
567567
568568
"""
569569
theta = getvector(theta)
570-
n = theta / np.pi * 2
571-
if isinstance(n, np.ndarray):
572-
n = n.astype(int)
573-
else:
574-
n = np.fix(n).astype(int)
575-
576-
y = np.where(np.bitwise_and(n, 1) == 0, theta - n * np.pi, n * np.pi - theta)
570+
theta = np.fmod(theta, 2 * np.pi)
571+
theta = np.where(theta > np.pi, theta - 2 * np.pi, theta)
572+
theta = np.where(theta < -np.pi, theta + 2 * np.pi, theta)
573+
y = np.where(np.abs(theta) <= np.pi / 2, theta, np.copysign(np.pi, theta) - theta)
577574
if isinstance(y, np.ndarray) and len(y) == 1:
578575
return float(y[0])
579576
else:

‎tests/base/test_vectors.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,30 @@ def test_angdiff(self):
278278
nt.assert_array_almost_equal(x, [0, 0, 0])
279279
self.assertIsInstance(x, np.ndarray)
280280

281+
def test_latitude_wrap_multiple_turns(self):
282+
angles = pi * np.arange(-4, 4.5, 0.5)
283+
expected = pi * np.array(
284+
[0, 0.5, 0, -0.5, 0, 0.5, 0, -0.5, 0, 0.5, 0, -0.5, 0, 0.5, 0, -0.5, 0]
285+
)
286+
nt.assert_allclose(wrap_mpi2_pi2(angles), expected, atol=1e-14)
287+
nt.assert_allclose(angle_wrap(angles, mode="-pi/2:pi/2"), expected, atol=1e-14)
288+
for angle, folded in zip(angles, expected):
289+
self.assertAlmostEqual(wrap_mpi2_pi2(angle), folded)
290+
291+
def test_latitude_wrap_boundaries_and_small_angles(self):
292+
epsilon = 1e-12
293+
angles = np.array(
294+
[-pi / 2 - epsilon, -pi / 2 + epsilon, pi / 2 - epsilon, pi / 2 + epsilon]
295+
)
296+
expected = np.array(
297+
[-pi / 2 + epsilon, -pi / 2 + epsilon, pi / 2 - epsilon, pi / 2 - epsilon]
298+
)
299+
nt.assert_allclose(wrap_mpi2_pi2(angles), expected, rtol=0, atol=1e-15)
300+
nt.assert_allclose(
301+
wrap_mpi2_pi2([1e-16, -1e-16]), [1e-16, -1e-16], rtol=0, atol=0
302+
)
303+
self.assertEqual(wrap_mpi2_pi2([]).size, 0)
304+
281305
def test_wrap(self):
282306
self.assertAlmostEqual(wrap_0_2pi(0), 0)
283307
self.assertAlmostEqual(wrap_0_2pi(2 * pi), 0)

0 commit comments

Comments
 (0)