1 /* Copyright 2002-2025 CS GROUP 2 * Licensed to CS GROUP (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.frames; 18 19 import org.hipparchus.CalculusFieldElement; 20 import org.hipparchus.Field; 21 import org.hipparchus.geometry.euclidean.threed.FieldRotation; 22 import org.hipparchus.geometry.euclidean.threed.FieldVector3D; 23 import org.hipparchus.geometry.euclidean.threed.Rotation; 24 import org.hipparchus.geometry.euclidean.threed.RotationConvention; 25 import org.hipparchus.geometry.euclidean.threed.Vector3D; 26 import org.orekit.errors.OrekitException; 27 import org.orekit.errors.OrekitMessages; 28 import org.orekit.files.ccsds.definitions.OrbitRelativeFrame; 29 import org.orekit.time.AbsoluteDate; 30 import org.orekit.time.FieldAbsoluteDate; 31 import org.orekit.utils.FieldPVCoordinates; 32 import org.orekit.utils.PVCoordinates; 33 34 /** 35 * Enumerate for different types of Local Orbital Frames. 36 * 37 * @author Luc Maisonobe 38 * @author Maxime Journot 39 * @author Vincent Cucchietti 40 */ 41 public enum LOFType implements LOF { 42 43 /** Constant for TNW frame 44 * (X axis aligned with velocity, Z axis aligned with orbital momentum). 45 * <p> 46 * The axes of this frame are parallel to the axes of the {@link #VNC} 47 * and {@link #NTW} frames: 48 * </p> 49 * <ul> 50 * <li>X<sub>TNW</sub> = X<sub>VNC</sub> = Y<sub>NTW</sub></li> 51 * <li>Y<sub>TNW</sub> = -Z<sub>VNC</sub> = -X<sub>NTW</sub></li> 52 * <li>Z<sub>TNW</sub> = Y<sub>VNC</sub> = Z<sub>NTW</sub></li> 53 * </ul> 54 * 55 * @see #VNC 56 * @see #NTW 57 */ 58 TNW { 59 /** {@inheritDoc} */ 60 @Override 61 public Rotation rotationFromInertial(final PVCoordinates pv) { 62 return new Rotation(pv.getVelocity(), pv.getMomentum(), 63 Vector3D.PLUS_I, Vector3D.PLUS_K); 64 } 65 66 /** {@inheritDoc} */ 67 @Override 68 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 69 final FieldPVCoordinates<T> pv) { 70 return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(), 71 new FieldVector3D<>(field, Vector3D.PLUS_I), 72 new FieldVector3D<>(field, Vector3D.PLUS_K)); 73 } 74 75 /** {@inheritDoc} */ 76 @Override 77 public OrbitRelativeFrame toOrbitRelativeFrame() { 78 return OrbitRelativeFrame.TNW; 79 } 80 81 }, 82 83 /** 84 * Constant for TNW frame considered inertial (X axis aligned with velocity, Z axis aligned with orbital momentum). 85 * <p> 86 * The axes of this frame are parallel to the axes of the {@link #VNC} and {@link #NTW} frames: 87 * </p> 88 * <ul> 89 * <li>X<sub>TNW</sub> = X<sub>VNC</sub> = Y<sub>NTW</sub></li> 90 * <li>Y<sub>TNW</sub> = -Z<sub>VNC</sub> = -X<sub>NTW</sub></li> 91 * <li>Z<sub>TNW</sub> = Y<sub>VNC</sub> = Z<sub>NTW</sub></li> 92 * </ul> 93 * 94 * @see #VNC 95 * @see #NTW 96 */ 97 TNW_INERTIAL { 98 /** {@inheritDoc} */ 99 @Override 100 public Rotation rotationFromInertial(final PVCoordinates pv) { 101 return TNW.rotationFromInertial(pv); 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 107 final FieldPVCoordinates<T> pv) { 108 return TNW.rotationFromInertial(field, pv); 109 } 110 111 /** {@inheritDoc} */ 112 @Override 113 public boolean isQuasiInertial() { 114 return true; 115 } 116 117 /** {@inheritDoc} */ 118 @Override 119 public OrbitRelativeFrame toOrbitRelativeFrame() { 120 return OrbitRelativeFrame.TNW_INERTIAL; 121 } 122 123 }, 124 125 /** Constant for QSW frame 126 * (X axis aligned with position, Z axis aligned with orbital momentum). 127 * <p> 128 * This frame is also known as the {@link #LVLH} frame, both constants are equivalent. 129 * </p> 130 * <p> 131 * The axes of these frames are parallel to the axes of the {@link #VVLH} frame: 132 * </p> 133 * <ul> 134 * <li>X<sub>QSW/LVLH</sub> = -Z<sub>VVLH</sub></li> 135 * <li>Y<sub>QSW/LVLH</sub> = X<sub>VVLH</sub></li> 136 * <li>Z<sub>QSW/LVLH</sub> = -Y<sub>VVLH</sub></li> 137 * </ul> 138 * 139 * @see #LVLH 140 * @see #VVLH 141 */ 142 QSW { 143 /** {@inheritDoc} */ 144 @Override 145 public Rotation rotationFromInertial(final PVCoordinates pv) { 146 return new Rotation(pv.getPosition(), pv.getMomentum(), 147 Vector3D.PLUS_I, Vector3D.PLUS_K); 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 153 final FieldPVCoordinates<T> pv) { 154 return new FieldRotation<>(pv.getPosition(), pv.getMomentum(), 155 new FieldVector3D<>(field, Vector3D.PLUS_I), 156 new FieldVector3D<>(field, Vector3D.PLUS_K)); 157 } 158 159 /** {@inheritDoc} */ 160 @Override 161 public OrbitRelativeFrame toOrbitRelativeFrame() { 162 return OrbitRelativeFrame.QSW; 163 } 164 165 }, 166 167 /** 168 * Constant for QSW frame considered inertial (X axis aligned with position, Z axis aligned with orbital momentum). 169 * <p> 170 * This frame is also known as the {@link #LVLH} frame, both constants are equivalent. 171 * </p> 172 * <p> 173 * The axes of these frames are parallel to the axes of the {@link #VVLH} frame: 174 * </p> 175 * <ul> 176 * <li>X<sub>QSW/LVLH</sub> = -Z<sub>VVLH</sub></li> 177 * <li>Y<sub>QSW/LVLH</sub> = X<sub>VVLH</sub></li> 178 * <li>Z<sub>QSW/LVLH</sub> = -Y<sub>VVLH</sub></li> 179 * </ul> 180 * 181 * @see #LVLH 182 * @see #VVLH 183 */ 184 QSW_INERTIAL { 185 /** {@inheritDoc} */ 186 @Override 187 public Rotation rotationFromInertial(final PVCoordinates pv) { 188 return QSW.rotationFromInertial(pv); 189 } 190 191 /** {@inheritDoc} */ 192 @Override 193 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 194 final FieldPVCoordinates<T> pv) { 195 return QSW.rotationFromInertial(field, pv); 196 } 197 198 /** {@inheritDoc} */ 199 @Override 200 public boolean isQuasiInertial() { 201 return true; 202 } 203 204 /** {@inheritDoc} */ 205 @Override 206 public OrbitRelativeFrame toOrbitRelativeFrame() { 207 return OrbitRelativeFrame.RSW_INERTIAL; 208 } 209 210 }, 211 212 /** Constant for Local Vertical, Local Horizontal frame 213 * (X axis aligned with position, Z axis aligned with orbital momentum). 214 * <p> 215 * BEWARE! Depending on the background (software used, textbook, community), 216 * different incompatible definitions for LVLH are used. This one is consistent 217 * with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and 218 * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}). 219 * </p> 220 * <p> 221 * This frame is also known as the {@link #QSW} frame, both constants are equivalent. 222 * </p> 223 * <p> 224 * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame: 225 * </p> 226 * <ul> 227 * <li>X<sub>LVLH/QSW</sub> = -Z<sub>LVLH_CCSDS</sub></li> 228 * <li>Y<sub>LVLH/QSW</sub> = X<sub>LVLH_CCSDS</sub></li> 229 * <li>Z<sub>LVLH/QSW</sub> = -Y<sub>LVLH_CCSDS</sub></li> 230 * </ul> 231 * 232 * @see #QSW 233 * @see #VVLH 234 */ 235 LVLH { 236 /** {@inheritDoc} */ 237 @Override 238 public Rotation rotationFromInertial(final PVCoordinates pv) { 239 return new Rotation(pv.getPosition(), pv.getMomentum(), 240 Vector3D.PLUS_I, Vector3D.PLUS_K); 241 } 242 243 /** {@inheritDoc} */ 244 @Override 245 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 246 final FieldPVCoordinates<T> pv) { 247 return new FieldRotation<>(pv.getPosition(), pv.getMomentum(), 248 new FieldVector3D<>(field, Vector3D.PLUS_I), 249 new FieldVector3D<>(field, Vector3D.PLUS_K)); 250 } 251 252 /** {@inheritDoc} */ 253 @Override 254 public OrbitRelativeFrame toOrbitRelativeFrame() { 255 throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION); 256 } 257 258 }, 259 260 /** 261 * Constant for Local Vertical, Local Horizontal frame considered inertial (X axis aligned with position, Z axis 262 * aligned with orbital momentum). 263 * <p> 264 * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for 265 * LVLH are used. This one is consistent with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and 266 * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}). 267 * </p> 268 * <p> 269 * This frame is also known as the {@link #QSW} frame, both constants are equivalent. 270 * </p> 271 * <p> 272 * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame: 273 * </p> 274 * <ul> 275 * <li>X<sub>LVLH/QSW</sub> = -Z<sub>LVLH_CCSDS</sub></li> 276 * <li>Y<sub>LVLH/QSW</sub> = X<sub>LVLH_CCSDS</sub></li> 277 * <li>Z<sub>LVLH/QSW</sub> = -Y<sub>LVLH_CCSDS</sub></li> 278 * </ul> 279 * 280 * @see #QSW 281 * @see #VVLH 282 */ 283 LVLH_INERTIAL { 284 /** {@inheritDoc} */ 285 @Override 286 public Rotation rotationFromInertial(final PVCoordinates pv) { 287 return LVLH.rotationFromInertial(pv); 288 } 289 290 /** {@inheritDoc} */ 291 @Override 292 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 293 final FieldPVCoordinates<T> pv) { 294 return LVLH.rotationFromInertial(field, pv); 295 } 296 297 /** {@inheritDoc} */ 298 @Override 299 public boolean isQuasiInertial() { 300 return true; 301 } 302 303 /** {@inheritDoc} */ 304 @Override 305 public OrbitRelativeFrame toOrbitRelativeFrame() { 306 throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION); 307 } 308 309 }, 310 311 /** Constant for Local Vertical, Local Horizontal frame as defined by CCSDS 312 * (Z axis aligned with opposite of position, Y axis aligned with opposite of orbital momentum). 313 * <p> 314 * BEWARE! Depending on the background (software used, textbook, community), 315 * different incompatible definitions for LVLH are used. This one is consistent 316 * with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However Vallado's 317 * book and with AGI's STK use another definition (see {@link #LVLH}). 318 * </p> 319 * <p> 320 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames: 321 * </p> 322 * <ul> 323 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li> 324 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li> 325 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li> 326 * </ul> 327 * 328 * @see #QSW 329 * @see #LVLH 330 * @since 11.0 331 */ 332 LVLH_CCSDS { 333 /** {@inheritDoc} */ 334 @Override 335 public Rotation rotationFromInertial(final PVCoordinates pv) { 336 return new Rotation(pv.getPosition(), pv.getMomentum(), 337 Vector3D.MINUS_K, Vector3D.MINUS_J); 338 } 339 340 /** {@inheritDoc} */ 341 @Override 342 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 343 final FieldPVCoordinates<T> pv) { 344 return new FieldRotation<>(pv.getPosition(), pv.getMomentum(), 345 new FieldVector3D<>(field, Vector3D.MINUS_K), 346 new FieldVector3D<>(field, Vector3D.MINUS_J)); 347 } 348 349 /** {@inheritDoc} */ 350 @Override 351 public OrbitRelativeFrame toOrbitRelativeFrame() { 352 return OrbitRelativeFrame.LVLH; 353 } 354 355 }, 356 357 /** 358 * Constant for Local Vertical, Local Horizontal frame as defined by CCSDS considered inertial (Z axis aligned with 359 * opposite of position, Y axis aligned with opposite of orbital momentum). 360 * <p> 361 * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for 362 * LVLH are used. This one is consistent with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However 363 * Vallado's book and with AGI's STK use another definition (see {@link #LVLH}). 364 * </p> 365 * <p> 366 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames: 367 * </p> 368 * <ul> 369 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li> 370 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li> 371 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li> 372 * </ul> 373 * 374 * @see #QSW 375 * @see #LVLH 376 * @since 11.0 377 */ 378 LVLH_CCSDS_INERTIAL { 379 /** {@inheritDoc} */ 380 @Override 381 public Rotation rotationFromInertial(final PVCoordinates pv) { 382 return LVLH_CCSDS.rotationFromInertial(pv); 383 } 384 385 /** {@inheritDoc} */ 386 @Override 387 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 388 final FieldPVCoordinates<T> pv) { 389 return LVLH_CCSDS.rotationFromInertial(field, pv); 390 } 391 392 /** {@inheritDoc} */ 393 @Override 394 public boolean isQuasiInertial() { 395 return true; 396 } 397 398 /** {@inheritDoc} */ 399 @Override 400 public OrbitRelativeFrame toOrbitRelativeFrame() { 401 return OrbitRelativeFrame.LVLH_INERTIAL; 402 } 403 404 }, 405 406 /** Constant for Vehicle Velocity, Local Horizontal frame 407 * (Z axis aligned with opposite of position, Y axis aligned with opposite of orbital momentum). 408 * <p> 409 * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK. 410 * </p> 411 * <p> 412 * Beware that the name is misleading: in the general case (i.e. not perfectly circular), 413 * none of the axes is perfectly aligned with velocity! The preferred name for this 414 * should be {@link #LVLH_CCSDS}. 415 * </p> 416 * <p> 417 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames: 418 * </p> 419 * <ul> 420 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li> 421 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li> 422 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li> 423 * </ul> 424 * @see #LVLH_CCSDS 425 */ 426 VVLH { 427 /** {@inheritDoc} */ 428 @Override 429 public Rotation rotationFromInertial(final PVCoordinates pv) { 430 return LVLH_CCSDS.rotationFromInertial(pv); 431 } 432 433 /** {@inheritDoc} */ 434 @Override 435 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 436 final FieldPVCoordinates<T> pv) { 437 return LVLH_CCSDS.rotationFromInertial(field, pv); 438 } 439 440 /** {@inheritDoc} */ 441 @Override 442 public OrbitRelativeFrame toOrbitRelativeFrame() { 443 return OrbitRelativeFrame.LVLH; 444 } 445 446 }, 447 448 /** 449 * Constant for Vehicle Velocity, Local Horizontal frame considered inertial (Z axis aligned with opposite of 450 * position, Y axis aligned with opposite of orbital momentum). 451 * <p> 452 * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK. 453 * </p> 454 * <p> 455 * Beware that the name is misleading: in the general case (i.e. not perfectly circular), none of the axes is 456 * perfectly aligned with velocity! The preferred name for this should be {@link #LVLH_CCSDS}. 457 * </p> 458 * <p> 459 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames: 460 * </p> 461 * <ul> 462 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li> 463 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li> 464 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li> 465 * </ul> 466 * 467 * @see #LVLH_CCSDS 468 */ 469 VVLH_INERTIAL { 470 /** {@inheritDoc} */ 471 @Override 472 public Rotation rotationFromInertial(final PVCoordinates pv) { 473 return VVLH.rotationFromInertial(pv); 474 } 475 476 /** {@inheritDoc} */ 477 @Override 478 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 479 final FieldPVCoordinates<T> pv) { 480 return VVLH.rotationFromInertial(field, pv); 481 } 482 483 /** {@inheritDoc} */ 484 @Override 485 public boolean isQuasiInertial() { 486 return true; 487 } 488 489 /** {@inheritDoc} */ 490 @Override 491 public OrbitRelativeFrame toOrbitRelativeFrame() { 492 return OrbitRelativeFrame.LVLH_INERTIAL; 493 } 494 495 }, 496 497 /** Constant for Velocity - Normal - Co-normal frame 498 * (X axis aligned with velocity, Y axis aligned with orbital momentum). 499 * <p> 500 * The axes of this frame are parallel to the axes of the {@link #TNW} 501 * and {@link #NTW} frames: 502 * </p> 503 * <ul> 504 * <li>X<sub>VNC</sub> = X<sub>TNW</sub> = Y<sub>NTW</sub></li> 505 * <li>Y<sub>VNC</sub> = Z<sub>TNW</sub> = Z<sub>NTW</sub></li> 506 * <li>Z<sub>VNC</sub> = -Y<sub>TNW</sub> = X<sub>NTW</sub></li> 507 * </ul> 508 * 509 * @see #TNW 510 * @see #NTW 511 */ 512 VNC { 513 /** {@inheritDoc} */ 514 @Override 515 public Rotation rotationFromInertial(final PVCoordinates pv) { 516 return new Rotation(pv.getVelocity(), pv.getMomentum(), 517 Vector3D.PLUS_I, Vector3D.PLUS_J); 518 } 519 520 /** {@inheritDoc} */ 521 @Override 522 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 523 final FieldPVCoordinates<T> pv) { 524 return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(), 525 new FieldVector3D<>(field, Vector3D.PLUS_I), 526 new FieldVector3D<>(field, Vector3D.PLUS_J)); 527 } 528 529 /** {@inheritDoc} */ 530 @Override 531 public OrbitRelativeFrame toOrbitRelativeFrame() { 532 return OrbitRelativeFrame.VNC_ROTATING; 533 } 534 535 }, 536 537 /** 538 * Constant for Velocity - Normal - Co-normal frame considered inertial (X axis aligned with velocity, Y axis 539 * aligned with orbital momentum). 540 * <p> 541 * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #NTW} frames: 542 * </p> 543 * <ul> 544 * <li>X<sub>VNC</sub> = X<sub>TNW</sub> = Y<sub>NTW</sub></li> 545 * <li>Y<sub>VNC</sub> = Z<sub>TNW</sub> = Z<sub>NTW</sub></li> 546 * <li>Z<sub>VNC</sub> = -Y<sub>TNW</sub> = X<sub>NTW</sub></li> 547 * </ul> 548 * 549 * @see #TNW 550 * @see #NTW 551 */ 552 VNC_INERTIAL { 553 /** {@inheritDoc} */ 554 @Override 555 public Rotation rotationFromInertial(final PVCoordinates pv) { 556 return VNC.rotationFromInertial(pv); 557 } 558 559 /** {@inheritDoc} */ 560 @Override 561 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 562 final FieldPVCoordinates<T> pv) { 563 return VNC.rotationFromInertial(field, pv); 564 } 565 566 /** {@inheritDoc} */ 567 @Override 568 public boolean isQuasiInertial() { 569 return true; 570 } 571 572 /** {@inheritDoc} */ 573 @Override 574 public OrbitRelativeFrame toOrbitRelativeFrame() { 575 return OrbitRelativeFrame.VNC_INERTIAL; 576 } 577 578 }, 579 580 /** 581 * Constant for Equinoctial Coordinate System (X axis aligned with ascending node, Z axis aligned with orbital 582 * momentum). 583 * 584 * @since 11.0 585 */ 586 EQW { 587 /** {@inheritDoc} */ 588 @Override 589 public Rotation rotationFromInertial(final PVCoordinates pv) { 590 final Vector3D m = pv.getMomentum(); 591 return new Rotation(new Vector3D(-m.getY(), m.getX(), 0), m, 592 Vector3D.PLUS_I, Vector3D.PLUS_K); 593 } 594 595 /** {@inheritDoc} */ 596 @Override 597 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 598 final FieldPVCoordinates<T> pv) { 599 final FieldVector3D<T> m = pv.getMomentum(); 600 return new FieldRotation<>(new FieldVector3D<>(m.getY().negate(), m.getX(), field.getZero()), 601 m, 602 new FieldVector3D<>(field, Vector3D.PLUS_I), 603 new FieldVector3D<>(field, Vector3D.PLUS_K)); 604 } 605 606 /** {@inheritDoc} */ 607 @Override 608 public boolean isQuasiInertial() { 609 return true; 610 } 611 612 /** {@inheritDoc} */ 613 @Override 614 public OrbitRelativeFrame toOrbitRelativeFrame() { 615 return OrbitRelativeFrame.EQW_INERTIAL; 616 } 617 618 }, 619 620 /** Constant for Transverse Velocity Normal coordinate system 621 * (Y axis aligned with velocity, Z axis aligned with orbital momentum). 622 * <p> 623 * The axes of this frame are parallel to the axes of the {@link #TNW} 624 * and {@link #VNC} frames: 625 * </p> 626 * <ul> 627 * <li>X<sub>NTW</sub> = -Y<sub>TNW</sub> = Z<sub>VNC</sub></li> 628 * <li>Y<sub>NTW</sub> = X<sub>TNW</sub> = X<sub>VNC</sub></li> 629 * <li>Z<sub>NTW</sub> = Z<sub>TNW</sub> = Y<sub>VNC</sub></li> 630 * </ul> 631 * @see #TNW 632 * @see #VNC 633 * @since 11.0 634 */ 635 NTW { 636 /** {@inheritDoc} */ 637 @Override 638 public Rotation rotationFromInertial(final PVCoordinates pv) { 639 return new Rotation(pv.getVelocity(), pv.getMomentum(), 640 Vector3D.PLUS_J, Vector3D.PLUS_K); 641 } 642 643 /** {@inheritDoc} */ 644 @Override 645 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 646 final FieldPVCoordinates<T> pv) { 647 return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(), 648 new FieldVector3D<>(field, Vector3D.PLUS_J), 649 new FieldVector3D<>(field, Vector3D.PLUS_K)); 650 } 651 652 /** {@inheritDoc} */ 653 @Override 654 public OrbitRelativeFrame toOrbitRelativeFrame() { 655 return OrbitRelativeFrame.NTW_ROTATING; 656 } 657 658 }, 659 660 /** 661 * Constant for Transverse Velocity Normal coordinate system considered inertial (Y axis aligned with velocity, Z 662 * axis aligned with orbital momentum). 663 * <p> 664 * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #VNC} frames: 665 * </p> 666 * <ul> 667 * <li>X<sub>NTW</sub> = -Y<sub>TNW</sub> = Z<sub>VNC</sub></li> 668 * <li>Y<sub>NTW</sub> = X<sub>TNW</sub> = X<sub>VNC</sub></li> 669 * <li>Z<sub>NTW</sub> = Z<sub>TNW</sub> = Y<sub>VNC</sub></li> 670 * </ul> 671 * 672 * @see #TNW 673 * @see #VNC 674 * @since 11.0 675 */ 676 NTW_INERTIAL { 677 /** {@inheritDoc} */ 678 @Override 679 public Rotation rotationFromInertial(final PVCoordinates pv) { 680 return NTW.rotationFromInertial(pv); 681 } 682 683 /** {@inheritDoc} */ 684 @Override 685 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 686 final FieldPVCoordinates<T> pv) { 687 return NTW.rotationFromInertial(field, pv); 688 } 689 690 /** {@inheritDoc} */ 691 @Override 692 public boolean isQuasiInertial() { 693 return true; 694 } 695 696 /** {@inheritDoc} */ 697 @Override 698 public OrbitRelativeFrame toOrbitRelativeFrame() { 699 return OrbitRelativeFrame.NTW_INERTIAL; 700 } 701 702 }, 703 704 /** 705 * Constant for East-North-Up frame. 706 * (Z aligned with position, North Pole in the (+Y, ±Z) half-plane) 707 * @see #NED 708 * @since 13.0 709 */ 710 ENU { 711 /** {@inheritDoc} */ 712 @Override 713 public Rotation rotationFromInertial(final PVCoordinates pv) { 714 return new Rotation(pv.getPosition(), east(pv), 715 Vector3D.PLUS_K, Vector3D.PLUS_I); 716 } 717 718 /** {@inheritDoc} */ 719 @Override 720 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 721 final FieldPVCoordinates<T> pv) { 722 return new FieldRotation<>(pv.getPosition(), east(pv), 723 FieldVector3D.getPlusK(field), FieldVector3D.getPlusI(field)); 724 } 725 726 /** {@inheritDoc} */ 727 @Override 728 public boolean isQuasiInertial() { 729 return false; 730 } 731 732 /** {@inheritDoc} */ 733 @Override 734 public OrbitRelativeFrame toOrbitRelativeFrame() { 735 return null; 736 } 737 738 }, 739 740 /** 741 * Constant for North-East-Down frame. 742 * (Z aligned with opposite of position, North Pole in the (+X, ±Z) half-plane) 743 * @see #ENU 744 * @since 13.0 745 */ 746 NED { 747 /** {@inheritDoc} */ 748 @Override 749 public Rotation rotationFromInertial(final PVCoordinates pv) { 750 return new Rotation(pv.getPosition(), east(pv), 751 Vector3D.MINUS_K, Vector3D.PLUS_J); 752 } 753 754 /** {@inheritDoc} */ 755 @Override 756 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 757 final FieldPVCoordinates<T> pv) { 758 return new FieldRotation<>(pv.getPosition(), east(pv), 759 FieldVector3D.getMinusK(field), FieldVector3D.getPlusJ(field)); 760 } 761 762 /** {@inheritDoc} */ 763 @Override 764 public boolean isQuasiInertial() { 765 return false; 766 } 767 768 /** {@inheritDoc} */ 769 @Override 770 public OrbitRelativeFrame toOrbitRelativeFrame() { 771 return null; 772 } 773 774 }; 775 776 /** {@inheritDoc} */ 777 public String getName() { 778 return this.name(); 779 } 780 781 /** 782 * Get the rotation from input to output {@link LOFType local orbital frame}. 783 * <p> 784 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 785 * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)} method must be called and 786 * the complete rotation transform must be extracted from it. 787 * 788 * @param in input commonly used local orbital frame 789 * @param out output commonly used local orbital frame 790 * @param pv position-velocity of the spacecraft in some inertial frame 791 * 792 * @return rotation from input to output local orbital frame 793 */ 794 static Rotation rotationFromLOFInToLOFOut(final LOFType in, final LOFType out, final PVCoordinates pv) { 795 return out.rotationFromLOF(in, pv); 796 } 797 798 /** 799 * Get the rotation from input to output {@link LOFType local orbital frame}. 800 * <p> 801 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 802 * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and 803 * the complete rotation transform must be extracted from it. 804 * 805 * @param field field to which the elements belong 806 * @param in input commonly used local orbital frame 807 * @param out output commonly used local orbital frame 808 * @param pv position-velocity of the spacecraft in some inertial frame 809 * @param <T> type of the field elements 810 * 811 * @return rotation from input to output local orbital frame 812 */ 813 static <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOFInToLOFOut(final Field<T> field, 814 final LOFType in, 815 final LOFType out, 816 final FieldPVCoordinates<T> pv) { 817 return out.rotationFromLOF(field, in, pv); 818 } 819 820 /** 821 * Get the rotation from input {@link LOF local orbital frame} to the instance. 822 * <p> 823 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 824 * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)} method must be called and the complete rotation 825 * transform must be extracted from it. 826 * 827 * @param fromLOF input local orbital frame 828 * @param pv position-velocity of the spacecraft in some inertial frame 829 * 830 * @return rotation from input local orbital frame to the instance 831 */ 832 public Rotation rotationFromLOF(final LOFType fromLOF, final PVCoordinates pv) { 833 834 // First compute the rotation from the input LOF to the pivot inertial 835 final Rotation fromLOFToInertial = fromLOF.rotationFromInertial(pv).revert(); 836 837 // Then compute the rotation from the pivot inertial to the output LOF 838 final Rotation inertialToThis = this.rotationFromInertial(pv); 839 840 // Output composed rotation 841 return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM); 842 } 843 844 /** 845 * Get the rotation from input {@link LOFType local orbital frame} to the instance. 846 * <p> 847 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 848 * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and the complete 849 * rotation transform must be extracted from it. 850 * 851 * @param field field to which the elements belong 852 * @param fromLOF input local orbital frame 853 * @param pv position-velocity of the spacecraft in some inertial frame 854 * @param <T> type of the field elements 855 * 856 * @return rotation from input local orbital frame to the instance 857 */ 858 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOF(final Field<T> field, 859 final LOFType fromLOF, 860 final FieldPVCoordinates<T> pv) { 861 862 // First compute the rotation from the input LOF to the pivot inertial 863 final FieldRotation<T> fromLOFToInertial = fromLOF.rotationFromInertial(field, pv).revert(); 864 865 // Then compute the rotation from the pivot inertial to the output LOF 866 final FieldRotation<T> inertialToThis = this.rotationFromInertial(field, pv); 867 868 // Output composed rotation 869 return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM); 870 } 871 872 /** 873 * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use 874 * {@link #rotationFromInertial(PVCoordinates)} instead. 875 */ 876 @Override 877 public Rotation rotationFromInertial(final AbsoluteDate date, final PVCoordinates pv) { 878 return rotationFromInertial(pv); 879 } 880 881 /** 882 * Get the rotation from inertial frame to local orbital frame. 883 * <p> 884 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 885 * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates)} method must be called and 886 * the complete rotation transform must be extracted from it. 887 * </p> 888 * 889 * @param pv position-velocity of the spacecraft in some inertial frame 890 * 891 * @return rotation from inertial frame to local orbital frame 892 */ 893 public abstract Rotation rotationFromInertial(PVCoordinates pv); 894 895 /** 896 * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use 897 * {@link #rotationFromInertial(Field, FieldPVCoordinates)} instead. 898 */ 899 @Override 900 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field, 901 final FieldAbsoluteDate<T> date, 902 final FieldPVCoordinates<T> pv) { 903 return rotationFromInertial(field, pv); 904 } 905 906 /** 907 * Get the rotation from inertial frame to local orbital frame. 908 * <p> 909 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 910 * the full {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be 911 * called and the complete rotation transform must be extracted from it. 912 * </p> 913 * 914 * @param field field to which the elements belong 915 * @param pv position-velocity of the spacecraft in some inertial frame 916 * @param <T> type of the field elements 917 * 918 * @return rotation from inertial frame to local orbital frame 919 */ 920 public abstract <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(Field<T> field, 921 FieldPVCoordinates<T> pv); 922 923 /** 924 * Convert current local orbital frame to CCSDS equivalent orbit relative frame when possible, null otherwise. 925 * 926 * @return CCSDS equivalent orbit relative frame when possible, null otherwise 927 * 928 * @see OrbitRelativeFrame 929 */ 930 public abstract OrbitRelativeFrame toOrbitRelativeFrame(); 931 932 /** Compute East direction. 933 * @param pv position-velocity of the spacecraft in some inertial frame 934 * @return East direction 935 * @since 13.0 936 */ 937 private static Vector3D east(final PVCoordinates pv) { 938 final Vector3D p = pv.getPosition(); 939 final double px = p.getX(); 940 final double py = p.getY(); 941 return (px == 0.0 && py == 0.0) ? Vector3D.PLUS_J : new Vector3D(-py, px, 0); 942 } 943 944 /** Compute East direction. 945 * @param <T> type of the field elements 946 * @param pv position-velocity of the spacecraft in some inertial frame 947 * @return East direction 948 * @since 13.0 949 */ 950 private static <T extends CalculusFieldElement<T>> FieldVector3D<T> east(final FieldPVCoordinates<T> pv) { 951 final FieldVector3D<T> p = pv.getPosition(); 952 final T px = p.getX(); 953 final T py = p.getY(); 954 return (px.isZero() && py.isZero()) ? 955 FieldVector3D.getPlusJ(px.getField()) : 956 new FieldVector3D<>(py.negate(), px, px.getField().getZero()); 957 } 958 959 }