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.analysis.differentiation.FieldUnivariateDerivative2; 22 import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2Field; 23 import org.hipparchus.analysis.differentiation.UnivariateDerivative2; 24 import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field; 25 import org.hipparchus.geometry.euclidean.threed.FieldRotation; 26 import org.hipparchus.geometry.euclidean.threed.FieldVector3D; 27 import org.hipparchus.geometry.euclidean.threed.Rotation; 28 import org.hipparchus.geometry.euclidean.threed.RotationConvention; 29 import org.hipparchus.geometry.euclidean.threed.Vector3D; 30 import org.orekit.time.AbsoluteDate; 31 import org.orekit.time.FieldAbsoluteDate; 32 import org.orekit.utils.AngularCoordinates; 33 import org.orekit.utils.FieldAngularCoordinates; 34 import org.orekit.utils.FieldPVCoordinates; 35 import org.orekit.utils.PVCoordinates; 36 37 /** 38 * Interface for local orbital frame. 39 * 40 * @author Vincent Cucchietti 41 */ 42 public interface LOF { 43 44 /** 45 * Get the rotation from input to output {@link LOF local orbital frame}. 46 * <p> 47 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 48 * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and 49 * the complete rotation transform must be extracted from it. 50 * 51 * @param field field to which the elements belong 52 * @param in input commonly used local orbital frame 53 * @param out output commonly used local orbital frame 54 * @param date date of the rotation 55 * @param pv position-velocity of the spacecraft in some inertial frame 56 * @param <T> type of the field elements 57 * 58 * @return rotation from input to output local orbital frame 59 * 60 * @since 11.3 61 */ 62 static <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOFInToLOFOut(final Field<T> field, 63 final LOF in, final LOF out, 64 final FieldAbsoluteDate<T> date, 65 final FieldPVCoordinates<T> pv) { 66 return out.rotationFromLOF(field, in, date, pv); 67 } 68 69 /** 70 * Get the transform from input to output {@link LOF local orbital frame}. 71 * 72 * @param in input commonly used local orbital frame 73 * @param out output commonly used local orbital frame 74 * @param date date of the transform 75 * @param pv position-velocity of the spacecraft in some inertial frame 76 * @param <T> type of the field elements 77 * 78 * @return rotation from input to output local orbital frame. 79 * 80 * @since 11.3 81 */ 82 static <T extends CalculusFieldElement<T>> FieldTransform<T> transformFromLOFInToLOFOut(final LOF in, final LOF out, 83 final FieldAbsoluteDate<T> date, 84 final FieldPVCoordinates<T> pv) { 85 return out.transformFromLOF(in, date, pv); 86 } 87 88 /** 89 * Get the rotation from input to output {@link LOF local orbital frame}. 90 * <p> 91 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 92 * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)} method must be called and 93 * the complete rotation transform must be extracted from it. 94 * 95 * @param in input commonly used local orbital frame 96 * @param out output commonly used local orbital frame 97 * @param date date of the rotation 98 * @param pv position-velocity of the spacecraft in some inertial frame 99 * 100 * @return rotation from input to output local orbital frame. 101 * 102 * @since 11.3 103 */ 104 static Rotation rotationFromLOFInToLOFOut(final LOF in, final LOF out, final AbsoluteDate date, final PVCoordinates pv) { 105 return out.rotationFromLOF(in, date, pv); 106 } 107 108 /** 109 * Get the transform from input to output {@link LOF local orbital frame}. 110 * 111 * @param in input commonly used local orbital frame 112 * @param out output commonly used local orbital frame 113 * @param date date of the transform 114 * @param pv position-velocity of the spacecraft in some inertial frame 115 * 116 * @return rotation from input to output local orbital frame 117 * 118 * @since 11.3 119 */ 120 static Transform transformFromLOFInToLOFOut(final LOF in, final LOF out, final AbsoluteDate date, 121 final PVCoordinates pv) { 122 return out.transformFromLOF(in, date, pv); 123 } 124 125 /** 126 * Get the rotation from input {@link LOF local orbital frame} to the instance. 127 * <p> 128 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 129 * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and 130 * the complete rotation transform must be extracted from it. 131 * 132 * @param field field to which the elements belong 133 * @param fromLOF input local orbital frame 134 * @param date date of the rotation 135 * @param pv position-velocity of the spacecraft in some inertial frame 136 * @param <T> type of the field elements 137 * 138 * @return rotation from input local orbital frame to the instance 139 * 140 * @since 11.3 141 */ 142 default <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOF(final Field<T> field, 143 final LOF fromLOF, 144 final FieldAbsoluteDate<T> date, 145 final FieldPVCoordinates<T> pv) { 146 147 // First compute the rotation from the input LOF to the pivot inertial 148 final FieldRotation<T> fromLOFToInertial = fromLOF.rotationFromInertial(field, date, pv).revert(); 149 150 // Then compute the rotation from the pivot inertial to the output LOF 151 final FieldRotation<T> inertialToThis = this.rotationFromInertial(field, date, pv); 152 153 // Output composed rotation 154 return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM); 155 } 156 157 /** 158 * Get the rotation from input {@link LOF commonly used local orbital frame} to the instance. 159 * 160 * @param fromLOF input local orbital frame 161 * @param date date of the transform 162 * @param pv position-velocity of the spacecraft in some inertial frame 163 * @param <T> type of the field elements 164 * 165 * @return rotation from input local orbital frame to the instance 166 * 167 * @since 11.3 168 */ 169 default <T extends CalculusFieldElement<T>> FieldTransform<T> transformFromLOF(final LOF fromLOF, 170 final FieldAbsoluteDate<T> date, 171 final FieldPVCoordinates<T> pv) { 172 173 // Get transform from input local orbital frame to inertial 174 final FieldTransform<T> fromLOFToInertial = fromLOF.transformFromInertial(date, pv).getInverse(); 175 176 // Get transform from inertial to output local orbital frame 177 final FieldTransform<T> inertialToLOFOut = this.transformFromInertial(date, pv); 178 179 // Output composition of both transforms 180 return new FieldTransform<>(date, fromLOFToInertial, inertialToLOFOut); 181 } 182 183 /** 184 * Get the transform from an inertial frame defining position-velocity and the local orbital frame. 185 * 186 * @param date current date 187 * @param pv position-velocity of the spacecraft in some inertial frame 188 * @param <T> type of the fields elements 189 * 190 * @return transform from the frame where position-velocity are defined to local orbital frame 191 * 192 * @since 9.0 193 */ 194 default <T extends CalculusFieldElement<T>> FieldTransform<T> transformFromInertial(final FieldAbsoluteDate<T> date, 195 final FieldPVCoordinates<T> pv) { 196 final Field<T> field = date.getField(); 197 if (isQuasiInertial()) { 198 return new FieldTransform<>(date, pv.getPosition().negate(), rotationFromInertial(field, date, pv)); 199 200 } else if (pv.getAcceleration().equals(FieldVector3D.getZero(field))) { 201 // we consider that the acceleration is not known 202 // compute the translation part of the transform 203 final FieldTransform<T> translation = new FieldTransform<>(date, pv.negate()); 204 205 // compute the rotation part of the transform 206 final FieldRotation<T> r = rotationFromInertial(date.getField(), date, pv); 207 final FieldVector3D<T> p = pv.getPosition(); 208 final FieldVector3D<T> momentum = pv.getMomentum(); 209 final FieldTransform<T> rotation = new FieldTransform<>(date, r, 210 new FieldVector3D<>(p.getNormSq().reciprocal(), r.applyTo(momentum))); 211 212 return new FieldTransform<>(date, translation, rotation); 213 214 } else { 215 // use automatic differentiation 216 // create date with independent variable 217 final FieldUnivariateDerivative2Field<T> fud2Field = FieldUnivariateDerivative2Field.getUnivariateDerivative2Field(field); 218 final FieldAbsoluteDate<FieldUnivariateDerivative2<T>> fud2Date = date.toFUD2Field(); 219 // create PV with independent variable 220 final FieldPVCoordinates<FieldUnivariateDerivative2<T>> fud2PV = pv.toUnivariateDerivative2PV(); 221 // compute rotation 222 final FieldRotation<FieldUnivariateDerivative2<T>> fud2Rotation = rotationFromInertial(fud2Field, fud2Date, 223 fud2PV); 224 // turn into FieldTransform whilst adding the translation 225 final FieldAngularCoordinates<T> fieldAngularCoordinates = new FieldAngularCoordinates<>(fud2Rotation); 226 return new FieldTransform<>(date, new FieldTransform<>(date, pv.negate()), 227 new FieldTransform<>(date, fieldAngularCoordinates)); 228 } 229 } 230 231 /** 232 * Get the rotation from inertial frame to local orbital frame. 233 * <p> 234 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 235 * the full {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be 236 * called and the complete rotation transform must be extracted from it. 237 * </p> 238 * 239 * @param field field to which the elements belong 240 * @param date date of the rotation 241 * @param pv position-velocity of the spacecraft in some inertial frame 242 * @param <T> type of the field elements 243 * 244 * @return rotation from inertial frame to local orbital frame 245 * 246 * @since 9.0 247 */ 248 <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(Field<T> field, FieldAbsoluteDate<T> date, 249 FieldPVCoordinates<T> pv); 250 251 /** 252 * Get the rotation from input {@link LOF local orbital frame} to the instance. 253 * <p> 254 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 255 * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)} method must be called and 256 * the complete rotation transform must be extracted from it. 257 * 258 * @param fromLOF input local orbital frame 259 * @param date date of the rotation 260 * @param pv position-velocity of the spacecraft in some inertial frame 261 * 262 * @return rotation from input local orbital frame to the instance 263 * 264 * @since 11.3 265 */ 266 default Rotation rotationFromLOF(final LOF fromLOF, final AbsoluteDate date, final PVCoordinates pv) { 267 268 // First compute the rotation from the input LOF to the pivot inertial 269 final Rotation fromLOFToInertial = fromLOF.rotationFromInertial(date, pv).revert(); 270 271 // Then compute the rotation from the pivot inertial to the output LOF 272 final Rotation inertialToThis = this.rotationFromInertial(date, pv); 273 274 // Output composed rotation 275 return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM); 276 } 277 278 /** 279 * Get the rotation from input {@link LOF local orbital frame} to the instance. 280 * 281 * @param fromLOF input local orbital frame 282 * @param date date of the transform 283 * @param pv position-velocity of the spacecraft in some inertial frame 284 * 285 * @return rotation from input local orbital frame to the instance 286 * 287 * @since 11.3 288 */ 289 default Transform transformFromLOF(final LOF fromLOF, final AbsoluteDate date, final PVCoordinates pv) { 290 291 // First compute the rotation from the input LOF to the pivot inertial 292 final Transform fromLOFToInertial = fromLOF.transformFromInertial(date, pv).getInverse(); 293 294 // Then compute the rotation from the pivot inertial to the output LOF 295 final Transform inertialToThis = this.transformFromInertial(date, pv); 296 297 // Output composed rotation 298 return new Transform(date, fromLOFToInertial, inertialToThis); 299 } 300 301 /** 302 * Get the transform from an inertial frame defining position-velocity and the local orbital frame. 303 * 304 * @param date current date 305 * @param pv position-velocity of the spacecraft in some inertial frame 306 * 307 * @return transform from the frame where position-velocity are defined to local orbital frame 308 */ 309 default Transform transformFromInertial(final AbsoluteDate date, final PVCoordinates pv) { 310 if (isQuasiInertial()) { 311 return new Transform(date, pv.getPosition().negate(), rotationFromInertial(date, pv)); 312 313 } else if (pv.getAcceleration().equals(Vector3D.ZERO)) { 314 // compute the rotation part of the transform assuming there is no known acceleration 315 final Rotation r = rotationFromInertial(date, pv); 316 final Vector3D p = pv.getPosition(); 317 final Vector3D momentum = pv.getMomentum(); 318 final AngularCoordinates angularCoordinates = new AngularCoordinates(r, 319 new Vector3D(1.0 / p.getNormSq(), r.applyTo(momentum))); 320 321 return new Transform(date, pv.negate(), angularCoordinates); 322 323 } else { 324 // use automatic differentiation 325 // create date with independent variable 326 final UnivariateDerivative2Field ud2Field = UnivariateDerivative2Field.getInstance(); 327 final UnivariateDerivative2 dt = new UnivariateDerivative2(0, 1, 0); 328 final FieldAbsoluteDate<UnivariateDerivative2> ud2Date = new FieldAbsoluteDate<>(ud2Field, date).shiftedBy(dt); 329 // create PV with independent variable 330 final FieldPVCoordinates<UnivariateDerivative2> ud2PVCoordinates = pv.toUnivariateDerivative2PV(); 331 // compute Field rotation 332 final FieldRotation<UnivariateDerivative2> ud2Rotation = rotationFromInertial(ud2Field, ud2Date, 333 ud2PVCoordinates); 334 // turn into Transform whilst adding translation 335 final AngularCoordinates angularCoordinates = new AngularCoordinates(ud2Rotation); 336 return new Transform(date, pv.negate(), angularCoordinates); 337 } 338 } 339 340 /** 341 * Get the rotation from inertial frame to local orbital frame. 342 * <p> 343 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, 344 * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be called and 345 * the complete rotation transform must be extracted from it. 346 * 347 * @param date date of the rotation 348 * @param pv position-velocity of the spacecraft in some inertial frame 349 * 350 * @return rotation from inertial frame to local orbital frame 351 */ 352 Rotation rotationFromInertial(AbsoluteDate date, PVCoordinates pv); 353 354 /** Get flag that indicates if current local orbital frame shall be treated as pseudo-inertial. 355 * @return flag that indicates if current local orbital frame shall be treated as pseudo-inertial 356 */ 357 default boolean isQuasiInertial() { 358 return false; 359 } 360 361 /** Get name of the local orbital frame. 362 * @return name of the local orbital frame 363 */ 364 String getName(); 365 366 }