1 /* Copyright 2002-2013 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.orbits;
18
19 import java.io.Serializable;
20
21 import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
22 import org.apache.commons.math3.linear.DecompositionSolver;
23 import org.apache.commons.math3.linear.MatrixUtils;
24 import org.apache.commons.math3.linear.QRDecomposition;
25 import org.apache.commons.math3.linear.RealMatrix;
26 import org.apache.commons.math3.util.FastMath;
27 import org.orekit.errors.OrekitException;
28 import org.orekit.errors.OrekitMessages;
29 import org.orekit.frames.Frame;
30 import org.orekit.frames.Transform;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.TimeInterpolable;
33 import org.orekit.time.TimeShiftable;
34 import org.orekit.time.TimeStamped;
35 import org.orekit.utils.PVCoordinates;
36 import org.orekit.utils.PVCoordinatesProvider;
37
38 /**
39 * This class handles orbital parameters.
40
41 * <p>
42 * For user convenience, both the Cartesian and the equinoctial elements
43 * are provided by this class, regardless of the canonical representation
44 * implemented in the derived class (which may be classical keplerian
45 * elements for example).
46 * </p>
47 * <p>
48 * The parameters are defined in a frame specified by the user. It is important
49 * to make sure this frame is consistent: it probably is inertial and centered
50 * on the central body. This information is used for example by some
51 * force models.
52 * </p>
53 * <p>
54 * The object <code>OrbitalParameters</code> is guaranteed to be immutable.
55 * </p>
56 * @author Luc Maisonobe
57 * @author Guylaine Prat
58 * @author Fabien Maussion
59 * @author Véronique Pommier-Maurussane
60 */
61 public abstract class Orbit
62 implements TimeStamped, TimeShiftable<Orbit>, TimeInterpolable<Orbit>,
63 Serializable, PVCoordinatesProvider {
64
65 /** Serializable UID. */
66 private static final long serialVersionUID = 438733454597999578L;
67
68 /** Frame in which are defined the orbital parameters. */
69 private final Frame frame;
70
71 /** Date of the orbital parameters. */
72 private final AbsoluteDate date;
73
74 /** Value of mu used to compute position and velocity (m<sup>3</sup>/s<sup>2</sup>). */
75 private final double mu;
76
77 /** Computed PVCoordinates. */
78 private transient PVCoordinates pvCoordinates;
79
80 /** Jacobian of the orbital parameters with mean angle with respect to the Cartesian coordinates. */
81 private transient double[][] jacobianMeanWrtCartesian;
82
83 /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with mean angle. */
84 private transient double[][] jacobianWrtParametersMean;
85
86 /** Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian coordinates. */
87 private transient double[][] jacobianEccentricWrtCartesian;
88
89 /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with eccentric angle. */
90 private transient double[][] jacobianWrtParametersEccentric;
91
92 /** Jacobian of the orbital parameters with true angle with respect to the Cartesian coordinates. */
93 private transient double[][] jacobianTrueWrtCartesian;
94
95 /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with true angle. */
96 private transient double[][] jacobianWrtParametersTrue;
97
98 /** Default constructor.
99 * Build a new instance with arbitrary default elements.
100 * @param frame the frame in which the parameters are defined
101 * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
102 * @param date date of the orbital parameters
103 * @param mu central attraction coefficient (m^3/s^2)
104 * @exception IllegalArgumentException if frame is not a {@link
105 * Frame#isPseudoInertial pseudo-inertial frame}
106 */
107 protected Orbit(final Frame frame, final AbsoluteDate date, final double mu)
108 throws IllegalArgumentException {
109 ensurePseudoInertialFrame(frame);
110 this.date = date;
111 this.mu = mu;
112 this.pvCoordinates = null;
113 this.frame = frame;
114 jacobianMeanWrtCartesian = null;
115 jacobianWrtParametersMean = null;
116 jacobianEccentricWrtCartesian = null;
117 jacobianWrtParametersEccentric = null;
118 jacobianTrueWrtCartesian = null;
119 jacobianWrtParametersTrue = null;
120 }
121
122 /** Set the orbit from Cartesian parameters.
123 * @param pvCoordinates the position and velocity in the inertial frame
124 * @param frame the frame in which the {@link PVCoordinates} are defined
125 * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
126 * @param date date of the orbital parameters
127 * @param mu central attraction coefficient (m^3/s^2)
128 * @exception IllegalArgumentException if frame is not a {@link
129 * Frame#isPseudoInertial pseudo-inertial frame}
130 */
131 protected Orbit(final PVCoordinates pvCoordinates, final Frame frame,
132 final AbsoluteDate date, final double mu)
133 throws IllegalArgumentException {
134 ensurePseudoInertialFrame(frame);
135 this.date = date;
136 this.mu = mu;
137 this.pvCoordinates = pvCoordinates;
138 this.frame = frame;
139 }
140
141 /** Get the orbit type.
142 * @return orbit type
143 */
144 public abstract OrbitType getType();
145
146 /** Ensure the defining frame is a pseudo-inertial frame.
147 * @param frame frame to check
148 * @exception IllegalArgumentException if frame is not a {@link
149 * Frame#isPseudoInertial pseudo-inertial frame}
150 */
151 private static void ensurePseudoInertialFrame(final Frame frame)
152 throws IllegalArgumentException {
153 if (!frame.isPseudoInertial()) {
154 throw OrekitException.createIllegalArgumentException(
155 OrekitMessages.NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_FOR_DEFINING_ORBITS,
156 frame.getName());
157 }
158 }
159
160 /** Get the frame in which the orbital parameters are defined.
161 * @return frame in which the orbital parameters are defined
162 */
163 public Frame getFrame() {
164 return frame;
165 }
166
167 /** Get the semi-major axis.
168 * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
169 * @return semi-major axis (m)
170 */
171 public abstract double getA();
172
173 /** Get the first component of the equinoctial eccentricity vector.
174 * @return first component of the equinoctial eccentricity vector
175 */
176 public abstract double getEquinoctialEx();
177
178 /** Get the second component of the equinoctial eccentricity vector.
179 * @return second component of the equinoctial eccentricity vector
180 */
181 public abstract double getEquinoctialEy();
182
183 /** Get the first component of the inclination vector.
184 * @return first component of the inclination vector
185 */
186 public abstract double getHx();
187
188 /** Get the second component of the inclination vector.
189 * @return second component of the inclination vector
190 */
191 public abstract double getHy();
192
193 /** Get the eccentric longitude argument.
194 * @return E + ω + Ω eccentric longitude argument (rad)
195 */
196 public abstract double getLE();
197
198 /** Get the true longitude argument.
199 * @return v + ω + Ω true longitude argument (rad)
200 */
201 public abstract double getLv();
202
203 /** Get the mean longitude argument.
204 * @return M + ω + Ω mean longitude argument (rad)
205 */
206 public abstract double getLM();
207
208 // Additional orbital elements
209
210 /** Get the eccentricity.
211 * @return eccentricity
212 */
213 public abstract double getE();
214
215 /** Get the inclination.
216 * @return inclination (rad)
217 */
218 public abstract double getI();
219
220 /** Get the central acceleration constant.
221 * @return central acceleration constant
222 */
223 public double getMu() {
224 return mu;
225 }
226
227 /** Get the keplerian period.
228 * <p>The keplerian period is computed directly from semi major axis
229 * and central acceleration constant.</p>
230 * @return keplerian period in seconds, or positive infinity for hyperbolic orbits
231 */
232 public double getKeplerianPeriod() {
233 final double a = getA();
234 return (a < 0) ? Double.POSITIVE_INFINITY : 2.0 * FastMath.PI * a * FastMath.sqrt(a / mu);
235 }
236
237 /** Get the keplerian mean motion.
238 * <p>The keplerian mean motion is computed directly from semi major axis
239 * and central acceleration constant.</p>
240 * @return keplerian mean motion in radians per second
241 */
242 public double getKeplerianMeanMotion() {
243 final double absA = FastMath.abs(getA());
244 return FastMath.sqrt(mu / absA) / absA;
245 }
246
247 /** Get the date of orbital parameters.
248 * @return date of the orbital parameters
249 */
250 public AbsoluteDate getDate() {
251 return date;
252 }
253
254 /** Get the {@link PVCoordinates} in a specified frame.
255 * @param outputFrame frame in which the position/velocity coordinates shall be computed
256 * @return pvCoordinates in the specified output frame
257 * @exception OrekitException if transformation between frames cannot be computed
258 * @see #getPVCoordinates()
259 */
260 public PVCoordinates getPVCoordinates(final Frame outputFrame)
261 throws OrekitException {
262 if (pvCoordinates == null) {
263 pvCoordinates = initPVCoordinates();
264 }
265
266 // If output frame requested is the same as definition frame,
267 // PV coordinates are returned directly
268 if (outputFrame == frame) {
269 return pvCoordinates;
270 }
271
272 // Else, PV coordinates are transformed to output frame
273 final Transform t = frame.getTransformTo(outputFrame, date);
274 return t.transformPVCoordinates(pvCoordinates);
275 }
276
277 /** {@inheritDoc} */
278 public PVCoordinates getPVCoordinates(final AbsoluteDate otherDate, final Frame otherFrame)
279 throws OrekitException {
280 return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame);
281 }
282
283
284 /** Get the {@link PVCoordinates} in definition frame.
285 * @return pvCoordinates in the definition frame
286 * @see #getPVCoordinates(Frame)
287 */
288 public PVCoordinates getPVCoordinates() {
289 if (pvCoordinates == null) {
290 pvCoordinates = initPVCoordinates();
291 }
292 return pvCoordinates;
293 }
294
295 /** Compute the position/velocity coordinates from the canonical parameters.
296 * @return computed position/velocity coordinates
297 */
298 protected abstract PVCoordinates initPVCoordinates();
299
300 /** Get a time-shifted orbit.
301 * <p>
302 * The orbit can be slightly shifted to close dates. This shift is based on
303 * a simple keplerian model. It is <em>not</em> intended as a replacement
304 * for proper orbit and attitude propagation but should be sufficient for
305 * small time shifts or coarse accuracy.
306 * </p>
307 * @param dt time shift in seconds
308 * @return a new orbit, shifted with respect to the instance (which is immutable)
309 */
310 public abstract Orbit shiftedBy(final double dt);
311
312 /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
313 * <p>
314 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
315 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
316 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
317 * </p>
318 * @param type type of the position angle to use
319 * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
320 * is larger than 6x6, only the 6x6 upper left corner will be modified
321 */
322 public void getJacobianWrtCartesian(final PositionAngle type, final double[][] jacobian) {
323
324 final double[][] cachedJacobian;
325 synchronized (this) {
326 switch (type) {
327 case MEAN :
328 if (jacobianMeanWrtCartesian == null) {
329 // first call, we need to compute the jacobian and cache it
330 jacobianMeanWrtCartesian = computeJacobianMeanWrtCartesian();
331 }
332 cachedJacobian = jacobianMeanWrtCartesian;
333 break;
334 case ECCENTRIC :
335 if (jacobianEccentricWrtCartesian == null) {
336 // first call, we need to compute the jacobian and cache it
337 jacobianEccentricWrtCartesian = computeJacobianEccentricWrtCartesian();
338 }
339 cachedJacobian = jacobianEccentricWrtCartesian;
340 break;
341 case TRUE :
342 if (jacobianTrueWrtCartesian == null) {
343 // first call, we need to compute the jacobian and cache it
344 jacobianTrueWrtCartesian = computeJacobianTrueWrtCartesian();
345 }
346 cachedJacobian = jacobianTrueWrtCartesian;
347 break;
348 default :
349 throw OrekitException.createInternalError(null);
350 }
351 }
352
353 // fill the user provided array
354 for (int i = 0; i < cachedJacobian.length; ++i) {
355 System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
356 }
357
358 }
359
360 /** Compute the Jacobian of the Cartesian parameters with respect to the orbital parameters.
361 * <p>
362 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
363 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
364 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
365 * </p>
366 * @param type type of the position angle to use
367 * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
368 * is larger than 6x6, only the 6x6 upper left corner will be modified
369 */
370 public void getJacobianWrtParameters(final PositionAngle type, final double[][] jacobian) {
371
372 final double[][] cachedJacobian;
373 synchronized (this) {
374 switch (type) {
375 case MEAN :
376 if (jacobianWrtParametersMean == null) {
377 // first call, we need to compute the jacobian and cache it
378 jacobianWrtParametersMean = createInverseJacobian(type);
379 }
380 cachedJacobian = jacobianWrtParametersMean;
381 break;
382 case ECCENTRIC :
383 if (jacobianWrtParametersEccentric == null) {
384 // first call, we need to compute the jacobian and cache it
385 jacobianWrtParametersEccentric = createInverseJacobian(type);
386 }
387 cachedJacobian = jacobianWrtParametersEccentric;
388 break;
389 case TRUE :
390 if (jacobianWrtParametersTrue == null) {
391 // first call, we need to compute the jacobian and cache it
392 jacobianWrtParametersTrue = createInverseJacobian(type);
393 }
394 cachedJacobian = jacobianWrtParametersTrue;
395 break;
396 default :
397 throw OrekitException.createInternalError(null);
398 }
399 }
400
401 // fill the user-provided array
402 for (int i = 0; i < cachedJacobian.length; ++i) {
403 System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
404 }
405
406 }
407
408 /** Create an inverse Jacobian.
409 * @param type type of the position angle to use
410 * @return inverse Jacobian
411 */
412 private double[][] createInverseJacobian(final PositionAngle type) {
413
414 // get the direct Jacobian
415 final double[][] directJacobian = new double[6][6];
416 getJacobianWrtCartesian(type, directJacobian);
417
418 // invert the direct Jacobian
419 final RealMatrix matrix = MatrixUtils.createRealMatrix(directJacobian);
420 final DecompositionSolver solver = new QRDecomposition(matrix).getSolver();
421 return solver.getInverse().getData();
422
423 }
424
425 /** Compute the Jacobian of the orbital parameters with mean angle with respect to the Cartesian parameters.
426 * <p>
427 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
428 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
429 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
430 * </p>
431 * @return 6x6 Jacobian matrix
432 * @see #computeJacobianEccentricWrtCartesian()
433 * @see #computeJacobianTrueWrtCartesian()
434 */
435 protected abstract double[][] computeJacobianMeanWrtCartesian();
436
437 /** Compute the Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian parameters.
438 * <p>
439 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
440 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
441 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
442 * </p>
443 * @return 6x6 Jacobian matrix
444 * @see #computeJacobianMeanWrtCartesian()
445 * @see #computeJacobianTrueWrtCartesian()
446 */
447 protected abstract double[][] computeJacobianEccentricWrtCartesian();
448
449 /** Compute the Jacobian of the orbital parameters with true angle with respect to the Cartesian parameters.
450 * <p>
451 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
452 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
453 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
454 * </p>
455 * @return 6x6 Jacobian matrix
456 * @see #computeJacobianMeanWrtCartesian()
457 * @see #computeJacobianEccentricWrtCartesian()
458 */
459 protected abstract double[][] computeJacobianTrueWrtCartesian();
460
461 /** Add the contribution of the Keplerian motion to parameters derivatives
462 * <p>
463 * This method is used by integration-based propagators to evaluate the part of Keplerian
464 * motion to evolution of the orbital state.
465 * </p>
466 * @param type type of the position angle in the state
467 * @param gm attraction coefficient to use
468 * @param pDot array containing orbital state derivatives to update (the Keplerian
469 * part must be <em>added</em> to the array components, as the array may already
470 * contain some non-zero elements corresponding to non-Keplerian parts)
471 */
472 public abstract void addKeplerContribution(final PositionAngle type, final double gm, double[] pDot);
473
474 /** Fill a Jacobian half row with a single vector.
475 * @param a coefficient of the vector
476 * @param v vector
477 * @param row Jacobian matrix row
478 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
479 */
480 protected static void fillHalfRow(final double a, final Vector3D v, final double[] row, final int j) {
481 row[j] = a * v.getX();
482 row[j + 1] = a * v.getY();
483 row[j + 2] = a * v.getZ();
484 }
485
486 /** Fill a Jacobian half row with a linear combination of vectors.
487 * @param a1 coefficient of the first vector
488 * @param v1 first vector
489 * @param a2 coefficient of the second vector
490 * @param v2 second vector
491 * @param row Jacobian matrix row
492 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
493 */
494 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
495 final double[] row, final int j) {
496 row[j] = a1 * v1.getX() + a2 * v2.getX();
497 row[j + 1] = a1 * v1.getY() + a2 * v2.getY();
498 row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ();
499 }
500
501 /** Fill a Jacobian half row with a linear combination of vectors.
502 * @param a1 coefficient of the first vector
503 * @param v1 first vector
504 * @param a2 coefficient of the second vector
505 * @param v2 second vector
506 * @param a3 coefficient of the third vector
507 * @param v3 third vector
508 * @param row Jacobian matrix row
509 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
510 */
511 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
512 final double a3, final Vector3D v3,
513 final double[] row, final int j) {
514 row[j] = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX();
515 row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY();
516 row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ();
517 }
518
519 /** Fill a Jacobian half row with a linear combination of vectors.
520 * @param a1 coefficient of the first vector
521 * @param v1 first vector
522 * @param a2 coefficient of the second vector
523 * @param v2 second vector
524 * @param a3 coefficient of the third vector
525 * @param v3 third vector
526 * @param a4 coefficient of the fourth vector
527 * @param v4 fourth vector
528 * @param row Jacobian matrix row
529 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
530 */
531 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
532 final double a3, final Vector3D v3, final double a4, final Vector3D v4,
533 final double[] row, final int j) {
534 row[j] = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX() + a4 * v4.getX();
535 row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY() + a4 * v4.getY();
536 row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ() + a4 * v4.getZ();
537 }
538
539 /** Fill a Jacobian half row with a linear combination of vectors.
540 * @param a1 coefficient of the first vector
541 * @param v1 first vector
542 * @param a2 coefficient of the second vector
543 * @param v2 second vector
544 * @param a3 coefficient of the third vector
545 * @param v3 third vector
546 * @param a4 coefficient of the fourth vector
547 * @param v4 fourth vector
548 * @param a5 coefficient of the fifth vector
549 * @param v5 fifth vector
550 * @param row Jacobian matrix row
551 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
552 */
553 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
554 final double a3, final Vector3D v3, final double a4, final Vector3D v4,
555 final double a5, final Vector3D v5,
556 final double[] row, final int j) {
557 row[j] = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX() + a4 * v4.getX() + a5 * v5.getX();
558 row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY() + a4 * v4.getY() + a5 * v5.getY();
559 row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ() + a4 * v4.getZ() + a5 * v5.getZ();
560 }
561
562 /** Fill a Jacobian half row with a linear combination of vectors.
563 * @param a1 coefficient of the first vector
564 * @param v1 first vector
565 * @param a2 coefficient of the second vector
566 * @param v2 second vector
567 * @param a3 coefficient of the third vector
568 * @param v3 third vector
569 * @param a4 coefficient of the fourth vector
570 * @param v4 fourth vector
571 * @param a5 coefficient of the fifth vector
572 * @param v5 fifth vector
573 * @param a6 coefficient of the sixth vector
574 * @param v6 sixth vector
575 * @param row Jacobian matrix row
576 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
577 */
578 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
579 final double a3, final Vector3D v3, final double a4, final Vector3D v4,
580 final double a5, final Vector3D v5, final double a6, final Vector3D v6,
581 final double[] row, final int j) {
582 row[j] = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX() + a4 * v4.getX() + a5 * v5.getX() + a6 * v6.getX();
583 row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY() + a4 * v4.getY() + a5 * v5.getY() + a6 * v6.getY();
584 row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ() + a4 * v4.getZ() + a5 * v5.getZ() + a6 * v6.getZ();
585 }
586
587 }