docs: def eci2ecef(eci: np.ndarray,
               time: datetime,
               useastropy: bool = True) -> np.ndarray:
      """
      Observer => Point  ECI  =>  ECEF
  
      Parameters
      ----------
      eci : tuple of float
          Nx3 target ECI location (x,y,z) [meters]
      time : datetime.datetime
          time of obsevation (UTC)
      useastropy : bool, optional
          use AstroPy for conversion
  
      Results
      -------
      x : float
          target x ECEF coordinate
      y : float
          target y ECEF coordinate
      z : float
          target z ECEF coordinate
      """
      useastropy = useastropy and Time
  
      if useastropy:
          gst = Time(time).sidereal_time('apparent', 'greenwich').radian
      else:
          gst = datetime2sidereal(time, 0.)
  
      gst = np.atleast_1d(gst)
      assert gst.ndim == 1 and isinstance(gst[0], float)  # must be in radians!
  
      eci = np.atleast_2d(eci)
      assert eci.shape[0] == gst.size, 'length of time does not match number of ECI positions'
  
      N, trip = eci.shape
      if eci.ndim > 2 or trip != 3:
          raise ValueError('eci triplets must be shape (N,3)')
  
      ecef = np.empty_like(eci)
  
      for i in range(N):
          ecef[i, :] = _rottrip(gst[i]) @ eci[i, :]
  
      return ecef.squeeze()