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