import numpy as np
from warnings import warn
from typing import Union, List, Tuple
from numpy.typing import ArrayLike
from spiceypy import spkpos, pxform
from crt._validate_values import validate_scale, validate_position, validate_rotation
[docs]class RigidBody:
"""
The :class:`RigidBody` class is the superclass to :class:`Entity` as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It provides methods for adjusting the pose (position and orientation)
as well as scale of a body. Additionally, it provides a SPICE interface allowing for NAIF ephemerides to be
used to control the pose of a body.
:param position: Position of the body |default| :code:`np.zeros(3)`
:type position: ArrayLike, optional
:param rotation: Rotation (orientation) of the body as a 3-by-3 rotation matrix
that must obey both :math:`R^T = R^{-1}` and :math:`\det(R)=1`
|default| :code:`np.eye(3)`
:type rotation: ArrayLike, optional
:param scale: Scale of the body |default| :code:`1`
:type scale: Union[float, int], optional
:param name: NAIF name or ID code of body |default| :code:`None`
:type name: Union[str, None], optional
:param frame: NAIF reference frame name of body |default| :code:`None`
:type frame: Union[str, None], optional
:param origin: NAIF name or ID code of reference frame origin |default| :code:`"SSB"`
:type origin: Union[str, None], optional
:param ref: NAIF reference frame name of reference frame |default| :code:`"J2000"`
:type ref: Union[str, None], optional
:param abcorr: Aberration correction flag |default| :code:`"NONE"`
:type abcorr: Union[str, None], optional
"""
def __init__(self, position: ArrayLike=np.zeros(3),
rotation: ArrayLike=np.eye(3), scale: Union[float, int]=1,
name: Union[str, None]=None, frame: Union[str, None]=None, origin: str="SSB",
ref: Union[str, None]="J2000", abcorr: Union[str, None]="NONE"):
self.scale = validate_scale(scale)
"""
Scale of the body (:code:`float`)
This attribute is used by the subclass :class:`~.Entity` and describes how the :class:`~.Entity`
geometry is scaled. It can be modified directly using :meth:`set_scale`.
"""
self.position = validate_position(position)
"""
Position of the body (:code:`numpy.ndarray` of shape :code:`(3,)`)
This attribute is used by the subclass :class:`~.Entity`, as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It describes how the body is translated in space. It can be modified directly
using either :meth:`set_position` or :meth:`set_pose`. Alternatively, it can be modified using
:meth:`spice_position` which uses predefined SPICE ephemerides to calculate a position for a
given ephemeris time.
"""
self.rotation = validate_rotation(rotation)
"""
Rotation of the body (:code:`numpy.ndarray` of shape :code:`(3,3)`)
This attribute is used by the subclasses :class:`~.Entity`, as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It describes how the body is translated in space. It can be modified directly
using either :meth:`set_rotation` or :meth:`set_pose`. Alternatively, it can be modified using
:meth:`spice_rotation` which uses predefined SPICE ephemerides to calculate a rotation for a
given ephemeris time.
"""
self.name = name
"""
NAIF name or ID code for the body (:code:`str`)
This attribute can be used by the subclasses :class:`~.Entity`, as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It is used by the SPICE utility, :func:`spiceypy.spkpos`, as the name for
the celestial body whose position will be used as the position of the body. This corresponds to
the :code:`targ` argument to :func:`spiceypy.spkpos`. Please refer to the
`spkpos <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html>`_ documentation
for more information.
"""
self.frame = frame
"""
NAIF reference frame name for the body (:code:`str`)
This attribute can be used by the subclasses :class:`~.Entity`, as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It is used by the SPICE utility, :func:`spiceypy.pxform`, as the name for
the reference frame whose orientation will be used as the rotation of the body. This corresponds to
the :code:`to` argument to :func:`spiceypy.pxform`. Please refer to the
`pxform <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html>`_ documentation
for more information.
"""
self.origin = origin
"""
NAIF name or ID code for the reference frame origin (:code:`str`)
This attribute can be used by the subclasses :class:`~.Entity`, as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It is used by the SPICE utility, :func:`spiceypy.spkpos`, as the name for
the celestial body whose position will be used as the origin of the reference frame. This corresponds to
the :code:`obs` argument to :func:`spiceypy.spkpos`. Please refer to the
`spkpos <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html>`_ documentation
for more information.
"""
self.ref = ref
"""
NAIF reference frame name (:code:`str`)
This attribute can be used by the subclasses :class:`~.Entity`, as well as all :class:`~.cameras.Camera` and
:class:`~.lights.Light` classes. It is used by both the SPICE utilities, :func:`spiceypy.pxform` and
:func:`spiceypy.spkpos`, as the name for the reference frame against in which positions and
rotations will be calculated. This corresponds to the :code:`ref` argument to both :func:`spiceypy.spkpos`
and :func:`spiceypy.pxform`. Please refer to the
`spkpos <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html>`_ or
`pxform <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html>`_ documentation for more
information.
"""
self.abcorr = abcorr
"""
Aberration correction flag (:code:`str`)
This attribute is used by by the SPICE utility, :func:`spiceypy.spkpos`, as an indication of what
light time correction should be used. This corresponds to the :code:`abcorr` argument to
:func:`spiceypy.spkpos`. Please refer to the
`spkpos <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html>`_ documentation
for more information.
"""
[docs] def set_scale(self, scale: float, cpp: bool =True):
"""
Set a new :attr:`~.RigidBody.scale`
:param scale: Scale to be applied
:type scale: float
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
self.scale = validate_scale(scale)
if cpp:
self._cpp.set_scale(self.scale)
[docs] def set_position(self, position: ArrayLike, cpp: bool =True):
"""
Set a new :attr:`~.RigidBody.position`
:param position: Position to be applied (xyz)
:type position: ArrayLike
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
self.position = validate_position(position)
if cpp:
self._cpp.set_position(self.position)
[docs] def set_rotation(self, rotation: ArrayLike, cpp: bool =True):
"""
Set a new :attr:`~.RigidBody.rotation`
:param rotation: 3 by 3 Rotation matrix that must obey :math:`R^T = R^{-1}` and :math:`\det(R)=1`
:type rotation: ArrayLike
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
self.rotation = validate_rotation(rotation)
if cpp:
self._cpp.set_rotation(self.rotation)
[docs] def set_pose(self, position: ArrayLike, rotation: ArrayLike, cpp: bool =True):
"""
Set both a new :attr:`~.RigidBody.position` and :attr:`~.RigidBody.rotation`
:param position: Position to be applied (xyz)
:type position: ArrayLike
:param rotation: 3 by 3 Rotation matrix that must obey :math:`R^T = R^{-1}` and :math:`\det(R)=1`
:type rotation: ArrayLike
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
self.position = validate_position(position)
self.rotation = validate_rotation(rotation)
if cpp:
self._cpp.set_pose(self.position, self.rotation)
[docs] def spice_position(self, et: float, cpp: bool =True):
"""
Set new position for a given ephemeris time using SPICE
This is done using :func:`spiceypy.spkpos`, where the :attr:`~.RigidBody.name` is used as the :code:`targ`
argument, :attr:`~.RigidBody.ref` is used as the :code:`ref` argument, :attr:`~.RigidBody.abcorr` is
used as the :code:`abcorr` argument, and :attr:`~.RigidBody.origin` is used as the :code:`obs` argument.
Please refer to the
`spkpos <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html>`_ documentation
for more information.
:param et: Ephemeris time (seconds since the J2000 epoch), also known as Barycentric Dynamical Time
:type et: float
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
position,_ = spkpos(self.name, et, self.ref, self.abcorr, self.origin)
self.position = validate_position(position)
if cpp:
self._cpp.set_position(self.position)
[docs] def spice_rotation(self, et: float, cpp: bool =True):
"""
Set new rotation for a given ephemeris time using SPICE
This is done using :func:`spiceypy.pxform`, where the :attr:`~.RigidBody.ref` is used as the :code:`from`
argument, and :attr:`~.RigidBody.frame` is used as the :code:`to` argumnet. Please refer to the
`pxform <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html>`_ documentation
for more information.
:param et: Ephemeris time (seconds since the J2000 epoch), also known as Barycentric Dynamical Time
:type et: float
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
rotation = pxform(self.ref, self.frame, et)
self.rotation = validate_rotation(rotation)
if cpp:
self._cpp.set_position(self.rotation)
[docs] def spice_pose(self, et: float, cpp: bool =True):
"""
Set both a new position and rotation for a given ephemeris time using SPICE
The position is updated using :func:`spiceypy.spkpos`, where the :attr:`~.RigidBody.name` is used as
the :code:`targ` argument, :attr:`~.RigidBody.ref` is used as the :code:`ref` argument,
:attr:`~.RigidBody.abcorr` is used as the :code:`abcorr` argument, and :attr:`~.RigidBody.origin` is
used as the :code:`obs` argument.
The rotation is updated using :func:`spiceypy.pxform`, where the :attr:`~.RigidBody.ref` is used as
the :code:`from` argument, and :attr:`~.RigidBody.frame` is used as the :code:`to` argumnet.
Please refer to the
`spkpos <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html>`_ or
`pxform <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html>`_ documentation for more
information.
:param et: Ephemeris time (seconds since the J2000 epoch), also known as Barycentric Dynamical Time
:type et: float
:param cpp: Flag for if the underlying C++ body should be updated
|default| :code:`True`
:type cpp: bool, optional
"""
position,_ = spkpos(self.name, et, self.ref, self.abcorr, self.origin)
rotation = pxform(self.ref, self.frame, et)
self.position = validate_position(position)
self.rotation = validate_rotation(rotation)
if cpp:
self._cpp.set_pose(self.position, self.rotation)