1 /* Copyright 2002-2019 CS Systèmes d'Information 2 * Licensed to CS Systèmes d'Information (CS) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * CS licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.orekit.propagation; 18 19 import java.io.Serializable; 20 import java.util.ArrayList; 21 import java.util.Collections; 22 import java.util.HashMap; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.stream.Stream; 26 27 import org.hipparchus.analysis.interpolation.HermiteInterpolator; 28 import org.hipparchus.exception.LocalizedCoreFormats; 29 import org.hipparchus.exception.MathIllegalStateException; 30 import org.hipparchus.geometry.euclidean.threed.Rotation; 31 import org.hipparchus.geometry.euclidean.threed.Vector3D; 32 import org.hipparchus.util.FastMath; 33 import org.orekit.attitudes.Attitude; 34 import org.orekit.attitudes.LofOffset; 35 import org.orekit.errors.OrekitException; 36 import org.orekit.errors.OrekitIllegalArgumentException; 37 import org.orekit.errors.OrekitMessages; 38 import org.orekit.frames.Frame; 39 import org.orekit.frames.LOFType; 40 import org.orekit.frames.Transform; 41 import org.orekit.orbits.Orbit; 42 import org.orekit.time.AbsoluteDate; 43 import org.orekit.time.TimeInterpolable; 44 import org.orekit.time.TimeShiftable; 45 import org.orekit.time.TimeStamped; 46 import org.orekit.utils.TimeStampedAngularCoordinates; 47 import org.orekit.utils.TimeStampedPVCoordinates; 48 49 50 /** This class is the representation of a complete state holding orbit, attitude 51 * and mass information at a given date. 52 * 53 * <p>It contains an {@link Orbit orbital state} at a current 54 * {@link AbsoluteDate} both handled by an {@link Orbit}, plus the current 55 * mass and attitude. Orbit and state are guaranteed to be consistent in terms 56 * of date and reference frame. The spacecraft state may also contain additional 57 * states, which are simply named double arrays which can hold any user-defined 58 * data. 59 * </p> 60 * <p> 61 * The state can be slightly shifted to close dates. This shift is based on 62 * a simple Keplerian model for orbit, a linear extrapolation for attitude 63 * taking the spin rate into account and no mass change. It is <em>not</em> 64 * intended as a replacement for proper orbit and attitude propagation but 65 * should be sufficient for either small time shifts or coarse accuracy. 66 * </p> 67 * <p> 68 * The instance <code>SpacecraftState</code> is guaranteed to be immutable. 69 * </p> 70 * @see org.orekit.propagation.numerical.NumericalPropagator 71 * @author Fabien Maussion 72 * @author Véronique Pommier-Maurussane 73 * @author Luc Maisonobe 74 */ 75 public class SpacecraftState 76 implements TimeStamped, TimeShiftable<SpacecraftState>, TimeInterpolable<SpacecraftState>, Serializable { 77 78 /** Serializable UID. */ 79 private static final long serialVersionUID = 20130407L; 80 81 /** Default mass. */ 82 private static final double DEFAULT_MASS = 1000.0; 83 84 /** 85 * tolerance on date comparison in {@link #checkConsistency(Orbit, Attitude)}. 100 ns 86 * corresponds to sub-mm accuracy at LEO orbital velocities. 87 */ 88 private static final double DATE_INCONSISTENCY_THRESHOLD = 100e-9; 89 90 /** Orbital state. */ 91 private final Orbit orbit; 92 93 /** Attitude. */ 94 private final Attitude attitude; 95 96 /** Current mass (kg). */ 97 private final double mass; 98 99 /** Additional states. */ 100 private final Map<String, double[]> additional; 101 102 /** Build a spacecraft state from orbit only. 103 * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p> 104 * @param orbit the orbit 105 */ 106 public SpacecraftState(final Orbit orbit) { 107 this(orbit, 108 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), 109 DEFAULT_MASS, null); 110 } 111 112 /** Build a spacecraft state from orbit and attitude provider. 113 * <p>Mass is set to an unspecified non-null arbitrary value.</p> 114 * @param orbit the orbit 115 * @param attitude attitude 116 * @exception IllegalArgumentException if orbit and attitude dates 117 * or frames are not equal 118 */ 119 public SpacecraftState(final Orbit orbit, final Attitude attitude) 120 throws IllegalArgumentException { 121 this(orbit, attitude, DEFAULT_MASS, null); 122 } 123 124 /** Create a new instance from orbit and mass. 125 * <p>Attitude law is set to an unspecified default attitude.</p> 126 * @param orbit the orbit 127 * @param mass the mass (kg) 128 */ 129 public SpacecraftState(final Orbit orbit, final double mass) { 130 this(orbit, 131 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), 132 mass, null); 133 } 134 135 /** Build a spacecraft state from orbit, attitude provider and mass. 136 * @param orbit the orbit 137 * @param attitude attitude 138 * @param mass the mass (kg) 139 * @exception IllegalArgumentException if orbit and attitude dates 140 * or frames are not equal 141 */ 142 public SpacecraftState(final Orbit orbit, final Attitude attitude, final double mass) 143 throws IllegalArgumentException { 144 this(orbit, attitude, mass, null); 145 } 146 147 /** Build a spacecraft state from orbit only. 148 * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p> 149 * @param orbit the orbit 150 * @param additional additional states 151 */ 152 public SpacecraftState(final Orbit orbit, final Map<String, double[]> additional) { 153 this(orbit, 154 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), 155 DEFAULT_MASS, additional); 156 } 157 158 /** Build a spacecraft state from orbit and attitude provider. 159 * <p>Mass is set to an unspecified non-null arbitrary value.</p> 160 * @param orbit the orbit 161 * @param attitude attitude 162 * @param additional additional states 163 * @exception IllegalArgumentException if orbit and attitude dates 164 * or frames are not equal 165 */ 166 public SpacecraftState(final Orbit orbit, final Attitude attitude, final Map<String, double[]> additional) 167 throws IllegalArgumentException { 168 this(orbit, attitude, DEFAULT_MASS, additional); 169 } 170 171 /** Create a new instance from orbit and mass. 172 * <p>Attitude law is set to an unspecified default attitude.</p> 173 * @param orbit the orbit 174 * @param mass the mass (kg) 175 * @param additional additional states 176 */ 177 public SpacecraftState(final Orbit orbit, final double mass, final Map<String, double[]> additional) { 178 this(orbit, 179 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), 180 mass, additional); 181 } 182 183 /** Build a spacecraft state from orbit, attitude provider and mass. 184 * @param orbit the orbit 185 * @param attitude attitude 186 * @param mass the mass (kg) 187 * @param additional additional states (may be null if no additional states are available) 188 * @exception IllegalArgumentException if orbit and attitude dates 189 * or frames are not equal 190 */ 191 public SpacecraftState(final Orbit orbit, final Attitude attitude, 192 final double mass, final Map<String, double[]> additional) 193 throws IllegalArgumentException { 194 checkConsistency(orbit, attitude); 195 this.orbit = orbit; 196 this.attitude = attitude; 197 this.mass = mass; 198 if (additional == null) { 199 this.additional = Collections.emptyMap(); 200 } else { 201 this.additional = new HashMap<String, double[]>(additional.size()); 202 for (final Map.Entry<String, double[]> entry : additional.entrySet()) { 203 this.additional.put(entry.getKey(), entry.getValue().clone()); 204 } 205 } 206 } 207 208 /** Add an additional state. 209 * <p> 210 * {@link SpacecraftState SpacecraftState} instances are immutable, 211 * so this method does <em>not</em> change the instance, but rather 212 * creates a new instance, which has the same orbit, attitude, mass 213 * and additional states as the original instance, except it also 214 * has the specified state. If the original instance already had an 215 * additional state with the same name, it will be overridden. If it 216 * did not have any additional state with that name, the new instance 217 * will have one more additional state than the original instance. 218 * </p> 219 * @param name name of the additional state 220 * @param value value of the additional state 221 * @return a new instance, with the additional state added 222 * @see #hasAdditionalState(String) 223 * @see #getAdditionalState(String) 224 * @see #getAdditionalStates() 225 */ 226 public SpacecraftState addAdditionalState(final String name, final double... value) { 227 final Map<String, double[]> newMap = new HashMap<String, double[]>(additional.size() + 1); 228 newMap.putAll(additional); 229 newMap.put(name, value.clone()); 230 return new SpacecraftState(orbit, attitude, mass, newMap); 231 } 232 233 /** Check orbit and attitude dates are equal. 234 * @param orbit the orbit 235 * @param attitude attitude 236 * @exception IllegalArgumentException if orbit and attitude dates 237 * are not equal 238 */ 239 private static void checkConsistency(final Orbit orbit, final Attitude attitude) 240 throws IllegalArgumentException { 241 if (FastMath.abs(orbit.getDate().durationFrom(attitude.getDate())) > 242 DATE_INCONSISTENCY_THRESHOLD) { 243 throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH, 244 orbit.getDate(), attitude.getDate()); 245 } 246 if (orbit.getFrame() != attitude.getReferenceFrame()) { 247 throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH, 248 orbit.getFrame().getName(), 249 attitude.getReferenceFrame().getName()); 250 } 251 } 252 253 /** Get a time-shifted state. 254 * <p> 255 * The state can be slightly shifted to close dates. This shift is based on 256 * simple models. For orbits, the model is a Keplerian one if no derivatives 257 * are available in the orbit, or Keplerian plus quadratic effect of the 258 * non-Keplerian acceleration if derivatives are available. For attitude, 259 * a polynomial model is used. Neither mass nor additional states change. 260 * Shifting is <em>not</em> intended as a replacement for proper orbit 261 * and attitude propagation but should be sufficient for small time shifts 262 * or coarse accuracy. 263 * </p> 264 * <p> 265 * As a rough order of magnitude, the following table shows the extrapolation 266 * errors obtained between this simple shift method and an {@link 267 * org.orekit.propagation.numerical.NumericalPropagator numerical 268 * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field, 269 * Sun and Moon third bodies attractions, drag and solar radiation pressure. 270 * Beware that these results will be different for other orbits. 271 * </p> 272 * <table border="1" cellpadding="5"> 273 * <caption>Extrapolation Error</caption> 274 * <tr bgcolor="#ccccff"><th>interpolation time (s)</th> 275 * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr> 276 * <tr><td bgcolor="#eeeeff"> 60</td><td> 18</td><td> 1.1</td></tr> 277 * <tr><td bgcolor="#eeeeff">120</td><td> 72</td><td> 9.1</td></tr> 278 * <tr><td bgcolor="#eeeeff">300</td><td> 447</td><td> 140</td></tr> 279 * <tr><td bgcolor="#eeeeff">600</td><td>1601</td><td>1067</td></tr> 280 * <tr><td bgcolor="#eeeeff">900</td><td>3141</td><td>3307</td></tr> 281 * </table> 282 * @param dt time shift in seconds 283 * @return a new state, shifted with respect to the instance (which is immutable) 284 * except for the mass and additional states which stay unchanged 285 */ 286 public SpacecraftState shiftedBy(final double dt) { 287 return new SpacecraftState(orbit.shiftedBy(dt), attitude.shiftedBy(dt), 288 mass, additional); 289 } 290 291 /** {@inheritDoc} 292 * <p> 293 * The additional states that are interpolated are the ones already present 294 * in the instance. The sample instances must therefore have at least the same 295 * additional states has the instance. They may have more additional states, 296 * but the extra ones will be ignored. 297 * </p> 298 * <p> 299 * As this implementation of interpolation is polynomial, it should be used only 300 * with small samples (about 10-20 points) in order to avoid <a 301 * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a> 302 * and numerical problems (including NaN appearing). 303 * </p> 304 */ 305 public SpacecraftState interpolate(final AbsoluteDate date, 306 final Stream<SpacecraftState> sample) { 307 308 // prepare interpolators 309 final List<Orbit> orbits = new ArrayList<>(); 310 final List<Attitude> attitudes = new ArrayList<>(); 311 final HermiteInterpolator massInterpolator = new HermiteInterpolator(); 312 final Map<String, HermiteInterpolator> additionalInterpolators = 313 new HashMap<String, HermiteInterpolator>(additional.size()); 314 for (final String name : additional.keySet()) { 315 additionalInterpolators.put(name, new HermiteInterpolator()); 316 } 317 318 // extract sample data 319 sample.forEach(state -> { 320 final double deltaT = state.getDate().durationFrom(date); 321 orbits.add(state.getOrbit()); 322 attitudes.add(state.getAttitude()); 323 massInterpolator.addSamplePoint(deltaT, 324 new double[] { 325 state.getMass() 326 }); 327 for (final Map.Entry<String, HermiteInterpolator> entry : additionalInterpolators.entrySet()) { 328 entry.getValue().addSamplePoint(deltaT, state.getAdditionalState(entry.getKey())); 329 } 330 }); 331 332 // perform interpolations 333 final Orbit interpolatedOrbit = orbit.interpolate(date, orbits); 334 final Attitude interpolatedAttitude = attitude.interpolate(date, attitudes); 335 final double interpolatedMass = massInterpolator.value(0)[0]; 336 final Map<String, double[]> interpolatedAdditional; 337 if (additional.isEmpty()) { 338 interpolatedAdditional = null; 339 } else { 340 interpolatedAdditional = new HashMap<String, double[]>(additional.size()); 341 for (final Map.Entry<String, HermiteInterpolator> entry : additionalInterpolators.entrySet()) { 342 interpolatedAdditional.put(entry.getKey(), entry.getValue().value(0)); 343 } 344 } 345 346 // create the complete interpolated state 347 return new SpacecraftState(interpolatedOrbit, interpolatedAttitude, 348 interpolatedMass, interpolatedAdditional); 349 350 } 351 352 /** Gets the current orbit. 353 * @return the orbit 354 */ 355 public Orbit getOrbit() { 356 return orbit; 357 } 358 359 /** Get the date. 360 * @return date 361 */ 362 public AbsoluteDate getDate() { 363 return orbit.getDate(); 364 } 365 366 /** Get the inertial frame. 367 * @return the frame 368 */ 369 public Frame getFrame() { 370 return orbit.getFrame(); 371 } 372 373 /** Check if an additional state is available. 374 * @param name name of the additional state 375 * @return true if the additional state is available 376 * @see #addAdditionalState(String, double[]) 377 * @see #getAdditionalState(String) 378 * @see #getAdditionalStates() 379 */ 380 public boolean hasAdditionalState(final String name) { 381 return additional.containsKey(name); 382 } 383 384 /** Check if two instances have the same set of additional states available. 385 * <p> 386 * Only the names and dimensions of the additional states are compared, 387 * not their values. 388 * </p> 389 * @param state state to compare to instance 390 * @exception MathIllegalStateException if an additional state does not have 391 * the same dimension in both states 392 */ 393 public void ensureCompatibleAdditionalStates(final SpacecraftState state) 394 throws MathIllegalStateException { 395 396 // check instance additional states is a subset of the other one 397 for (final Map.Entry<String, double[]> entry : additional.entrySet()) { 398 final double[] other = state.additional.get(entry.getKey()); 399 if (other == null) { 400 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, 401 entry.getKey()); 402 } 403 if (other.length != entry.getValue().length) { 404 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH, 405 other.length, entry.getValue().length); 406 } 407 } 408 409 if (state.additional.size() > additional.size()) { 410 // the other state has more additional states 411 for (final String name : state.additional.keySet()) { 412 if (!additional.containsKey(name)) { 413 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, 414 name); 415 } 416 } 417 } 418 419 } 420 421 /** Get an additional state. 422 * @param name name of the additional state 423 * @return value of the additional state 424 * @see #addAdditionalState(String, double[]) 425 * @see #hasAdditionalState(String) 426 * @see #getAdditionalStates() 427 */ 428 public double[] getAdditionalState(final String name) { 429 if (!additional.containsKey(name)) { 430 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name); 431 } 432 return additional.get(name).clone(); 433 } 434 435 /** Get an unmodifiable map of additional states. 436 * @return unmodifiable map of additional states 437 * @see #addAdditionalState(String, double[]) 438 * @see #hasAdditionalState(String) 439 * @see #getAdditionalState(String) 440 */ 441 public Map<String, double[]> getAdditionalStates() { 442 return Collections.unmodifiableMap(additional); 443 } 444 445 /** Compute the transform from orbite/attitude reference frame to spacecraft frame. 446 * <p>The spacecraft frame origin is at the point defined by the orbit, 447 * and its orientation is defined by the attitude.</p> 448 * @return transform from specified frame to current spacecraft frame 449 */ 450 public Transform toTransform() { 451 final AbsoluteDate date = orbit.getDate(); 452 return new Transform(date, 453 new Transform(date, orbit.getPVCoordinates().negate()), 454 new Transform(date, attitude.getOrientation())); 455 } 456 457 /** Get the central attraction coefficient. 458 * @return mu central attraction coefficient (m^3/s^2) 459 */ 460 public double getMu() { 461 return orbit.getMu(); 462 } 463 464 /** Get the Keplerian period. 465 * <p>The Keplerian period is computed directly from semi major axis 466 * and central acceleration constant.</p> 467 * @return Keplerian period in seconds 468 */ 469 public double getKeplerianPeriod() { 470 return orbit.getKeplerianPeriod(); 471 } 472 473 /** Get the Keplerian mean motion. 474 * <p>The Keplerian mean motion is computed directly from semi major axis 475 * and central acceleration constant.</p> 476 * @return Keplerian mean motion in radians per second 477 */ 478 public double getKeplerianMeanMotion() { 479 return orbit.getKeplerianMeanMotion(); 480 } 481 482 /** Get the semi-major axis. 483 * @return semi-major axis (m) 484 */ 485 public double getA() { 486 return orbit.getA(); 487 } 488 489 /** Get the first component of the eccentricity vector (as per equinoctial parameters). 490 * @return e cos(ω + Ω), first component of eccentricity vector 491 * @see #getE() 492 */ 493 public double getEquinoctialEx() { 494 return orbit.getEquinoctialEx(); 495 } 496 497 /** Get the second component of the eccentricity vector (as per equinoctial parameters). 498 * @return e sin(ω + Ω), second component of the eccentricity vector 499 * @see #getE() 500 */ 501 public double getEquinoctialEy() { 502 return orbit.getEquinoctialEy(); 503 } 504 505 /** Get the first component of the inclination vector (as per equinoctial parameters). 506 * @return tan(i/2) cos(Ω), first component of the inclination vector 507 * @see #getI() 508 */ 509 public double getHx() { 510 return orbit.getHx(); 511 } 512 513 /** Get the second component of the inclination vector (as per equinoctial parameters). 514 * @return tan(i/2) sin(Ω), second component of the inclination vector 515 * @see #getI() 516 */ 517 public double getHy() { 518 return orbit.getHy(); 519 } 520 521 /** Get the true longitude argument (as per equinoctial parameters). 522 * @return v + ω + Ω true longitude argument (rad) 523 * @see #getLE() 524 * @see #getLM() 525 */ 526 public double getLv() { 527 return orbit.getLv(); 528 } 529 530 /** Get the eccentric longitude argument (as per equinoctial parameters). 531 * @return E + ω + Ω eccentric longitude argument (rad) 532 * @see #getLv() 533 * @see #getLM() 534 */ 535 public double getLE() { 536 return orbit.getLE(); 537 } 538 539 /** Get the mean longitude argument (as per equinoctial parameters). 540 * @return M + ω + Ω mean longitude argument (rad) 541 * @see #getLv() 542 * @see #getLE() 543 */ 544 public double getLM() { 545 return orbit.getLM(); 546 } 547 548 // Additional orbital elements 549 550 /** Get the eccentricity. 551 * @return eccentricity 552 * @see #getEquinoctialEx() 553 * @see #getEquinoctialEy() 554 */ 555 public double getE() { 556 return orbit.getE(); 557 } 558 559 /** Get the inclination. 560 * @return inclination (rad) 561 * @see #getHx() 562 * @see #getHy() 563 */ 564 public double getI() { 565 return orbit.getI(); 566 } 567 568 /** Get the {@link TimeStampedPVCoordinates} in orbit definition frame. 569 * Compute the position and velocity of the satellite. This method caches its 570 * results, and recompute them only when the method is called with a new value 571 * for mu. The result is provided as a reference to the internally cached 572 * {@link TimeStampedPVCoordinates}, so the caller is responsible to copy it in a separate 573 * {@link TimeStampedPVCoordinates} if it needs to keep the value for a while. 574 * @return pvCoordinates in orbit definition frame 575 */ 576 public TimeStampedPVCoordinates getPVCoordinates() { 577 return orbit.getPVCoordinates(); 578 } 579 580 /** Get the {@link TimeStampedPVCoordinates} in given output frame. 581 * Compute the position and velocity of the satellite. This method caches its 582 * results, and recompute them only when the method is called with a new value 583 * for mu. The result is provided as a reference to the internally cached 584 * {@link TimeStampedPVCoordinates}, so the caller is responsible to copy it in a separate 585 * {@link TimeStampedPVCoordinates} if it needs to keep the value for a while. 586 * @param outputFrame frame in which coordinates should be defined 587 * @return pvCoordinates in orbit definition frame 588 */ 589 public TimeStampedPVCoordinates getPVCoordinates(final Frame outputFrame) { 590 return orbit.getPVCoordinates(outputFrame); 591 } 592 593 /** Get the attitude. 594 * @return the attitude. 595 */ 596 public Attitude getAttitude() { 597 return attitude; 598 } 599 600 /** Gets the current mass. 601 * @return the mass (kg) 602 */ 603 public double getMass() { 604 return mass; 605 } 606 607 /** Replace the instance with a data transfer object for serialization. 608 * @return data transfer object that will be serialized 609 */ 610 private Object writeReplace() { 611 return new DTO(this); 612 } 613 614 /** Internal class used only for serialization. */ 615 private static class DTO implements Serializable { 616 617 /** Serializable UID. */ 618 private static final long serialVersionUID = 20140617L; 619 620 /** Orbit. */ 621 private final Orbit orbit; 622 623 /** Attitude and mass double values. */ 624 private double[] d; 625 626 /** Additional states. */ 627 private final Map<String, double[]> additional; 628 629 /** Simple constructor. 630 * @param state instance to serialize 631 */ 632 private DTO(final SpacecraftState state) { 633 634 this.orbit = state.orbit; 635 this.additional = state.additional.isEmpty() ? null : state.additional; 636 637 final Rotation rotation = state.attitude.getRotation(); 638 final Vector3D spin = state.attitude.getSpin(); 639 final Vector3D rotationAcceleration = state.attitude.getRotationAcceleration(); 640 this.d = new double[] { 641 rotation.getQ0(), rotation.getQ1(), rotation.getQ2(), rotation.getQ3(), 642 spin.getX(), spin.getY(), spin.getZ(), 643 rotationAcceleration.getX(), rotationAcceleration.getY(), rotationAcceleration.getZ(), 644 state.mass 645 }; 646 647 } 648 649 /** Replace the deserialized data transfer object with a {@link SpacecraftState}. 650 * @return replacement {@link SpacecraftState} 651 */ 652 private Object readResolve() { 653 return new SpacecraftState(orbit, 654 new Attitude(orbit.getFrame(), 655 new TimeStampedAngularCoordinates(orbit.getDate(), 656 new Rotation(d[0], d[1], d[2], d[3], false), 657 new Vector3D(d[4], d[5], d[6]), 658 new Vector3D(d[7], d[8], d[9]))), 659 d[10], additional); 660 } 661 662 } 663 664 }