nrl-tracker 1.5.0__py3-none-any.whl → 1.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nrl_tracker-1.5.0.dist-info → nrl_tracker-1.6.0.dist-info}/METADATA +7 -6
- {nrl_tracker-1.5.0.dist-info → nrl_tracker-1.6.0.dist-info}/RECORD +12 -9
- pytcl/__init__.py +1 -1
- pytcl/astronomical/__init__.py +66 -0
- pytcl/astronomical/reference_frames.py +632 -1
- pytcl/astronomical/sgp4.py +710 -0
- pytcl/astronomical/tle.py +558 -0
- pytcl/dynamic_estimation/kalman/__init__.py +18 -0
- pytcl/dynamic_estimation/kalman/h_infinity.py +613 -0
- {nrl_tracker-1.5.0.dist-info → nrl_tracker-1.6.0.dist-info}/LICENSE +0 -0
- {nrl_tracker-1.5.0.dist-info → nrl_tracker-1.6.0.dist-info}/WHEEL +0 -0
- {nrl_tracker-1.5.0.dist-info → nrl_tracker-1.6.0.dist-info}/top_level.txt +0 -0
|
@@ -23,7 +23,7 @@ References
|
|
|
23
23
|
|
|
24
24
|
import logging
|
|
25
25
|
from functools import lru_cache
|
|
26
|
-
from typing import Tuple
|
|
26
|
+
from typing import Optional, Tuple
|
|
27
27
|
|
|
28
28
|
import numpy as np
|
|
29
29
|
from numpy.typing import NDArray
|
|
@@ -689,6 +689,619 @@ def equatorial_to_ecliptic(
|
|
|
689
689
|
return R @ r_eq
|
|
690
690
|
|
|
691
691
|
|
|
692
|
+
# =============================================================================
|
|
693
|
+
# TEME Frame Transformations
|
|
694
|
+
# =============================================================================
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def teme_to_pef(
|
|
698
|
+
r_teme: NDArray[np.floating],
|
|
699
|
+
jd_ut1: float,
|
|
700
|
+
) -> NDArray[np.floating]:
|
|
701
|
+
"""
|
|
702
|
+
Transform position from TEME to PEF (Pseudo Earth-Fixed).
|
|
703
|
+
|
|
704
|
+
TEME is the True Equator, Mean Equinox frame used by SGP4.
|
|
705
|
+
This transformation applies only the GMST rotation.
|
|
706
|
+
|
|
707
|
+
Parameters
|
|
708
|
+
----------
|
|
709
|
+
r_teme : ndarray
|
|
710
|
+
Position in TEME frame (km), shape (3,).
|
|
711
|
+
jd_ut1 : float
|
|
712
|
+
Julian date in UT1.
|
|
713
|
+
|
|
714
|
+
Returns
|
|
715
|
+
-------
|
|
716
|
+
r_pef : ndarray
|
|
717
|
+
Position in PEF frame (km), shape (3,).
|
|
718
|
+
"""
|
|
719
|
+
gmst = gmst_iau82(jd_ut1)
|
|
720
|
+
R = sidereal_rotation_matrix(gmst)
|
|
721
|
+
return R @ r_teme
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def pef_to_teme(
|
|
725
|
+
r_pef: NDArray[np.floating],
|
|
726
|
+
jd_ut1: float,
|
|
727
|
+
) -> NDArray[np.floating]:
|
|
728
|
+
"""
|
|
729
|
+
Transform position from PEF to TEME.
|
|
730
|
+
|
|
731
|
+
Parameters
|
|
732
|
+
----------
|
|
733
|
+
r_pef : ndarray
|
|
734
|
+
Position in PEF frame (km), shape (3,).
|
|
735
|
+
jd_ut1 : float
|
|
736
|
+
Julian date in UT1.
|
|
737
|
+
|
|
738
|
+
Returns
|
|
739
|
+
-------
|
|
740
|
+
r_teme : ndarray
|
|
741
|
+
Position in TEME frame (km), shape (3,).
|
|
742
|
+
"""
|
|
743
|
+
gmst = gmst_iau82(jd_ut1)
|
|
744
|
+
R = sidereal_rotation_matrix(gmst)
|
|
745
|
+
return R.T @ r_pef
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
def teme_to_itrf(
|
|
749
|
+
r_teme: NDArray[np.floating],
|
|
750
|
+
jd_ut1: float,
|
|
751
|
+
xp: float = 0.0,
|
|
752
|
+
yp: float = 0.0,
|
|
753
|
+
) -> NDArray[np.floating]:
|
|
754
|
+
"""
|
|
755
|
+
Transform position from TEME to ITRF (Earth-fixed).
|
|
756
|
+
|
|
757
|
+
TEME is the True Equator, Mean Equinox frame used by SGP4/SDP4.
|
|
758
|
+
This is the frame in which TLE-propagated positions are expressed.
|
|
759
|
+
|
|
760
|
+
Parameters
|
|
761
|
+
----------
|
|
762
|
+
r_teme : ndarray
|
|
763
|
+
Position in TEME frame (km), shape (3,).
|
|
764
|
+
jd_ut1 : float
|
|
765
|
+
Julian date in UT1.
|
|
766
|
+
xp : float, optional
|
|
767
|
+
Polar motion x (radians). Default 0.
|
|
768
|
+
yp : float, optional
|
|
769
|
+
Polar motion y (radians). Default 0.
|
|
770
|
+
|
|
771
|
+
Returns
|
|
772
|
+
-------
|
|
773
|
+
r_itrf : ndarray
|
|
774
|
+
Position in ITRF frame (km), shape (3,).
|
|
775
|
+
|
|
776
|
+
Notes
|
|
777
|
+
-----
|
|
778
|
+
TEME is a quasi-inertial frame that uses the mean equinox instead
|
|
779
|
+
of the true equinox. The transformation sequence is:
|
|
780
|
+
|
|
781
|
+
TEME -> PEF (via GMST rotation) -> ITRF (via polar motion)
|
|
782
|
+
|
|
783
|
+
Examples
|
|
784
|
+
--------
|
|
785
|
+
>>> from pytcl.astronomical.sgp4 import sgp4_propagate
|
|
786
|
+
>>> from pytcl.astronomical.tle import parse_tle
|
|
787
|
+
>>> tle = parse_tle(line1, line2)
|
|
788
|
+
>>> state = sgp4_propagate(tle, 0.0)
|
|
789
|
+
>>> r_itrf = teme_to_itrf(state.r, jd_ut1)
|
|
790
|
+
"""
|
|
791
|
+
r_pef = teme_to_pef(r_teme, jd_ut1)
|
|
792
|
+
W = polar_motion_matrix(xp, yp)
|
|
793
|
+
return W @ r_pef
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def itrf_to_teme(
|
|
797
|
+
r_itrf: NDArray[np.floating],
|
|
798
|
+
jd_ut1: float,
|
|
799
|
+
xp: float = 0.0,
|
|
800
|
+
yp: float = 0.0,
|
|
801
|
+
) -> NDArray[np.floating]:
|
|
802
|
+
"""
|
|
803
|
+
Transform position from ITRF to TEME.
|
|
804
|
+
|
|
805
|
+
Parameters
|
|
806
|
+
----------
|
|
807
|
+
r_itrf : ndarray
|
|
808
|
+
Position in ITRF frame (km), shape (3,).
|
|
809
|
+
jd_ut1 : float
|
|
810
|
+
Julian date in UT1.
|
|
811
|
+
xp : float, optional
|
|
812
|
+
Polar motion x (radians). Default 0.
|
|
813
|
+
yp : float, optional
|
|
814
|
+
Polar motion y (radians). Default 0.
|
|
815
|
+
|
|
816
|
+
Returns
|
|
817
|
+
-------
|
|
818
|
+
r_teme : ndarray
|
|
819
|
+
Position in TEME frame (km), shape (3,).
|
|
820
|
+
"""
|
|
821
|
+
W = polar_motion_matrix(xp, yp)
|
|
822
|
+
r_pef = W.T @ r_itrf
|
|
823
|
+
return pef_to_teme(r_pef, jd_ut1)
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def teme_to_gcrf(
|
|
827
|
+
r_teme: NDArray[np.floating],
|
|
828
|
+
jd_tt: float,
|
|
829
|
+
) -> NDArray[np.floating]:
|
|
830
|
+
"""
|
|
831
|
+
Transform position from TEME to GCRF (inertial).
|
|
832
|
+
|
|
833
|
+
This transformation accounts for the difference between
|
|
834
|
+
the mean and true equinox (equation of equinoxes) and then
|
|
835
|
+
applies precession and nutation to go from TOD to GCRF.
|
|
836
|
+
|
|
837
|
+
Parameters
|
|
838
|
+
----------
|
|
839
|
+
r_teme : ndarray
|
|
840
|
+
Position in TEME frame (km), shape (3,).
|
|
841
|
+
jd_tt : float
|
|
842
|
+
Julian date in TT (Terrestrial Time).
|
|
843
|
+
|
|
844
|
+
Returns
|
|
845
|
+
-------
|
|
846
|
+
r_gcrf : ndarray
|
|
847
|
+
Position in GCRF frame (km), shape (3,).
|
|
848
|
+
|
|
849
|
+
Notes
|
|
850
|
+
-----
|
|
851
|
+
The transformation sequence is:
|
|
852
|
+
|
|
853
|
+
TEME -> TOD (via equation of equinoxes)
|
|
854
|
+
TOD -> MOD (via nutation, inverse)
|
|
855
|
+
MOD -> GCRF (via precession, inverse)
|
|
856
|
+
|
|
857
|
+
Examples
|
|
858
|
+
--------
|
|
859
|
+
>>> state = sgp4_propagate(tle, 60.0)
|
|
860
|
+
>>> r_gcrf = teme_to_gcrf(state.r, jd_tt)
|
|
861
|
+
"""
|
|
862
|
+
eq_eq = equation_of_equinoxes(jd_tt)
|
|
863
|
+
|
|
864
|
+
# TEME to TOD: rotate by equation of equinoxes
|
|
865
|
+
cos_eq = np.cos(-eq_eq)
|
|
866
|
+
sin_eq = np.sin(-eq_eq)
|
|
867
|
+
|
|
868
|
+
R_eq = np.array([[cos_eq, -sin_eq, 0], [sin_eq, cos_eq, 0], [0, 0, 1]])
|
|
869
|
+
|
|
870
|
+
r_tod = R_eq @ r_teme
|
|
871
|
+
|
|
872
|
+
# TOD to MOD (inverse nutation)
|
|
873
|
+
N = nutation_matrix(jd_tt)
|
|
874
|
+
r_mod = N.T @ r_tod
|
|
875
|
+
|
|
876
|
+
# MOD to GCRF (inverse precession)
|
|
877
|
+
P = precession_matrix_iau76(jd_tt)
|
|
878
|
+
return P.T @ r_mod
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def gcrf_to_teme(
|
|
882
|
+
r_gcrf: NDArray[np.floating],
|
|
883
|
+
jd_tt: float,
|
|
884
|
+
) -> NDArray[np.floating]:
|
|
885
|
+
"""
|
|
886
|
+
Transform position from GCRF to TEME.
|
|
887
|
+
|
|
888
|
+
Parameters
|
|
889
|
+
----------
|
|
890
|
+
r_gcrf : ndarray
|
|
891
|
+
Position in GCRF frame (km), shape (3,).
|
|
892
|
+
jd_tt : float
|
|
893
|
+
Julian date in TT.
|
|
894
|
+
|
|
895
|
+
Returns
|
|
896
|
+
-------
|
|
897
|
+
r_teme : ndarray
|
|
898
|
+
Position in TEME frame (km), shape (3,).
|
|
899
|
+
"""
|
|
900
|
+
# GCRF to MOD (precession)
|
|
901
|
+
P = precession_matrix_iau76(jd_tt)
|
|
902
|
+
r_mod = P @ r_gcrf
|
|
903
|
+
|
|
904
|
+
# MOD to TOD (nutation)
|
|
905
|
+
N = nutation_matrix(jd_tt)
|
|
906
|
+
r_tod = N @ r_mod
|
|
907
|
+
|
|
908
|
+
# TOD to TEME: rotate by equation of equinoxes
|
|
909
|
+
eq_eq = equation_of_equinoxes(jd_tt)
|
|
910
|
+
cos_eq = np.cos(eq_eq)
|
|
911
|
+
sin_eq = np.sin(eq_eq)
|
|
912
|
+
|
|
913
|
+
R_eq = np.array([[cos_eq, -sin_eq, 0], [sin_eq, cos_eq, 0], [0, 0, 1]])
|
|
914
|
+
|
|
915
|
+
return R_eq @ r_tod
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
def teme_to_itrf_with_velocity(
|
|
919
|
+
r_teme: NDArray[np.floating],
|
|
920
|
+
v_teme: NDArray[np.floating],
|
|
921
|
+
jd_ut1: float,
|
|
922
|
+
xp: float = 0.0,
|
|
923
|
+
yp: float = 0.0,
|
|
924
|
+
) -> Tuple[NDArray[np.floating], NDArray[np.floating]]:
|
|
925
|
+
"""
|
|
926
|
+
Transform position and velocity from TEME to ITRF.
|
|
927
|
+
|
|
928
|
+
This properly accounts for the velocity transformation including
|
|
929
|
+
the Earth's rotation rate.
|
|
930
|
+
|
|
931
|
+
Parameters
|
|
932
|
+
----------
|
|
933
|
+
r_teme : ndarray
|
|
934
|
+
Position in TEME frame (km), shape (3,).
|
|
935
|
+
v_teme : ndarray
|
|
936
|
+
Velocity in TEME frame (km/s), shape (3,).
|
|
937
|
+
jd_ut1 : float
|
|
938
|
+
Julian date in UT1.
|
|
939
|
+
xp : float, optional
|
|
940
|
+
Polar motion x (radians). Default 0.
|
|
941
|
+
yp : float, optional
|
|
942
|
+
Polar motion y (radians). Default 0.
|
|
943
|
+
|
|
944
|
+
Returns
|
|
945
|
+
-------
|
|
946
|
+
r_itrf : ndarray
|
|
947
|
+
Position in ITRF frame (km), shape (3,).
|
|
948
|
+
v_itrf : ndarray
|
|
949
|
+
Velocity in ITRF frame (km/s), shape (3,).
|
|
950
|
+
"""
|
|
951
|
+
omega_earth = 7.29211514670698e-5 # rad/s
|
|
952
|
+
|
|
953
|
+
gmst = gmst_iau82(jd_ut1)
|
|
954
|
+
R = sidereal_rotation_matrix(gmst)
|
|
955
|
+
W = polar_motion_matrix(xp, yp)
|
|
956
|
+
|
|
957
|
+
# Position transformation
|
|
958
|
+
r_pef = R @ r_teme
|
|
959
|
+
r_itrf = W @ r_pef
|
|
960
|
+
|
|
961
|
+
# Velocity includes Earth rotation effect
|
|
962
|
+
omega_vec = np.array([0.0, 0.0, omega_earth])
|
|
963
|
+
v_pef = R @ v_teme - np.cross(omega_vec, r_pef)
|
|
964
|
+
v_itrf = W @ v_pef
|
|
965
|
+
|
|
966
|
+
return r_itrf, v_itrf
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
def itrf_to_teme_with_velocity(
|
|
970
|
+
r_itrf: NDArray[np.floating],
|
|
971
|
+
v_itrf: NDArray[np.floating],
|
|
972
|
+
jd_ut1: float,
|
|
973
|
+
xp: float = 0.0,
|
|
974
|
+
yp: float = 0.0,
|
|
975
|
+
) -> Tuple[NDArray[np.floating], NDArray[np.floating]]:
|
|
976
|
+
"""
|
|
977
|
+
Transform position and velocity from ITRF to TEME.
|
|
978
|
+
|
|
979
|
+
Parameters
|
|
980
|
+
----------
|
|
981
|
+
r_itrf : ndarray
|
|
982
|
+
Position in ITRF frame (km), shape (3,).
|
|
983
|
+
v_itrf : ndarray
|
|
984
|
+
Velocity in ITRF frame (km/s), shape (3,).
|
|
985
|
+
jd_ut1 : float
|
|
986
|
+
Julian date in UT1.
|
|
987
|
+
xp : float, optional
|
|
988
|
+
Polar motion x (radians). Default 0.
|
|
989
|
+
yp : float, optional
|
|
990
|
+
Polar motion y (radians). Default 0.
|
|
991
|
+
|
|
992
|
+
Returns
|
|
993
|
+
-------
|
|
994
|
+
r_teme : ndarray
|
|
995
|
+
Position in TEME frame (km), shape (3,).
|
|
996
|
+
v_teme : ndarray
|
|
997
|
+
Velocity in TEME frame (km/s), shape (3,).
|
|
998
|
+
"""
|
|
999
|
+
omega_earth = 7.29211514670698e-5 # rad/s
|
|
1000
|
+
|
|
1001
|
+
gmst = gmst_iau82(jd_ut1)
|
|
1002
|
+
R = sidereal_rotation_matrix(gmst)
|
|
1003
|
+
W = polar_motion_matrix(xp, yp)
|
|
1004
|
+
|
|
1005
|
+
# Position transformation
|
|
1006
|
+
r_pef = W.T @ r_itrf
|
|
1007
|
+
r_teme = R.T @ r_pef
|
|
1008
|
+
|
|
1009
|
+
# Velocity includes Earth rotation effect
|
|
1010
|
+
omega_vec = np.array([0.0, 0.0, omega_earth])
|
|
1011
|
+
v_pef = W.T @ v_itrf
|
|
1012
|
+
v_teme = R.T @ (v_pef + np.cross(omega_vec, r_pef))
|
|
1013
|
+
|
|
1014
|
+
return r_teme, v_teme
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
# =============================================================================
|
|
1018
|
+
# TOD/MOD Frame Transformations (Legacy Conventions)
|
|
1019
|
+
# =============================================================================
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
def gcrf_to_mod(
|
|
1023
|
+
r_gcrf: NDArray[np.floating],
|
|
1024
|
+
jd_tt: float,
|
|
1025
|
+
) -> NDArray[np.floating]:
|
|
1026
|
+
"""
|
|
1027
|
+
Transform position from GCRF to MOD (Mean of Date).
|
|
1028
|
+
|
|
1029
|
+
MOD is the mean equator and mean equinox of date frame.
|
|
1030
|
+
This applies only the precession transformation.
|
|
1031
|
+
|
|
1032
|
+
Parameters
|
|
1033
|
+
----------
|
|
1034
|
+
r_gcrf : ndarray
|
|
1035
|
+
Position in GCRF frame (km), shape (3,).
|
|
1036
|
+
jd_tt : float
|
|
1037
|
+
Julian date in TT (Terrestrial Time).
|
|
1038
|
+
|
|
1039
|
+
Returns
|
|
1040
|
+
-------
|
|
1041
|
+
r_mod : ndarray
|
|
1042
|
+
Position in MOD frame (km), shape (3,).
|
|
1043
|
+
|
|
1044
|
+
Notes
|
|
1045
|
+
-----
|
|
1046
|
+
MOD is a legacy frame convention. For most modern applications,
|
|
1047
|
+
GCRF (J2000) is preferred. MOD was historically used in older
|
|
1048
|
+
software and publications.
|
|
1049
|
+
|
|
1050
|
+
The transformation is simply the precession matrix:
|
|
1051
|
+
r_mod = P @ r_gcrf
|
|
1052
|
+
|
|
1053
|
+
See Also
|
|
1054
|
+
--------
|
|
1055
|
+
mod_to_gcrf : Inverse transformation.
|
|
1056
|
+
gcrf_to_tod : Includes nutation for true of date.
|
|
1057
|
+
"""
|
|
1058
|
+
P = precession_matrix_iau76(jd_tt)
|
|
1059
|
+
return P @ r_gcrf
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
def mod_to_gcrf(
|
|
1063
|
+
r_mod: NDArray[np.floating],
|
|
1064
|
+
jd_tt: float,
|
|
1065
|
+
) -> NDArray[np.floating]:
|
|
1066
|
+
"""
|
|
1067
|
+
Transform position from MOD (Mean of Date) to GCRF.
|
|
1068
|
+
|
|
1069
|
+
Parameters
|
|
1070
|
+
----------
|
|
1071
|
+
r_mod : ndarray
|
|
1072
|
+
Position in MOD frame (km), shape (3,).
|
|
1073
|
+
jd_tt : float
|
|
1074
|
+
Julian date in TT.
|
|
1075
|
+
|
|
1076
|
+
Returns
|
|
1077
|
+
-------
|
|
1078
|
+
r_gcrf : ndarray
|
|
1079
|
+
Position in GCRF frame (km), shape (3,).
|
|
1080
|
+
|
|
1081
|
+
See Also
|
|
1082
|
+
--------
|
|
1083
|
+
gcrf_to_mod : Forward transformation.
|
|
1084
|
+
"""
|
|
1085
|
+
P = precession_matrix_iau76(jd_tt)
|
|
1086
|
+
return P.T @ r_mod
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
def gcrf_to_tod(
|
|
1090
|
+
r_gcrf: NDArray[np.floating],
|
|
1091
|
+
jd_tt: float,
|
|
1092
|
+
) -> NDArray[np.floating]:
|
|
1093
|
+
"""
|
|
1094
|
+
Transform position from GCRF to TOD (True of Date).
|
|
1095
|
+
|
|
1096
|
+
TOD is the true equator and true equinox of date frame.
|
|
1097
|
+
This applies both precession and nutation transformations.
|
|
1098
|
+
|
|
1099
|
+
Parameters
|
|
1100
|
+
----------
|
|
1101
|
+
r_gcrf : ndarray
|
|
1102
|
+
Position in GCRF frame (km), shape (3,).
|
|
1103
|
+
jd_tt : float
|
|
1104
|
+
Julian date in TT (Terrestrial Time).
|
|
1105
|
+
|
|
1106
|
+
Returns
|
|
1107
|
+
-------
|
|
1108
|
+
r_tod : ndarray
|
|
1109
|
+
Position in TOD frame (km), shape (3,).
|
|
1110
|
+
|
|
1111
|
+
Notes
|
|
1112
|
+
-----
|
|
1113
|
+
TOD is a legacy frame convention. The transformation is:
|
|
1114
|
+
r_mod = P @ r_gcrf
|
|
1115
|
+
r_tod = N @ r_mod
|
|
1116
|
+
|
|
1117
|
+
where P is the precession matrix and N is the nutation matrix.
|
|
1118
|
+
|
|
1119
|
+
See Also
|
|
1120
|
+
--------
|
|
1121
|
+
tod_to_gcrf : Inverse transformation.
|
|
1122
|
+
gcrf_to_mod : Mean of date (without nutation).
|
|
1123
|
+
"""
|
|
1124
|
+
P = precession_matrix_iau76(jd_tt)
|
|
1125
|
+
N = nutation_matrix(jd_tt)
|
|
1126
|
+
return N @ (P @ r_gcrf)
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
def tod_to_gcrf(
|
|
1130
|
+
r_tod: NDArray[np.floating],
|
|
1131
|
+
jd_tt: float,
|
|
1132
|
+
) -> NDArray[np.floating]:
|
|
1133
|
+
"""
|
|
1134
|
+
Transform position from TOD (True of Date) to GCRF.
|
|
1135
|
+
|
|
1136
|
+
Parameters
|
|
1137
|
+
----------
|
|
1138
|
+
r_tod : ndarray
|
|
1139
|
+
Position in TOD frame (km), shape (3,).
|
|
1140
|
+
jd_tt : float
|
|
1141
|
+
Julian date in TT.
|
|
1142
|
+
|
|
1143
|
+
Returns
|
|
1144
|
+
-------
|
|
1145
|
+
r_gcrf : ndarray
|
|
1146
|
+
Position in GCRF frame (km), shape (3,).
|
|
1147
|
+
|
|
1148
|
+
See Also
|
|
1149
|
+
--------
|
|
1150
|
+
gcrf_to_tod : Forward transformation.
|
|
1151
|
+
"""
|
|
1152
|
+
P = precession_matrix_iau76(jd_tt)
|
|
1153
|
+
N = nutation_matrix(jd_tt)
|
|
1154
|
+
return P.T @ (N.T @ r_tod)
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
def mod_to_tod(
|
|
1158
|
+
r_mod: NDArray[np.floating],
|
|
1159
|
+
jd_tt: float,
|
|
1160
|
+
) -> NDArray[np.floating]:
|
|
1161
|
+
"""
|
|
1162
|
+
Transform position from MOD (Mean of Date) to TOD (True of Date).
|
|
1163
|
+
|
|
1164
|
+
This applies only the nutation transformation.
|
|
1165
|
+
|
|
1166
|
+
Parameters
|
|
1167
|
+
----------
|
|
1168
|
+
r_mod : ndarray
|
|
1169
|
+
Position in MOD frame (km), shape (3,).
|
|
1170
|
+
jd_tt : float
|
|
1171
|
+
Julian date in TT.
|
|
1172
|
+
|
|
1173
|
+
Returns
|
|
1174
|
+
-------
|
|
1175
|
+
r_tod : ndarray
|
|
1176
|
+
Position in TOD frame (km), shape (3,).
|
|
1177
|
+
|
|
1178
|
+
Notes
|
|
1179
|
+
-----
|
|
1180
|
+
The transformation is simply the nutation matrix:
|
|
1181
|
+
r_tod = N @ r_mod
|
|
1182
|
+
|
|
1183
|
+
See Also
|
|
1184
|
+
--------
|
|
1185
|
+
tod_to_mod : Inverse transformation.
|
|
1186
|
+
"""
|
|
1187
|
+
N = nutation_matrix(jd_tt)
|
|
1188
|
+
return N @ r_mod
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
def tod_to_mod(
|
|
1192
|
+
r_tod: NDArray[np.floating],
|
|
1193
|
+
jd_tt: float,
|
|
1194
|
+
) -> NDArray[np.floating]:
|
|
1195
|
+
"""
|
|
1196
|
+
Transform position from TOD (True of Date) to MOD (Mean of Date).
|
|
1197
|
+
|
|
1198
|
+
Parameters
|
|
1199
|
+
----------
|
|
1200
|
+
r_tod : ndarray
|
|
1201
|
+
Position in TOD frame (km), shape (3,).
|
|
1202
|
+
jd_tt : float
|
|
1203
|
+
Julian date in TT.
|
|
1204
|
+
|
|
1205
|
+
Returns
|
|
1206
|
+
-------
|
|
1207
|
+
r_mod : ndarray
|
|
1208
|
+
Position in MOD frame (km), shape (3,).
|
|
1209
|
+
|
|
1210
|
+
See Also
|
|
1211
|
+
--------
|
|
1212
|
+
mod_to_tod : Forward transformation.
|
|
1213
|
+
"""
|
|
1214
|
+
N = nutation_matrix(jd_tt)
|
|
1215
|
+
return N.T @ r_tod
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
def tod_to_itrf(
|
|
1219
|
+
r_tod: NDArray[np.floating],
|
|
1220
|
+
jd_ut1: float,
|
|
1221
|
+
jd_tt: Optional[float] = None,
|
|
1222
|
+
xp: float = 0.0,
|
|
1223
|
+
yp: float = 0.0,
|
|
1224
|
+
) -> NDArray[np.floating]:
|
|
1225
|
+
"""
|
|
1226
|
+
Transform position from TOD (True of Date) to ITRF.
|
|
1227
|
+
|
|
1228
|
+
Parameters
|
|
1229
|
+
----------
|
|
1230
|
+
r_tod : ndarray
|
|
1231
|
+
Position in TOD frame (km), shape (3,).
|
|
1232
|
+
jd_ut1 : float
|
|
1233
|
+
Julian date in UT1.
|
|
1234
|
+
jd_tt : float, optional
|
|
1235
|
+
Julian date in TT. If not provided, assumed equal to jd_ut1.
|
|
1236
|
+
xp : float, optional
|
|
1237
|
+
Polar motion x (radians). Default 0.
|
|
1238
|
+
yp : float, optional
|
|
1239
|
+
Polar motion y (radians). Default 0.
|
|
1240
|
+
|
|
1241
|
+
Returns
|
|
1242
|
+
-------
|
|
1243
|
+
r_itrf : ndarray
|
|
1244
|
+
Position in ITRF frame (km), shape (3,).
|
|
1245
|
+
|
|
1246
|
+
Notes
|
|
1247
|
+
-----
|
|
1248
|
+
The transformation applies the sidereal rotation (using GAST)
|
|
1249
|
+
and polar motion:
|
|
1250
|
+
r_pef = R(GAST) @ r_tod
|
|
1251
|
+
r_itrf = W @ r_pef
|
|
1252
|
+
|
|
1253
|
+
See Also
|
|
1254
|
+
--------
|
|
1255
|
+
itrf_to_tod : Inverse transformation.
|
|
1256
|
+
"""
|
|
1257
|
+
if jd_tt is None:
|
|
1258
|
+
jd_tt = jd_ut1
|
|
1259
|
+
gast = gast_iau82(jd_ut1, jd_tt)
|
|
1260
|
+
R = sidereal_rotation_matrix(gast)
|
|
1261
|
+
W = polar_motion_matrix(xp, yp)
|
|
1262
|
+
return W @ (R @ r_tod)
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
def itrf_to_tod(
|
|
1266
|
+
r_itrf: NDArray[np.floating],
|
|
1267
|
+
jd_ut1: float,
|
|
1268
|
+
jd_tt: Optional[float] = None,
|
|
1269
|
+
xp: float = 0.0,
|
|
1270
|
+
yp: float = 0.0,
|
|
1271
|
+
) -> NDArray[np.floating]:
|
|
1272
|
+
"""
|
|
1273
|
+
Transform position from ITRF to TOD (True of Date).
|
|
1274
|
+
|
|
1275
|
+
Parameters
|
|
1276
|
+
----------
|
|
1277
|
+
r_itrf : ndarray
|
|
1278
|
+
Position in ITRF frame (km), shape (3,).
|
|
1279
|
+
jd_ut1 : float
|
|
1280
|
+
Julian date in UT1.
|
|
1281
|
+
jd_tt : float, optional
|
|
1282
|
+
Julian date in TT. If not provided, assumed equal to jd_ut1.
|
|
1283
|
+
xp : float, optional
|
|
1284
|
+
Polar motion x (radians). Default 0.
|
|
1285
|
+
yp : float, optional
|
|
1286
|
+
Polar motion y (radians). Default 0.
|
|
1287
|
+
|
|
1288
|
+
Returns
|
|
1289
|
+
-------
|
|
1290
|
+
r_tod : ndarray
|
|
1291
|
+
Position in TOD frame (km), shape (3,).
|
|
1292
|
+
|
|
1293
|
+
See Also
|
|
1294
|
+
--------
|
|
1295
|
+
tod_to_itrf : Forward transformation.
|
|
1296
|
+
"""
|
|
1297
|
+
if jd_tt is None:
|
|
1298
|
+
jd_tt = jd_ut1
|
|
1299
|
+
gast = gast_iau82(jd_ut1, jd_tt)
|
|
1300
|
+
R = sidereal_rotation_matrix(gast)
|
|
1301
|
+
W = polar_motion_matrix(xp, yp)
|
|
1302
|
+
return R.T @ (W.T @ r_itrf)
|
|
1303
|
+
|
|
1304
|
+
|
|
692
1305
|
def clear_transformation_cache() -> None:
|
|
693
1306
|
"""Clear cached transformation matrices.
|
|
694
1307
|
|
|
@@ -743,6 +1356,24 @@ __all__ = [
|
|
|
743
1356
|
# Ecliptic/equatorial
|
|
744
1357
|
"ecliptic_to_equatorial",
|
|
745
1358
|
"equatorial_to_ecliptic",
|
|
1359
|
+
# TEME transformations (for SGP4/SDP4)
|
|
1360
|
+
"teme_to_pef",
|
|
1361
|
+
"pef_to_teme",
|
|
1362
|
+
"teme_to_itrf",
|
|
1363
|
+
"itrf_to_teme",
|
|
1364
|
+
"teme_to_gcrf",
|
|
1365
|
+
"gcrf_to_teme",
|
|
1366
|
+
"teme_to_itrf_with_velocity",
|
|
1367
|
+
"itrf_to_teme_with_velocity",
|
|
1368
|
+
# TOD/MOD transformations (legacy conventions)
|
|
1369
|
+
"gcrf_to_mod",
|
|
1370
|
+
"mod_to_gcrf",
|
|
1371
|
+
"gcrf_to_tod",
|
|
1372
|
+
"tod_to_gcrf",
|
|
1373
|
+
"mod_to_tod",
|
|
1374
|
+
"tod_to_mod",
|
|
1375
|
+
"tod_to_itrf",
|
|
1376
|
+
"itrf_to_tod",
|
|
746
1377
|
# Cache management
|
|
747
1378
|
"clear_transformation_cache",
|
|
748
1379
|
"get_cache_info",
|