The Geocentric Celestial Reference Frame (GCRF) and International Terrestrial Reference Frame (ITRF) are the modern IAU/IERS standard reference frames for Earth-orbiting satellite applications.
The Geocentric Celestial Reference Frame is the standard modern inertial reference frame for Earth-orbiting satellites. It is aligned with the International Celestial Reference Frame (ICRF) and realized using the positions of distant quasars. The GCRF has its origin at the Earth's center of mass and its axes are fixed with respect to distant stars.
The GCRF is an Earth-centered inertial (ECI) frame, meaning it does not rotate with the Earth.
The International Terrestrial Reference Frame is the standard Earth-fixed reference frame maintained by the International Earth Rotation and Reference Systems Service (IERS). The ITRF rotates with the Earth and its axes are aligned with the Earth's geographic coordinate system (polar axis and Greenwich meridian).
The ITRF is an Earth-centered Earth-fixed (ECEF) frame, meaning it rotates with the Earth.
Brahe implements the IAU 2006/2000A precession-nutation model with Celestial Intermediate Origin (CIO) based transformation, following IERS conventions. The transformation is accomplished using the IAU 2006/2000A, CIO-based theory using classical angles. The method as described in section 5.5 of the SOFA C transformation cookbook. The transformation accounts for:
Precession and nutation of Earth's rotation axis
Earth's rotation about its instantaneous spin axis
Polar motion and UT1-UTC corrections
These transformations are time-dependent and require Earth Orientation Parameters (EOP) for high accuracy. The transformations will use the currently loaded Earth orientation data provider to obtain the necessary parameters automatically. See Earth Orientation Data for more details.
importbraheasbhimportnumpyasnpbh.initialize_eop()# Define orbital elements in degrees# LEO satellite: 500 km altitude, sun-synchronous orbitoe=np.array([bh.R_EARTH+500e3,# Semi-major axis (m)0.01,# Eccentricity97.8,# Inclination (deg)15.0,# Right ascension of ascending node (deg)30.0,# Argument of periapsis (deg)45.0,# Mean anomaly (deg)])print("Orbital elements (degrees):")print(f" a = {oe[0]:.3f} m = {(oe[0]-bh.R_EARTH)/1e3:.1f} km altitude")print(f" e = {oe[1]:.4f}")print(f" i = {oe[2]:.4f}°")print(f" Ω = {oe[3]:.4f}°")print(f" ω = {oe[4]:.4f}°")print(f" M = {oe[5]:.4f}°\n")# Orbital elements (degrees):# a = 6878136.300 m = 500.0 km altitude# e = 0.0100# i = 97.8000°# Ω = 15.0000°# ω = 30.0000°# M = 45.0000°epc=bh.Epoch(2024,1,1,12,0,0.0,time_system=bh.UTC)print(f"Epoch: {epc}")# Epoch: 2024-01-01 12:00:00.000 UTC# Convert to GCRF Cartesian statestate_gcrf=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)print("GCRF state vector:")print(f" Position: [{state_gcrf[0]:.3f}, {state_gcrf[1]:.3f}, {state_gcrf[2]:.3f}] m")print(f" Velocity: [{state_gcrf[3]:.6f}, {state_gcrf[4]:.6f}, {state_gcrf[5]:.6f}] m/s\n")# GCRF state vector:# Position: [1848964.106, -434937.468, 6560410.530] m# Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s# Transform to ITRF at specific epochstate_itrf=bh.state_gcrf_to_itrf(epc,state_gcrf)print("\nITRF state vector:")print(f" Position: [{state_itrf[0]:.3f}, {state_itrf[1]:.3f}, {state_itrf[2]:.3f}] m")print(f" Velocity: [{state_itrf[3]:.6f}, {state_itrf[4]:.6f}, {state_itrf[5]:.6f}] m/s")# ITRF state vector:# Position: [757164.267, 1725863.563, 6564672.302] m# Velocity: [989.350643, -7432.740021, 1896.768934] m/s
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define orbital elements in degrees// LEO satellite: 500 km altitude, sun-synchronous orbitletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,// Semi-major axis (m)0.01,// Eccentricity97.8,// Inclination (deg)15.0,// Right ascension of ascending node (deg)30.0,// Argument of periapsis (deg)45.0,// Mean anomaly (deg));println!("Orbital elements (degrees):");println!(" a = {:.3} m = {:.1} km altitude",oe[0],(oe[0]-bh::R_EARTH)/1e3);println!(" e = {:.4}",oe[1]);println!(" i = {:.4}°",oe[2]);println!(" Ω = {:.4}°",oe[3]);println!(" ω = {:.4}°",oe[4]);println!(" M = {:.4}°\n",oe[5]);// Orbital elements (degrees):// a = 6878136.300 m = 500.0 km altitude// e = 0.0100// i = 97.8000°// Ω = 15.0000°// ω = 30.0000°// M = 45.0000°letepc=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);println!("Epoch: {}",epc);// Epoch: 2024-01-01 12:00:00.000 UTC// Convert to GCRF Cartesian stateletstate_gcrf=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);println!("GCRF state vector:");println!(" Position: [{:.3}, {:.3}, {:.3}] m",state_gcrf[0],state_gcrf[1],state_gcrf[2]);println!(" Velocity: [{:.6}, {:.6}, {:.6}] m/s\n",state_gcrf[3],state_gcrf[4],state_gcrf[5]);// GCRF state vector:// Position: [1848964.106, -434937.468, 6560410.530] m// Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s// Transform to ITRF at specific epochletstate_itrf=bh::state_gcrf_to_itrf(epc,state_gcrf);println!("\nITRF state vector:");println!(" Position: [{:.3}, {:.3}, {:.3}] m",state_itrf[0],state_itrf[1],state_itrf[2]);println!(" Velocity: [{:.6}, {:.6}, {:.6}] m/s",state_itrf[3],state_itrf[4],state_itrf[5]);// ITRF state vector:// Position: [757164.267, 1725863.563, 6564672.302] m// Velocity: [989.350643, -7432.740021, 1896.768934] m/s}
Velocity Transformation
Simply rotating velocity vectors will not yield correct velocity components in the ITRF frame due to the Earth's rotation. State vector transformation functions properly account for observed velocity changes in the ITRF frame due to Earth's rotation.
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define orbital elements in degrees// LEO satellite: 500 km altitude, sun-synchronous orbitletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,// Semi-major axis (m)0.01,// Eccentricity97.8,// Inclination (deg)15.0,// Right ascension of ascending node (deg)30.0,// Argument of periapsis (deg)45.0,// Mean anomaly (deg));println!("Orbital elements (degrees):");println!(" a = {:.3} m = {:.1} km altitude",oe[0],(oe[0]-bh::R_EARTH)/1e3);println!(" e = {:.4}",oe[1]);println!(" i = {:.4}°",oe[2]);println!(" Ω = {:.4}°",oe[3]);println!(" ω = {:.4}°",oe[4]);println!(" M = {:.4}°\n",oe[5]);// Convert to GCRF Cartesian stateletstate_gcrf=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);// Define epochletepc=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);println!("Epoch: {}",epc);println!("GCRF state vector:");println!(" Position: [{:.3}, {:.3}, {:.3}] m",state_gcrf[0],state_gcrf[1],state_gcrf[2]);println!(" Velocity: [{:.6}, {:.6}, {:.6}] m/s\n",state_gcrf[3],state_gcrf[4],state_gcrf[5]);// Position: [1848964.106, -434937.468, 6560410.530] m// Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s// Transform to ITRFletstate_itrf=bh::state_gcrf_to_itrf(epc,state_gcrf);println!("ITRF state vector:");println!(" Position: [{:.3}, {:.3}, {:.3}] m",state_itrf[0],state_itrf[1],state_itrf[2]);println!(" Velocity: [{:.6}, {:.6}, {:.6}] m/s\n",state_itrf[3],state_itrf[4],state_itrf[5]);// Position: [757164.267, 1725863.563, 6564672.302] m// Velocity: [989.350643, -7432.740021, 1896.768934] m/s// Transform back to GCRFletstate_gcrf_back=bh::state_itrf_to_gcrf(epc,state_itrf);println!("\nGCRF state vector (transformed from ITRF):");println!(" Position: [{:.3}, {:.3}, {:.3}] m",state_gcrf_back[0],state_gcrf_back[1],state_gcrf_back[2]);println!(" Velocity: [{:.6}, {:.6}, {:.6}] m/s",state_gcrf_back[3],state_gcrf_back[4],state_gcrf_back[5]);// Position: [1848964.106, -434937.468, 6560410.530] m// Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s// Verify round-trip transformationletdiff_pos=(na::Vector3::new(state_gcrf[0],state_gcrf[1],state_gcrf[2])-na::Vector3::new(state_gcrf_back[0],state_gcrf_back[1],state_gcrf_back[2])).norm();letdiff_vel=(na::Vector3::new(state_gcrf[3],state_gcrf[4],state_gcrf[5])-na::Vector3::new(state_gcrf_back[3],state_gcrf_back[4],state_gcrf_back[5])).norm();println!("\nRound-trip error:");println!(" Position: {:.6e} m",diff_pos);println!(" Velocity: {:.6e} m/s",diff_vel);// Expected output:// Position: 9.617484e-10 m// Velocity: 9.094947e-13 m/s}
Velocity Transformation
Simply rotating velocity vectors will not yield correct velocity components in the GCRF frame due to the Earth's rotation. State vector transformation functions properly account for observed velocity changes when transforming from the rotating ITRF frame.
importbraheasbhimportnumpyasnpbh.initialize_eop()# Define epochepc=bh.Epoch(2024,1,1,12,0,0.0,time_system=bh.UTC)# Get rotation matrix from ITRF to GCRFR_itrf_to_gcrf=bh.rotation_itrf_to_gcrf(epc)print(f"Epoch: {epc.to_datetime()}")print("\nITRF to GCRF rotation matrix:")print(f" [{R_itrf_to_gcrf[0,0]:10.7f}, {R_itrf_to_gcrf[0,1]:10.7f}, {R_itrf_to_gcrf[0,2]:10.7f}]")print(f" [{R_itrf_to_gcrf[1,0]:10.7f}, {R_itrf_to_gcrf[1,1]:10.7f}, {R_itrf_to_gcrf[1,2]:10.7f}]")print(f" [{R_itrf_to_gcrf[2,0]:10.7f}, {R_itrf_to_gcrf[2,1]:10.7f}, {R_itrf_to_gcrf[2,2]:10.7f}]\n")# [ 0.1794538, 0.9837637, 0.0023225]# [-0.9837663, 0.1794542, 0.0000338]# [-0.0003836, -0.0022908, 0.9999973]# Verify it's the transpose of GCRF to ITRF rotationR_gcrf_to_itrf=bh.rotation_gcrf_to_itrf(epc)print("Verification: R_itrf_to_gcrf = R_gcrf_to_itrf^T")print(f" Max difference: {np.max(np.abs(R_itrf_to_gcrf-R_gcrf_to_itrf.T)):.2e}\n")# Max difference: 0.00e0# Define orbital elements in degrees for satellite positionoe=np.array([bh.R_EARTH+500e3,# Semi-major axis (m)0.01,# Eccentricity97.8,# Inclination (deg)15.0,# RAAN (deg)30.0,# Argument of periapsis (deg)45.0,# Mean anomaly (deg)])# Convert to GCRF Cartesian state and extract positionstate_gcrf=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)# Transform to ITRFpos_itrf=bh.position_gcrf_to_itrf(epc,state_gcrf[0:3])print("Satellite position in ITRF:")print(f" [{pos_itrf[0]:.3f}, {pos_itrf[1]:.3f}, {pos_itrf[2]:.3f}] m\n")# [757164.267, 1725863.563, 6564672.302] m# Transform back to GCRF using rotation matrixpos_gcrf=R_itrf_to_gcrf@pos_itrfprint("Satellite position in GCRF (using rotation matrix):")print(f" [{pos_gcrf[0]:.3f}, {pos_gcrf[1]:.3f}, {pos_gcrf[2]:.3f}] m")# [1848964.106, -434937.468, 6560410.530] m# Verify using position transformation functionpos_gcrf_direct=bh.position_itrf_to_gcrf(epc,pos_itrf)print("\nSatellite position in GCRF (using position_itrf_to_gcrf):")print(f" [{pos_gcrf_direct[0]:.3f}, {pos_gcrf_direct[1]:.3f}, {pos_gcrf_direct[2]:.3f}] m")# [1848964.106, -434937.468, 6560410.530] m
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define epochletepc=bh::Epoch::from_datetime(2024,1,1,0,0,0.0,0.0,bh::TimeSystem::UTC);letepc=epc+12.0*3600.0;// Add 12 hours// Get rotation matrix from ITRF to GCRFletr_itrf_to_gcrf=bh::rotation_itrf_to_gcrf(epc);println!("Epoch: 2024-01-01 12:00:00 UTC");println!("\nITRF to GCRF rotation matrix:");println!(" [{:10.7}, {:10.7}, {:10.7}]",r_itrf_to_gcrf[(0,0)],r_itrf_to_gcrf[(0,1)],r_itrf_to_gcrf[(0,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]",r_itrf_to_gcrf[(1,0)],r_itrf_to_gcrf[(1,1)],r_itrf_to_gcrf[(1,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]\n",r_itrf_to_gcrf[(2,0)],r_itrf_to_gcrf[(2,1)],r_itrf_to_gcrf[(2,2)]);// [ 0.1794538, 0.9837637, 0.0023225]// [-0.9837663, 0.1794542, 0.0000338]// [-0.0003836, -0.0022908, 0.9999973]// Verify it's the transpose of GCRF to ITRF rotationletr_gcrf_to_itrf=bh::rotation_gcrf_to_itrf(epc);letdiff=(r_itrf_to_gcrf-r_gcrf_to_itrf.transpose()).abs();letmax_diff=diff.max();println!("Verification: R_itrf_to_gcrf = R_gcrf_to_itrf^T");println!(" Max difference: {:.2e}\n",max_diff);// Max difference: 0.00e+00// Define orbital elements in degrees for satellite positionletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,// Semi-major axis (m)0.01,// Eccentricity97.8,// Inclination (deg)15.0,// RAAN (deg)30.0,// Argument of periapsis (deg)45.0,// Mean anomaly (deg));// Convert to GCRF Cartesian state and extract positionletstate_gcrf=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);letpos_gcrf_orig=na::Vector3::new(state_gcrf[0],state_gcrf[1],state_gcrf[2]);// Transform to ITRFletpos_itrf=bh::position_gcrf_to_itrf(epc,pos_gcrf_orig);println!("Satellite position in ITRF:");println!(" [{:.3}, {:.3}, {:.3}] m\n",pos_itrf[0],pos_itrf[1],pos_itrf[2]);// [757164.267, 1725863.563, 6564672.302] m// Transform back to GCRF using rotation matrixletpos_gcrf=r_itrf_to_gcrf*pos_itrf;println!("Satellite position in GCRF (using rotation matrix):");println!(" [{:.3}, {:.3}, {:.3}] m",pos_gcrf[0],pos_gcrf[1],pos_gcrf[2]);// [1848964.106, -434937.468, 6560410.530] m// Verify using position transformation functionletpos_gcrf_direct=bh::position_itrf_to_gcrf(epc,pos_itrf);println!("\nSatellite position in GCRF (using position_itrf_to_gcrf):");println!(" [{:.3}, {:.3}, {:.3}] m",pos_gcrf_direct[0],pos_gcrf_direct[1],pos_gcrf_direct[2]);// [1848964.106, -434937.468, 6560410.530] m}
The full GCRF to ITRF transformation is composed of three sequential rotations. Brahe provides access to these intermediate rotation matrices for advanced applications or for understanding the transformation components.
importbraheasbhimportnumpyasnpbh.initialize_eop()# Define epochepc=bh.Epoch(2024,1,1,12,0,0.0,time_system=bh.UTC)# Get BPN matrix (GCRF to CIRS transformation)R_bpn=bh.bias_precession_nutation(epc)print(f"Epoch: {epc.to_datetime()}")print("\nBias-Precession-Nutation (BPN) matrix:")print("Transforms from GCRF to CIRS")print(f" [{R_bpn[0,0]:10.7f}, {R_bpn[0,1]:10.7f}, {R_bpn[0,2]:10.7f}]")print(f" [{R_bpn[1,0]:10.7f}, {R_bpn[1,1]:10.7f}, {R_bpn[1,2]:10.7f}]")print(f" [{R_bpn[2,0]:10.7f}, {R_bpn[2,1]:10.7f}, {R_bpn[2,2]:10.7f}]\n")# [ 0.9999973, 0.0000000, -0.0023216]# [-0.0000001, 1.0000000, -0.0000329]# [ 0.0023216, 0.0000329, 0.9999973]# Define orbital elements in degreesoe=np.array([bh.R_EARTH+500e3,# Semi-major axis (m)0.01,# Eccentricity97.8,# Inclination (deg)15.0,# RAAN (deg)30.0,# Argument of periapsis (deg)45.0,# Mean anomaly (deg)])# Convert to GCRF (ECI) positionstate_gcrf=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)pos_gcrf=state_gcrf[0:3]print("Satellite position in GCRF:")print(f" [{pos_gcrf[0]:.3f}, {pos_gcrf[1]:.3f}, {pos_gcrf[2]:.3f}] m\n")# [1848964.106, -434937.468, 6560410.530] m# Transform to CIRS using BPN matrixpos_cirs=R_bpn@pos_gcrfprint("Satellite position in CIRS:")print(f" [{pos_cirs[0]:.3f}, {pos_cirs[1]:.3f}, {pos_cirs[2]:.3f}] m")# [1833728.342, -435153.781, 6564671.107] m# Calculate the magnitude of the changediff=np.linalg.norm(pos_gcrf-pos_cirs)print(f"\nPosition change magnitude: {diff:.3f} m")# Position change magnitude: 15821.751 m
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define epochletepc=bh::Epoch::from_datetime(2024,1,1,0,0,0.0,0.0,bh::TimeSystem::UTC);letepc=epc+12.0*3600.0;// Add 12 hours// Get BPN matrix (GCRF to CIRS transformation)letr_bpn=bh::bias_precession_nutation(epc);println!("Epoch: 2024-01-01 12:00:00 UTC");println!("\nBias-Precession-Nutation (BPN) matrix:");println!("Transforms from GCRF to CIRS");println!(" [{:10.7}, {:10.7}, {:10.7}]",r_bpn[(0,0)],r_bpn[(0,1)],r_bpn[(0,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]",r_bpn[(1,0)],r_bpn[(1,1)],r_bpn[(1,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]\n",r_bpn[(2,0)],r_bpn[(2,1)],r_bpn[(2,2)]);// [ 0.9999973, 0.0000000, -0.0023216]// [-0.0000001, 1.0000000, -0.0000329]// [ 0.0023216, 0.0000329, 0.9999973]// Define orbital elements in degreesletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,// Semi-major axis (m)0.01,// Eccentricity97.8,// Inclination (deg)15.0,// RAAN (deg)30.0,// Argument of periapsis (deg)45.0,// Mean anomaly (deg));// Convert to GCRF (ECI) positionletstate_gcrf=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);letpos_gcrf=na::Vector3::new(state_gcrf[0],state_gcrf[1],state_gcrf[2]);println!("Satellite position in GCRF:");println!(" [{:.3}, {:.3}, {:.3}] m\n",pos_gcrf[0],pos_gcrf[1],pos_gcrf[2]);// [1848964.106, -434937.468, 6560410.530] m// Transform to CIRS using BPN matrixletpos_cirs=r_bpn*pos_gcrf;println!("Satellite position in CIRS:");println!(" [{:.3}, {:.3}, {:.3}] m",pos_cirs[0],pos_cirs[1],pos_cirs[2]);// [1833728.342, -435153.781, 6564671.107] m// Calculate the magnitude of the changeletdiff=(pos_gcrf-pos_cirs).norm();println!("\nPosition change magnitude: {:.3} m",diff);println!("Note: BPN effects are typically meters to tens of meters");// Position change magnitude: 15821.751 m}
importbraheasbhimportnumpyasnpbh.initialize_eop()# Define epochepc=bh.Epoch(2024,1,1,12,0,0.0,time_system=bh.UTC)# Get Earth rotation matrix (CIRS to TIRS transformation)R_er=bh.earth_rotation(epc)print(f"Epoch: {epc.to_datetime()}")print("\nEarth Rotation matrix:")print("Transforms from CIRS to TIRS")print(f" [{R_er[0,0]:10.7f}, {R_er[0,1]:10.7f}, {R_er[0,2]:10.7f}]")print(f" [{R_er[1,0]:10.7f}, {R_er[1,1]:10.7f}, {R_er[1,2]:10.7f}]")print(f" [{R_er[2,0]:10.7f}, {R_er[2,1]:10.7f}, {R_er[2,2]:10.7f}]\n")# [ 0.1794542, -0.9837663, 0.0000000]# [ 0.9837663, 0.1794542, 0.0000000]# [ 0.0000000, 0.0000000, 1.0000000]# Define orbital elements in degreesoe=np.array([bh.R_EARTH+500e3,# Semi-major axis (m)0.01,# Eccentricity97.8,# Inclination (deg)15.0,# RAAN (deg)30.0,# Argument of periapsis (deg)45.0,# Mean anomaly (deg)])# Convert to GCRF and then to CIRSstate_gcrf=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)pos_gcrf=state_gcrf[0:3]R_bpn=bh.bias_precession_nutation(epc)pos_cirs=R_bpn@pos_gcrfprint("Satellite position in CIRS:")print(f" [{pos_cirs[0]:.3f}, {pos_cirs[1]:.3f}, {pos_cirs[2]:.3f}] m\n")# [1833728.342, -435153.781, 6564671.107] m# Apply Earth rotation to get TIRSpos_tirs=R_er@pos_cirsprint("Satellite position in TIRS:")print(f" [{pos_tirs[0]:.3f}, {pos_tirs[1]:.3f}, {pos_tirs[2]:.3f}] m")# [757159.942, 1725870.003, 6564671.107] m# Calculate the magnitude of the changediff=np.linalg.norm(pos_cirs-pos_tirs)print(f"\nPosition change magnitude: {diff:.3f} m")print("Note: Earth rotation causes large position changes (km scale)")print(f" due to ~{np.degrees(bh.OMEGA_EARTH*3600):.3f}° rotation per hour")# Position change magnitude: 2414337.034 m
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define epochletepc=bh::Epoch::from_datetime(2024,1,1,0,0,0.0,0.0,bh::TimeSystem::UTC);letepc=epc+12.0*3600.0;// Add 12 hours// Get Earth rotation matrix (CIRS to TIRS transformation)letr_er=bh::earth_rotation(epc);println!("Epoch: 2024-01-01 12:00:00 UTC");println!("\nEarth Rotation matrix:");println!("Transforms from CIRS to TIRS");println!(" [{:10.7}, {:10.7}, {:10.7}]",r_er[(0,0)],r_er[(0,1)],r_er[(0,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]",r_er[(1,0)],r_er[(1,1)],r_er[(1,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]\n",r_er[(2,0)],r_er[(2,1)],r_er[(2,2)]);// [ 0.1794542, -0.9837663, 0.0000000]// [ 0.9837663, 0.1794542, 0.0000000]// [ 0.0000000, 0.0000000, 1.0000000]// Define orbital elements in degreesletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,// Semi-major axis (m)0.01,// Eccentricity97.8,// Inclination (deg)15.0,// RAAN (deg)30.0,// Argument of periapsis (deg)45.0,// Mean anomaly (deg));// Convert to GCRF and then to CIRSletstate_gcrf=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);letpos_gcrf=na::Vector3::new(state_gcrf[0],state_gcrf[1],state_gcrf[2]);letr_bpn=bh::bias_precession_nutation(epc);letpos_cirs=r_bpn*pos_gcrf;println!("Satellite position in CIRS:");println!(" [{:.3}, {:.3}, {:.3}] m\n",pos_cirs[0],pos_cirs[1],pos_cirs[2]);// [1833728.342, -435153.781, 6564671.107] m// Apply Earth rotation to get TIRSletpos_tirs=r_er*pos_cirs;println!("Satellite position in TIRS:");println!(" [{:.3}, {:.3}, {:.3}] m",pos_tirs[0],pos_tirs[1],pos_tirs[2]);// [757159.942, 1725870.003, 6564671.107] m// Calculate the magnitude of the changeletdiff=(pos_cirs-pos_tirs).norm();println!("\nPosition change magnitude: {:.3} m",diff);println!("Note: Earth rotation causes large position changes (km scale)");println!(" due to ~{:.3}° rotation per hour",(bh::OMEGA_EARTH*3600.0).to_degrees());// Position change magnitude: 2414337.034 m}
importbraheasbhimportnumpyasnpbh.initialize_eop()# Define epochepc=bh.Epoch(2024,1,1,12,0,0.0,time_system=bh.UTC)# Get polar motion matrix (TIRS to ITRF transformation)R_pm=bh.polar_motion(epc)print(f"Epoch: {epc.to_datetime()}")print("\nPolar Motion matrix:")print("Transforms from TIRS to ITRF")print(f" [{R_pm[0,0]:10.7f}, {R_pm[0,1]:10.7f}, {R_pm[0,2]:10.7f}]")print(f" [{R_pm[1,0]:10.7f}, {R_pm[1,1]:10.7f}, {R_pm[1,2]:10.7f}]")print(f" [{R_pm[2,0]:10.7f}, {R_pm[2,1]:10.7f}, {R_pm[2,2]:10.7f}]\n")# [ 1.0000000, -0.0000000, 0.0000007]# [ 0.0000000, 1.0000000, -0.0000010]# [-0.0000007, 0.0000010, 1.0000000]# Define orbital elements in degreesoe=np.array([bh.R_EARTH+500e3,# Semi-major axis (m)0.01,# Eccentricity97.8,# Inclination (deg)15.0,# RAAN (deg)30.0,# Argument of periapsis (deg)45.0,# Mean anomaly (deg)])# Convert through the full chain: GCRF → CIRS → TIRSstate_gcrf=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)pos_gcrf=state_gcrf[0:3]R_bpn=bh.bias_precession_nutation(epc)R_er=bh.earth_rotation(epc)pos_tirs=R_er@R_bpn@pos_gcrfprint("Satellite position in TIRS:")print(f" [{pos_tirs[0]:.3f}, {pos_tirs[1]:.3f}, {pos_tirs[2]:.3f}] m\n")# [757159.942, 1725870.003, 6564671.107] m# Apply polar motion to get ITRFpos_itrf=R_pm@pos_tirsprint("Satellite position in ITRF:")print(f" [{pos_itrf[0]:.3f}, {pos_itrf[1]:.3f}, {pos_itrf[2]:.3f}] m")# [757164.267, 1725863.563, 6564672.302] m# Calculate the magnitude of the changediff=np.linalg.norm(pos_tirs-pos_itrf)print(f"\nPosition change magnitude: {diff:.3f} m")print("Note: Polar motion effects are typically centimeters to meters")# Position change magnitude: 7.849 m# Verify against full transformationpos_itrf_direct=bh.position_gcrf_to_itrf(epc,pos_gcrf)print("\nVerification using position_gcrf_to_itrf:")print(f" [{pos_itrf_direct[0]:.3f}, {pos_itrf_direct[1]:.3f}, {pos_itrf_direct[2]:.3f}] m")print(f" Max difference: {np.max(np.abs(pos_itrf-pos_itrf_direct)):.2e} m")# [757164.267, 1725863.563, 6564672.302] m# Max difference: 1.16e-10 m
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define epochletepc=bh::Epoch::from_datetime(2024,1,1,0,0,0.0,0.0,bh::TimeSystem::UTC);letepc=epc+12.0*3600.0;// Add 12 hours// Get polar motion matrix (TIRS to ITRF transformation)letr_pm=bh::polar_motion(epc);println!("Epoch: 2024-01-01 12:00:00 UTC");println!("\nPolar Motion matrix:");println!("Transforms from TIRS to ITRF");println!(" [{:10.7}, {:10.7}, {:10.7}]",r_pm[(0,0)],r_pm[(0,1)],r_pm[(0,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]",r_pm[(1,0)],r_pm[(1,1)],r_pm[(1,2)]);println!(" [{:10.7}, {:10.7}, {:10.7}]\n",r_pm[(2,0)],r_pm[(2,1)],r_pm[(2,2)]);// [ 1.0000000, -0.0000000, 0.0000007]// [ 0.0000000, 1.0000000, -0.0000010]// [-0.0000007, 0.0000010, 1.0000000]// Define orbital elements in degreesletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,// Semi-major axis (m)0.01,// Eccentricity97.8,// Inclination (deg)15.0,// RAAN (deg)30.0,// Argument of periapsis (deg)45.0,// Mean anomaly (deg));// Convert through the full chain: GCRF → CIRS → TIRSletstate_gcrf=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);letpos_gcrf=na::Vector3::new(state_gcrf[0],state_gcrf[1],state_gcrf[2]);letr_bpn=bh::bias_precession_nutation(epc);letr_er=bh::earth_rotation(epc);letpos_tirs=r_er*r_bpn*pos_gcrf;println!("Satellite position in TIRS:");println!(" [{:.3}, {:.3}, {:.3}] m\n",pos_tirs[0],pos_tirs[1],pos_tirs[2]);// [757159.942, 1725870.003, 6564671.107] m// Apply polar motion to get ITRFletpos_itrf=r_pm*pos_tirs;println!("Satellite position in ITRF:");println!(" [{:.3}, {:.3}, {:.3}] m",pos_itrf[0],pos_itrf[1],pos_itrf[2]);// [757164.267, 1725863.563, 6564672.302] m// Calculate the magnitude of the changeletdiff=(pos_tirs-pos_itrf).norm();println!("\nPosition change magnitude: {:.3} m",diff);println!("Note: Polar motion effects are typically centimeters to meters");// Position change magnitude: 7.849 m// Verify against full transformationletpos_itrf_direct=bh::position_gcrf_to_itrf(epc,pos_gcrf);println!("\nVerification using position_gcrf_to_itrf:");println!(" [{:.3}, {:.3}, {:.3}] m",pos_itrf_direct[0],pos_itrf_direct[1],pos_itrf_direct[2]);letmax_diff=(pos_itrf-pos_itrf_direct).abs().max();println!(" Max difference: {:.2e} m",max_diff);// [757164.267, 1725863.563, 6564672.302] m// Max difference: 1.16e-10 m}
Note
For most applications, use the combined rotation_gcrf_to_itrf or state_gcrf_to_itrf functions rather than computing intermediate matrices separately. The intermediate matrices are provided for educational purposes and specialized applications.