import numpy as np
import pytest
from astrora._core import GCRS, ITRS, TEME, Epoch
POS_TOL = 1.0 VEL_TOL = 0.001
def test_teme_creation():
pos = np.array([7000000.0, 0.0, 0.0])
vel = np.array([0.0, 7500.0, 0.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
teme = TEME(pos, vel, epoch)
np.testing.assert_array_equal(teme.position, pos)
np.testing.assert_array_equal(teme.velocity, vel)
assert teme.obstime == epoch
def test_teme_properties():
pos = np.array([7000000.0, 1000000.0, 500000.0])
vel = np.array([1000.0, 7000.0, 500.0])
epoch = Epoch(2024, 6, 15, 12, 0, 0, 0)
teme = TEME(pos, vel, epoch)
assert isinstance(teme.position, np.ndarray)
assert teme.position.shape == (3,)
np.testing.assert_allclose(teme.position, pos, rtol=1e-12)
assert isinstance(teme.velocity, np.ndarray)
assert teme.velocity.shape == (3,)
np.testing.assert_allclose(teme.velocity, vel, rtol=1e-12)
assert isinstance(teme.obstime, Epoch)
assert teme.obstime == epoch
def test_teme_repr():
pos = np.array([7000000.0, 0.0, 0.0])
vel = np.array([0.0, 7500.0, 0.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
teme = TEME(pos, vel, epoch)
repr_str = repr(teme)
assert "TEME" in repr_str
assert "7.000e" in repr_str assert "7.500e" in repr_str assert "2024-01-01" in repr_str
def test_teme_to_itrs():
pos_teme = np.array([7000000.0, 0.0, 0.0])
vel_teme = np.array([0.0, 7500.0, 0.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
teme = TEME(pos_teme, vel_teme, epoch)
itrs = teme.to_itrs()
assert isinstance(itrs, ITRS)
assert isinstance(itrs.position, np.ndarray)
assert isinstance(itrs.velocity, np.ndarray)
r_teme = np.linalg.norm(pos_teme)
r_itrs = np.linalg.norm(itrs.position)
assert abs(r_itrs - r_teme) < POS_TOL
assert abs(itrs.position[2] - pos_teme[2]) < 1e-6
assert itrs.obstime == epoch
def test_itrs_to_teme():
pos_itrs = np.array([6378137.0, 0.0, 0.0])
vel_itrs = np.array([0.0, 0.0, 0.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
itrs = ITRS(pos_itrs, vel_itrs, epoch)
teme = itrs.to_teme()
assert isinstance(teme, TEME)
assert isinstance(teme.position, np.ndarray)
assert isinstance(teme.velocity, np.ndarray)
r_itrs = np.linalg.norm(pos_itrs)
r_teme = np.linalg.norm(teme.position)
assert abs(r_teme - r_itrs) < POS_TOL
assert abs(teme.position[2] - pos_itrs[2]) < 1e-6
assert teme.obstime == epoch
def test_teme_itrs_roundtrip():
pos = np.array([7000000.0, 1000000.0, 500000.0])
vel = np.array([1000.0, 7000.0, 500.0])
epoch = Epoch(2024, 6, 15, 12, 0, 0, 0)
teme1 = TEME(pos, vel, epoch)
itrs = teme1.to_itrs()
teme2 = itrs.to_teme()
np.testing.assert_allclose(teme2.position, pos, atol=1e-3)
np.testing.assert_allclose(teme2.velocity, vel, atol=1e-6)
assert teme2.obstime == epoch
def test_itrs_teme_roundtrip():
pos = np.array([6378137.0, 1000000.0, 500000.0])
vel = np.array([100.0, 50.0, 25.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
itrs1 = ITRS(pos, vel, epoch)
teme = itrs1.to_teme()
itrs2 = teme.to_itrs()
np.testing.assert_allclose(itrs2.position, pos, atol=1e-3)
np.testing.assert_allclose(itrs2.velocity, vel, atol=1e-6)
assert itrs2.obstime == epoch
def test_teme_to_gcrs():
pos = np.array([7000000.0, 1000000.0, 500000.0])
vel = np.array([1000.0, 7000.0, 500.0])
epoch = Epoch(2024, 1, 1, 12, 0, 0, 0)
teme = TEME(pos, vel, epoch)
gcrs = teme.to_gcrs()
assert isinstance(gcrs, GCRS)
assert isinstance(gcrs.position, np.ndarray)
assert isinstance(gcrs.velocity, np.ndarray)
r_teme = np.linalg.norm(pos)
r_gcrs = np.linalg.norm(gcrs.position)
assert abs(r_gcrs - r_teme) < POS_TOL
assert gcrs.obstime == epoch
def test_teme_conservation_of_position_magnitude():
pos = np.array([8000000.0, 5656854.0, 1000000.0]) vel = np.array([-5000.0, 3000.0, 1000.0])
epoch = Epoch(2024, 1, 1, 12, 0, 0, 0)
teme = TEME(pos, vel, epoch)
itrs = teme.to_itrs()
r_teme = np.linalg.norm(pos)
r_itrs = np.linalg.norm(itrs.position)
np.testing.assert_allclose(r_itrs, r_teme, atol=1e-3)
def test_teme_polar_position_unchanged():
r_pole = 6356752.0 pos_teme = np.array([0.0, 0.0, r_pole])
vel_teme = np.array([0.0, 0.0, 100.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
teme = TEME(pos_teme, vel_teme, epoch)
itrs = teme.to_itrs()
np.testing.assert_allclose(itrs.position[0], 0.0, atol=1e-3)
np.testing.assert_allclose(itrs.position[1], 0.0, atol=1e-3)
np.testing.assert_allclose(itrs.position[2], r_pole, atol=POS_TOL)
def test_teme_leo_satellite():
r_earth = 6371000.0
h = 408000.0 r = r_earth + h
pos = np.array([r, 0.0, 0.0])
v_circ = np.sqrt(398600.4418e9 / r) vel = np.array([0.0, v_circ, 0.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
teme = TEME(pos, vel, epoch)
r_mag = np.linalg.norm(teme.position)
np.testing.assert_allclose(r_mag, r, atol=POS_TOL)
v_mag = np.linalg.norm(teme.velocity)
assert 7000.0 < v_mag < 8000.0
def test_teme_geo_satellite():
r_geo = 42164000.0 pos = np.array([r_geo, 0.0, 0.0])
v_geo = np.sqrt(398600.4418e9 / r_geo) vel = np.array([0.0, v_geo, 0.0])
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
teme = TEME(pos, vel, epoch)
r_mag = np.linalg.norm(teme.position)
np.testing.assert_allclose(r_mag, r_geo, atol=POS_TOL)
v_mag = np.linalg.norm(teme.velocity)
np.testing.assert_allclose(v_mag, v_geo, atol=POS_TOL)
assert 3000.0 < v_mag < 3100.0
def test_teme_invalid_inputs():
epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
with pytest.raises(ValueError, match="Position and velocity must be 3-element arrays"):
TEME(np.array([1.0, 2.0]), np.array([1.0, 2.0, 3.0]), epoch)
with pytest.raises(ValueError, match="Position and velocity must be 3-element arrays"):
TEME(np.array([1.0, 2.0, 3.0]), np.array([1.0, 2.0]), epoch)
def test_teme_different_epochs():
pos = np.array([7000000.0, 0.0, 0.0])
vel = np.array([0.0, 7500.0, 0.0])
epoch1 = Epoch.j2000_epoch()
teme1 = TEME(pos, vel, epoch1)
itrs1 = teme1.to_itrs()
assert itrs1.obstime == epoch1
epoch2 = Epoch(2025, 6, 15, 12, 0, 0, 0)
teme2 = TEME(pos, vel, epoch2)
itrs2 = teme2.to_itrs()
assert itrs2.obstime == epoch2
assert not np.allclose(itrs1.position, itrs2.position, atol=1e3)
def test_teme_itrs_velocity_transformation():
r_earth = 6378137.0
pos_itrs = np.array([r_earth, 0.0, 0.0])
vel_itrs = np.array([0.0, 0.0, 0.0]) epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
itrs = ITRS(pos_itrs, vel_itrs, epoch)
teme = itrs.to_teme()
omega_earth = 7.2921150e-5
expected_v = omega_earth * r_earth
v_teme = np.linalg.norm(teme.velocity)
np.testing.assert_allclose(v_teme, expected_v, atol=1.0)
dot_product = np.dot(teme.velocity, teme.position)
np.testing.assert_allclose(dot_product, 0.0, atol=100.0)
assert abs(teme.velocity[2]) < 1.0
def test_teme_equatorial_plane_satellites():
altitudes = [400e3, 1000e3, 10000e3, 35786e3] epoch = Epoch(2024, 1, 1, 0, 0, 0, 0)
for h in altitudes:
r = 6371000.0 + h
pos = np.array([r, 0.0, 0.0])
v_circ = np.sqrt(398600.4418e9 / r)
vel = np.array([0.0, v_circ, 0.0])
teme = TEME(pos, vel, epoch)
itrs = teme.to_itrs()
r_teme = np.linalg.norm(pos)
r_itrs = np.linalg.norm(itrs.position)
np.testing.assert_allclose(r_itrs, r_teme, atol=POS_TOL)
assert abs(itrs.position[2]) < 1e-6
def test_teme_high_inclination_orbit():
r = 7000000.0
pos = np.array([0.0, r, 0.0])
vel = np.array([0.0, 0.0, 7500.0]) epoch = Epoch(2024, 1, 1, 12, 0, 0, 0)
teme = TEME(pos, vel, epoch)
itrs = teme.to_itrs()
gcrs = teme.to_gcrs()
r_teme = np.linalg.norm(pos)
r_itrs = np.linalg.norm(itrs.position)
r_gcrs = np.linalg.norm(gcrs.position)
np.testing.assert_allclose(r_itrs, r_teme, atol=POS_TOL)
np.testing.assert_allclose(r_gcrs, r_teme, atol=POS_TOL)
if __name__ == "__main__":
pytest.main([__file__, "-v"])