BodyOrientation#

class BodyOrientation(rotation_vector: ndarray | None = None, body_fixed_frame: str | None = None, rotation: Callable | None = None, frame_class_id: int = 3000, base_frame: str = 'J2000', interpolation_frame: str = 'ECLIPJ2000', max_interval: float = 21600.0, description: str = 'User-supplied absolute body orientation', pole_x: float | None = None, pole_y: float | None = None, tai_minus_ut1: float | None = None, model: str = 'EOP1', celestial_pole_dx: float = 0.0, celestial_pole_dy: float = 0.0, nutation_delta_psi: float = 0.0, nutation_delta_epsilon: float = 0.0, ut1_time_scale: str = 'TAI')#

Bases: object

A body-frame correction or an absolute planetary orientation model.

Use the constructor for a small active rotation, earth_correction() for Earth-convention deltas, earth_eop() for absolute Earth inputs, and from_rotation() for another body’s absolute orientation law. An instance has exactly one of these modes. dataclasses.replace makes an independent instance with revised inputs, e.g. for an EOP comparison.

Parameters:
rotation_vectorarray_like, shape (3,), optional

Active right-hand body-fixed X/Y/Z correction in radians. Surface displacement is \(\delta r = \varepsilon \times r\) to first order.

body_fixed_framestr, optional

SPICE axes of the correction. Required for a correction instance.

rotationcallable, optional

Absolute inertial-to-body rotation callback; see from_rotation().

frame_class_idint, optional

Binary PCK class ID; Earth ITRF93 uses 3000. See from_rotation().

base_frame, interpolation_framestr, optional

Inertial callback axes and PCK interpolation axes. Defaults to J2000 and ECLIPJ2000 respectively.

max_intervalfloat, optional

Maximum Chebyshev record length, seconds; default 21600.

descriptionstr, optional

Provenance for a user-supplied rotation law.

pole_x, pole_y, tai_minus_ut1, modeloptional

Absolute Earth EOPs and convention; see earth_eop().

ut1_time_scalestr, optional

Which time scale UT1 is formed from; see earth_eop().

celestial_pole_dx, celestial_pole_dyfloat, optional

Modern CPOs in radians; see earth_eop().

nutation_delta_psi, nutation_delta_epsilonfloat, optional

Legacy nutation corrections in radians; see earth_eop().

Attributes:
body_fixed_frame
convention

The complete convention for report labels and PCK provenance.

eop_option

Selected Earth convention: EOP1 (ITRF93), EOP2 (IERS2010), or None.

pole_x
pole_y
rotation
rotation_vector
tai_minus_ut1

Methods

celestial_pole_correction([delta_x, delta_y])

Return the J2000 rotation vector of a celestial-pole-offset correction.

celestial_pole_correction_partials()

Return the rotation-vector column of each celestial pole offset.

displacement(position_body_fixed[, epoch, ...])

Displacement of a body-fixed point caused by the correction.

earth_correction([delta_pole_x, ...])

Construct a small Earth correction relative to the nominal kernel.

earth_correction_partials()

Return the rotation-vector column of each Earth EOP delta.

earth_eop(pole_x, pole_y, tai_minus_ut1[, ...])

Construct absolute, constant Earth EOPs independently of a loaded PCK.

from_rotation(rotation, frame_class_id, *[, ...])

Construct an absolute rotation law for any planet or other body.

iau_angles(epoch[, frame, coefficients, ...])

Return the IAU pole and prime-meridian angles of a body and their rates.

iau_matrix(angles)

Return the body-fixed to J2000 rotation of IAU pole angles.

iau_pole_coefficients(body)

Return a body's secular IAU rotation constants from the kernel pool.

rotation_components(epoch)

Return (W, R, Q), with celestial -> terrestrial = W @ R @ Q.

rotation_matrix(epoch)

Return celestial -> terrestrial rotation at TDB seconds past J2000.

state_displacement(position_body_fixed, epoch)

Position and velocity displacement of a body-fixed point.

write_pck(path, start, stop)

Write a new type-2 PCK for this model's frame class ID.

Notes

Corrections affect station measurement geometry, including velocity, but do not replace loaded kernels or alter gravity. An absolute model can write a PCK shared by all SPICE frame consumers. A leap-second kernel is needed for absolute Earth models; generic callbacks define their own time dependence using TDB seconds past J2000. UT1 is an Earth convention.

See orientation-station-estimation for EOP signs, PCK precedence, planetary frames, cylindrical coordinates and complete estimation examples.

classmethod earth_correction(delta_pole_x=0.0, delta_pole_y=0.0, delta_ut1=0.0)#

Construct a small Earth correction relative to the nominal kernel.

Parameters:
delta_pole_x, delta_pole_yfloat, optional

Intended minus nominal terrestrial xp/yp, radians. The active body-fixed rotation is (-delta_yp, -delta_xp, omega*delta_ut1). Defaults to 0..

delta_ut1float, optional

Intended minus nominal UT1, seconds. Positive advances Earth spin. Defaults to 0..

Returns:
BodyOrientation

First-order station correction in ITRF93. Absolute EOP numbers must not be passed here; use earth_eop() for those.

Raises:
ValueError

If any of the three deltas is not finite.

classmethod earth_eop(pole_x, pole_y, tai_minus_ut1, model='EOP1', *, celestial_pole_dx=0.0, celestial_pole_dy=0.0, nutation_delta_psi=0.0, nutation_delta_epsilon=0.0, ut1_time_scale='TAI')#

Construct absolute, constant Earth EOPs independently of a loaded PCK.

Parameters:
pole_x, pole_yfloat

Absolute terrestrial polar-motion coordinates in radians.

tai_minus_ut1float

Absolute TAI-UT1 in seconds: UT1 = TAI - tai_minus_ut1.

modelstr, optional

EOP1: ITRF93-style legacy IAU1976/1980 orientation, equinox/GMST82 + (dpsi+dPsi)cos(eps), s’=0. EOP2: IERS2010 orientation, IAU2006/2000A CIO/ERA with s06 and sp00. The descriptive names ITRF93, IERS2010, IAU1976/1980 and IAU2006/2000A are also accepted. The three EOP numbers do not determine this convention. Defaults to "EOP1".

celestial_pole_dx, celestial_pole_dyfloat, optional

Modern observed celestial pole offsets dX/dY, radians. Supported only for IAU2006/2000A; distinct from terrestrial xp/yp. Defaults to 0..

nutation_delta_psi, nutation_delta_epsilonfloat, optional

IAU1980 nutation longitude/obliquity corrections, radians. Supported only for IAU1976/1980. dPsi also enters the equation of equinoxes. Defaults to 0..

ut1_time_scalestr, optional

Which time scale UT1 is formed from. Defaults to "TAI", which is correct: UT1 = TAI - tai_minus_ut1, with TAI reached from the ephemeris epoch through the full TDB->TT->TAI chain. "TDB" instead treats the ephemeris epoch as if it were TT, so UT1 = TDB - 32.184 - tai_minus_ut1 and the TDB-TT periodic term never reaches the spin angle.

Choose "TDB" only to reproduce another tool that omits that conversion. It is not a refinement and it is not small: TDB-TT is periodic with an amplitude of 1.66 ms, which is 0.5 m of station motion at the equator, well above the noise of a metre-class ranging system. It varies annually, so the error it causes differs between arcs at different times of year.

Returns:
BodyOrientation

Absolute model supporting rotation_components, rotation_matrix and write_pck. The PCK uses ITRF93 class ID 3000.

Raises:
ValueError

If model or ut1_time_scale is not recognised, an input is not finite, celestial pole offsets are given with IAU1976/1980, or nutation corrections are given with IAU2006/2000A.

Notes

EOPs/CPOs stay constant over coverage; model precession/nutation and spin vary with time. ERFA supplies SOFA-derived routines. Precession and nutation use TT; spin uses UT1. CPOs default to zero and are never inferred from observations. Load an LSK for the TDB/TT/TAI conversion. The legacy celestial axes are mean J2000; modern axes are GCRS/ICRS. SPICE calls both J2000 without converting between the conventions. EOP1/EOP2 select the rotation convention, not new station coordinates. Both export PCK class ID 3000 so existing ITRF93-referenced station SPKs use the selected orientation. EOP2 implements the IERS2010 rotation chain with the supplied constant EOPs/CPOs; no time-varying tidal EOP series is synthesized.

References

https://iers-conventions.obspm.fr/content/chapter5/icc5.pdf liberfa/erfa

classmethod from_rotation(rotation, frame_class_id, *, base_frame='J2000', interpolation_frame='ECLIPJ2000', max_interval=21600.0, description='User-supplied absolute body orientation')#

Construct an absolute rotation law for any planet or other body.

Parameters:
rotationcallable

Vectorized function of TDB seconds, returning base_frame-to-body matrices with shape epoch.shape + (3,3).

frame_class_idint

PCK frame class ID (not the body or frame ID). Load an FK mapping this ID to the intended class-2 body-fixed frame.

base_framestr, optional

Callback inertial axes. Defaults to "J2000".

interpolation_framestr, optional

PCK inertial base. Select axes avoiding the body’s 3-1-3 Euler pole singularity. Defaults to "ECLIPJ2000".

max_intervalfloat, optional

Maximum Chebyshev record length, seconds. Use a shorter interval for fast rotation. Interpolation is checked. Defaults to 21600..

descriptionstr, optional

Provenance recorded in the PCK comments. Defaults to "User-supplied absolute body orientation".

Returns:
BodyOrientation

Absolute model supporting rotation_matrix and write_pck. The callback supplies the physics; no Earth rotation law is assumed.

Raises:
ValueError

If frame_class_id is not an integer, max_interval is not positive and finite, or base_frame or interpolation_frame is not inertial.

TypeError

If rotation is not callable.

static celestial_pole_correction(delta_x=0.0, delta_y=0.0)#

Return the J2000 rotation vector of a celestial-pole-offset correction.

Parameters:
delta_x, delta_yfloat, optional

Intended minus nominal celestial pole offsets \(\delta X\), \(\delta Y\), radians. Defaults to 0..

Returns:
numpy.ndarray

Rotation vector \((-\delta Y, \delta X, 0)\), rad, shape (3,), in J2000 axes and held fixed there. Unlike earth_correction() it does not turn with the Earth; apply it with inertial_rotation_partials().

Raises:
NonFiniteOffsetError

If either offset is not finite. It is also a ValueError.

Notes

To first order the CIP matrix \(Q(X, Y)\) of IERS Conventions (2010) eq. 5.10 is \(I + [\omega\times]\) with this \(\omega\). Frame bias and \(Q \ne I\) change the column by about 3e-3 of itself, far below what a measurement can see.

The same rotation corrects the IAU 1980 nutation of a model="EOP1" kernel, with \(\delta X = \delta\psi\sin\varepsilon_A\) and \(\delta Y = \delta\varepsilon\) (IERS Conventions (2010) eq. 5.26 without its precession cross terms, which are about 6e-3 of the offsets).

static celestial_pole_correction_partials()#

Return the rotation-vector column of each celestial pole offset.

Returns:
dict

Keyword accepted by celestial_pole_correction() mapped to the constant \(\partial\omega/\partial p\), shape (3,), in radians per radian, J2000 axes. Exact, since the correction is linear in both offsets.

static earth_correction_partials()#

Return the rotation-vector column of each Earth EOP delta.

Returns:
dict

Keyword accepted by earth_correction() mapped to the constant \(\partial\varepsilon/\partial p\), shape (3,), in radians per radian for the pole terms and radians per second for UT1.

Notes

earth_correction() is linear in its three inputs, so these columns are exact rather than a linearization, and a station partial chains them straight into rotation_partials(). Keying by the constructor’s own argument names keeps the two from drifting apart.

static iau_angles(epoch, frame=None, coefficients=None, *, pole_ra=None, pole_dec=None, pole_pm=None, pole_w0=None)#

Return the IAU pole and prime-meridian angles of a body and their rates.

Parameters:
epochfloat

TDB seconds past J2000.

framestr, optional

The body’s SPICE body-fixed frame, e.g. "IAU_MARS". Used when no angle is replaced. Defaults to None.

coefficientsarray_like, shape (3, 3), optional

Secular constants as returned by iau_pole_coefficients(); shorter rows are padded with zeros. Needed when an angle is replaced or frame is None. Defaults to None.

pole_ra, pole_decfloat, optional

Estimated pole right ascension and declination, rad. Each replaces its secular polynomial and is constant in time. Defaults to None.

pole_pmfloat, optional

Estimated prime-meridian rate, deg/day. Gives \(W = W_0 + \mathrm{PM}\,d\) without the quadratic term. Defaults to None.

pole_w0float, optional

Estimated prime-meridian angle at J2000, rad. Replaces \(W_0\). Defaults to None.

Returns:
anglesnumpy.ndarray

\((\alpha, \delta, W)\), rad, shape (3,).

ratesnumpy.ndarray

\((\dot\alpha, \dot\delta, \dot W)\), rad/s, shape (3,).

Raises:
MissingPoleCoefficientsError

If the secular path is needed and coefficients is None. It is also a ValueError.

Notes

With frame given and nothing replaced, the angles are SPICE’s full model, read back from sxform("J2000", frame) as the 3-1-3 Euler angles \((\alpha + 90^\circ, 90^\circ - \delta, W)\), so the nutation-precession terms and the time derivatives are included and \(W\) is reduced to \([-\pi, \pi]\).

Otherwise the secular model of the kernel is used, \(\alpha = \alpha_0 + \alpha_1 T + \alpha_2 T^2\), likewise \(\delta\), and \(W = W_0 + W_1 d + W_2 d^2\), with \(d\) days and \(T\) Julian centuries past J2000, and each estimated value replaces its term as described above. This is the estimated-pole model of SphericalHarmonicsGravity, so gravity and the optical measurement models see one body orientation.

static iau_matrix(angles)#

Return the body-fixed to J2000 rotation of IAU pole angles.

Parameters:
anglesarray_like, shape (3,)

\((\alpha, \delta, W)\), rad, e.g. from iau_angles().

Returns:
numpy.ndarray

Shape (3, 3), \(R_z(\alpha + \pi/2)\,R_x(\pi/2 - \delta)\, R_z(W)\) with active rotations, so \(r_{J2000} = R\,r_{body}\). It equals SPICE’s pxform(frame, "J2000") for the angles of iau_angles().

static iau_pole_coefficients(body)#

Return a body’s secular IAU rotation constants from the kernel pool.

Parameters:
bodystr or int

SPICE body name or ID, e.g. "MARS" or 499.

Returns:
numpy.ndarray

Shape (3, 3). Rows are POLE_RA, POLE_DEC (deg, deg per Julian century, deg per century squared) and PM (deg, deg/day, deg/day squared). A term missing from the kernel is zero.

Notes

A text PCK such as pck00010.tpc must be loaded. Only the secular terms are read; the nutation-precession series of the full model are left to SPICE.

displacement(position_body_fixed, epoch: float = None, frame_from: str = None, frame_to: str = None) → ArrayWUnits#

Displacement of a body-fixed point caused by the correction.

Parameters:
position_body_fixedArrayWUnits, ArrayWFrame or array_like

Position in the body-fixed frame, shape (3,). Bare numbers are km.

epochfloat, optional

TDB seconds past J2000. Required only when frame_to is given and differs from frame_from. Defaults to None.

frame_fromstr, optional

The body-fixed frame position_body_fixed is expressed in. Defaults to None, meaning body_fixed_frame.

frame_tostr, optional

Rotate the displacement into this frame before returning it. Defaults to None, keeping it in frame_from.

Returns:
ArrayWUnits

Displacement, km, shape (3,).

Raises:
ValueError

If this instance is not a correction model, or frame_to differs from frame_from and epoch is not given.

RuntimeError

If SPICE cannot resolve the rotation from frame_from to frame_to.

rotation_components(epoch)#

Return (W, R, Q), with celestial -> terrestrial = W @ R @ Q.

Each component has shape (3, 3), or epoch.shape + (3, 3) for arrays. For IAU1976/1980, Q maps mean-J2000 to the true equator/equinox of date and R uses GAST. For IAU2006/2000A, Q maps GCRS/ICRS to CIRS and R uses ERA. SPICE names the inertial base J2000 in both cases; it does not itself convert the legacy model’s mean-J2000 axes to ICRS. The loaded Earth PCK is never consulted or decomposed. Its three rotation angles do not uniquely supply xp, yp, UT1 and two CPOs.

Parameters:
epochfloat or array_like

TDB seconds past J2000.

Returns:
polar_motionnp.ndarray

W, the polar-motion matrix built from pole_x, pole_y and s’ (zero for IAU1976/1980).

rotationnp.ndarray

R, the spin about the pole of date by GAST (IAU1976/1980) or ERA (IAU2006/2000A).

celestialnp.ndarray

Q, precession-nutation from the celestial axes to the true equator and equinox of date (IAU1976/1980) or to CIRS (IAU2006/2000A).

Raises:
ValueError

If this instance has no absolute Earth EOPs, epoch is not finite, or the corrected celestial pole has X^2 + Y^2 >= 1.

SpiceyError

If no leap-second kernel is loaded.

rotation_matrix(epoch)#

Return celestial -> terrestrial rotation at TDB seconds past J2000.

See rotation_components() for the celestial axes of each Earth convention.

Parameters:
epochfloat or array_like

TDB seconds past J2000.

Returns:
np.ndarray

Rotation from the inertial base frame to the body-fixed frame, shape (3, 3) for a scalar epoch or epoch.shape + (3, 3).

Raises:
ValueError

If this instance is a correction model or epoch is not finite, or as raised by rotation_components().

state_displacement(position_body_fixed, epoch: float, frame_from: str = None, frame_to: str = 'J2000', velocity_body_fixed=None) → ArrayWUnits#

Position and velocity displacement of a body-fixed point.

A station is static in the body-fixed frame, so the correction moves it by \(\varepsilon \times r\) there and leaves its body-fixed velocity at zero. Its inertial velocity still changes, by \(\Omega \times \delta r\) — the moved point sits on a different radius from the spin axis. That term is small but not negligible for Doppler: 23 m of displacement is 1.7 mm/s, against measurement noise often quoted at 1 mm/s.

The state transformation carries both terms, so the body-fixed six-vector [delta_r, delta_v] (delta_v is zero for a static station) is rotated with sxform rather than the position alone.

Parameters:
position_body_fixedArrayWUnits, ArrayWFrame or array_like

Position in the body-fixed frame, shape (3,). Bare numbers are km.

epochfloat

TDB seconds past J2000.

frame_fromstr, optional

The body-fixed frame position_body_fixed is expressed in. Defaults to None, meaning body_fixed_frame.

frame_tostr, optional

Frame to express the displacement in. Defaults to "J2000".

velocity_body_fixedArrayWUnits, ArrayWFrame or array_like, optional

Nominal body-fixed velocity, shape (3,); bare numbers are km/s. Its cross product with the correction supplies the body-fixed velocity delta. Defaults to None, a zero velocity.

Returns:
ArrayWUnits

[dx, dy, dz, dvx, dvy, dvz] in km and km/s, shape (6,).

Raises:
ValueError

If this instance is not a correction model.

RuntimeError

If SPICE cannot resolve the state transformation from frame_from to frame_to.

write_pck(path, start, stop)#

Write a new type-2 PCK for this model’s frame class ID.

start/stop are TDB seconds past J2000. The caller must explicitly load the returned file after competing PCKs to select it within this interval. Existing files are not overwritten or unloaded. Outside this interval SPICE may use another loaded PCK.

Degree-12 Chebyshev records span at most max_interval (six hours by default). Euler angles use interpolation_frame (ECLIPJ2000 by default) as their base to avoid the near-J2000-pole singularity. Their interpolation is checked against direct rotation_matrix() values at independent points, including every record boundary, before writing.

Parameters:
pathstr or pathlib.Path

Output PCK file. It must not exist yet.

start, stopfloat

Coverage interval, TDB seconds past J2000, with stop > start.

Returns:
pathlib.Path

The written PCK file.

Raises:
ValueError

If this instance is a correction model; start or stop is not finite or stop <= start; rotation_matrix() does not return proper orthogonal matrices; or the checked interpolation error exceeds 5e-11.

FileExistsError

If path already exists.

IAU_POLE_PARAMETERS = ('pole_ra', 'pole_dec', 'pole_pm', 'pole_w0')#

State keys of an estimated IAU body orientation.

Pole right ascension and declination (rad), prime-meridian rate (deg/day) and prime-meridian angle at J2000 (rad). Each replaces its term of the body’s secular PCK model, as iau_angles() describes.

IAU_POLE_UNITS = {'pole_dec': rad, 'pole_pm': [(8.64E+04 sec^-1) (1.75E-02 rad^1)], 'pole_ra': rad, 'pole_w0': rad}#

Units of each of IAU_POLE_PARAMETERS.

IAU_ROTATION_ITEMS = ('POLE_RA', 'POLE_DEC', 'PM')#

Kernel-pool names of the IAU secular rotation constants, in the row order of iau_pole_coefficients().

STATE_UNITS = (km, km, km, km/sec, km/sec, km/sec)#

Row units of state_displacement().

base_frame: str = 'J2000'#
body_fixed_frame: str | None = None#
celestial_pole_dx: float = 0.0#
celestial_pole_dy: float = 0.0#
property convention#

The complete convention for report labels and PCK provenance.

description: str = 'User-supplied absolute body orientation'#
property eop_option#

Selected Earth convention: EOP1 (ITRF93), EOP2 (IERS2010), or None.

The option names identify the legacy and IERS rotation conventions. They do not perform a change of terrestrial station-coordinate datum.

frame_class_id: int = 3000#
interpolation_frame: str = 'ECLIPJ2000'#
max_interval: float = 21600.0#
model: str = 'EOP1'#
nutation_delta_epsilon: float = 0.0#
nutation_delta_psi: float = 0.0#
pole_x: float | None = None#
pole_y: float | None = None#
rotation: Callable | None = None#
rotation_vector: ndarray | None = None#
tai_minus_ut1: float | None = None#
ut1_time_scale: str = 'TAI'#
exception MissingPoleCoefficientsError#

Bases: ValueError

iau_angles() needed the secular PCK coefficients of a body but was given none.

exception NonFiniteOffsetError#

Bases: ValueError

A celestial pole offset given to celestial_pole_correction() was not finite.